├── .bashrc ├── .config ├── WALLPAPER.jpg ├── alacritty │ └── alacritty.toml ├── emacs │ ├── early-init.el │ ├── init.el │ ├── package-quickstart.el │ └── package-quickstart.elc ├── gtk-3.0 │ └── settings.ini ├── neofetch │ └── config.conf └── picom.conf ├── .profile ├── .xinitrc ├── README.md ├── copy-current-config.sh ├── dmenu ├── LICENSE ├── Makefile ├── README ├── arg.h ├── config.def.h ├── config.mk ├── dmenu.1 ├── dmenu.c ├── dmenu_path ├── dmenu_run ├── drw.c ├── drw.h ├── stest.1 ├── stest.c ├── util.c └── util.h ├── dwm ├── .gitignore ├── LICENSE ├── Makefile ├── README ├── config.def.h ├── config.mk ├── drw.c ├── drw.h ├── dwm.1 ├── dwm.c ├── dwm.png ├── patches │ ├── dwm-activetagindicatorbar-6.2.diff │ ├── dwm-alttagsdecoration-2020010304-cb3f58a.diff │ ├── dwm-cool-autostart-6.2.diff │ ├── dwm-focusonclick-20200110-61bb8b2.diff │ ├── dwm-moveresize-6.2.diff │ ├── dwm-smartborders-6.2.diff │ ├── dwm-status2d-6.3.diff │ ├── dwm-statuspadding-20150524-c8e9479.diff │ └── dwm-uselessgap-20211119-58414bee958f2.diff ├── transient.c ├── util.c └── util.h ├── dwm_bar.sh ├── packages.txt └── tabbed ├── LICENSE ├── Makefile ├── README ├── arg.h ├── config.def.h ├── patches ├── tabbed-autohide-20201222-dabf6a2.diff ├── tabbed-bar-height-0.6.diff └── tabbed-cwd-20230128-41e2b8f.diff ├── tabbed.1 ├── tabbed.c ├── tabbed.c.orig ├── tabbed.c.rej ├── xembed.1 └── xembed.c /.bashrc: -------------------------------------------------------------------------------- 1 | # If not running interactively, don't do anything 2 | case $- in *i*) ;; *) return;; esac 3 | # bash completion 4 | if [[ -f /etc/bash_completion ]]; then 5 | /etc/bash_completion 6 | fi 7 | # history stuff 8 | HISTCONTROL=ignoreboth 9 | shopt -s histappend 10 | HISTSIZE=500 11 | HISTFILESIZE=1000 12 | HISTFILE=~/.local/history 13 | # update the values of LINES and COLUMNS on window resize 14 | shopt -s checkwinsize 15 | # prompt 16 | PS1='\033[1;31m\W/ \e[0m' 17 | #PROMPT_COMMAND='echo -en "\033]0;[${PWD##*/}]\a"' 18 | PROMPT_COMMAND='echo -en "\033]0;$(dirs)\a"' 19 | # Path 20 | export PATH=~/.local/bin:$PATH 21 | # editor 22 | export EDITOR="emacs" 23 | # shorten long commands 24 | alias ga='git add .' 25 | alias gs='git status' 26 | alias gc='git commit -m' 27 | alias gp='git push' 28 | alias g="cd ~/git" 29 | alias autoremove='yay -Rsn $(yay -Qdtq)' 30 | alias mpv="mpv --loop=inf" 31 | alias img="\mpv --loop=inf --pause" 32 | alias dio="gio tree" 33 | alias rickroll="curl -s -L https://raw.githubusercontent.com/keroserene/rickrollrc/master/roll.sh | bash" 34 | # options for commands 35 | alias ls="ls --color=yes --group-directories-first" 36 | alias la="ls -A --color=yes --group-directories-first" 37 | alias rm="rm -Ir" 38 | # shorten name 39 | function e() { 40 | emacs $1 & 41 | } 42 | alias v="nvim" 43 | alias py="python" 44 | alias open="xdg-open" 45 | # other 46 | shopt -s autocd 47 | # emsdk 48 | export PATH=/usr/lib/emsdk:$PATH 49 | export PATH=/usr/lib/emsdk/upstream/emscripten:$PATH 50 | export PATH=/usr/lib/emsdk/node/14.18.2_64bit/bin:$PATH 51 | export EMSDK=/usr/lib/emsdk 52 | export EMSDK_NODE=/usr/lib/emsdk/node/14.18.2_64bit/bin/node 53 | 54 | # emacs vterm 55 | vterm_printf() { 56 | if [ -n "$TMUX" ] && ([ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ]); then 57 | # Tell tmux to pass the escape sequences through 58 | printf "\ePtmux;\e\e]%s\007\e\\" "$1" 59 | elif [ "${TERM%%-*}" = "screen" ]; then 60 | # GNU screen (screen, screen-256color, screen-256color-bce) 61 | printf "\eP\e]%s\007\e\\" "$1" 62 | else 63 | printf "\e]%s\e\\" "$1" 64 | fi 65 | } 66 | -------------------------------------------------------------------------------- /.config/WALLPAPER.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mespyr/dotfiles/f75874642649d2b886a5e3a66fc9ed8fb4714941/.config/WALLPAPER.jpg -------------------------------------------------------------------------------- /.config/alacritty/alacritty.toml: -------------------------------------------------------------------------------- 1 | [colors] 2 | draw_bold_text_with_bright_colors = true 3 | 4 | # Default colors 5 | [colors.primary] 6 | background = '#100f0f' 7 | foreground = '#fffcf0' 8 | dim_foreground = '#FFFCF0' 9 | bright_foreground = '#FFFCF0' 10 | 11 | # Normal colors 12 | [colors.normal] 13 | black = '#100f0f' 14 | red = '#af3029' 15 | green = '#66800b' 16 | yellow = '#ad8301' 17 | blue = '#205ea6' 18 | magenta = '#a02f6f' 19 | cyan = '#24837b' 20 | white = '#fffcf0' 21 | 22 | # Bright colors 23 | [colors.bright] 24 | black = '#100f0f' 25 | red = '#d14d41' 26 | green = '#879a39' 27 | yellow = '#d0a215' 28 | blue = '#4385be' 29 | magenta = '#ce5d97' 30 | cyan = '#3aa99f' 31 | white = '#fffcf0' 32 | [cursor] 33 | unfocused_hollow = false 34 | thickness = 0.4 35 | blink_interval = 500 36 | 37 | [cursor.style] 38 | shape = "Beam" 39 | blinking = "Always" 40 | 41 | [debug] 42 | persistent_logging = true 43 | 44 | [font] 45 | size = 14 46 | 47 | [font.bold] 48 | family = "Iosevka Term NFM" 49 | style = "Bold" 50 | 51 | [font.glyph_offset] 52 | x = 0 53 | y = 0 54 | 55 | [font.italic] 56 | family = "Iosevka Term NFM" 57 | style = "Italic" 58 | 59 | [font.normal] 60 | family = "Iosevka Term NFM" 61 | style = "Normal" 62 | 63 | [font.offset] 64 | x = 0 65 | y = 0 66 | 67 | [mouse] 68 | hide_when_typing = true 69 | 70 | [scrolling] 71 | history = 5000 72 | multiplier = 3 73 | 74 | [selection] 75 | save_to_clipboard = true 76 | 77 | [window] 78 | dynamic_padding = false 79 | dynamic_title = true 80 | 81 | [window.padding] 82 | x = 15 83 | y = 10 84 | -------------------------------------------------------------------------------- /.config/emacs/early-init.el: -------------------------------------------------------------------------------- 1 | ;; Set Garbage Collection threshold high during startup 2 | (setq gc-cons-threshold most-positive-fixnum 3 | gc-cons-percentage 0.8) 4 | (add-hook 'emacs-startup-hook 5 | (lambda () (setq gc-cons-threshold (* 16 1024 1024)))) 6 | 7 | ;; Use PLISTS for LSP performance boost 8 | (setenv "LSP_USE_PLISTS" "true") 9 | 10 | ;; Prevent unnecessary UI elements from loading early 11 | (setq inhibit-startup-message t 12 | inhibit-startup-screen t 13 | initial-scratch-message nil 14 | use-dialog-box nil 15 | native-comp-async-report-warnings-errors 'silent) 16 | 17 | (scroll-bar-mode -1) 18 | (tool-bar-mode -1) 19 | (tooltip-mode -1) 20 | (set-fringe-mode -1) 21 | (menu-bar-mode -1) 22 | (global-visual-line-mode 1) 23 | (advice-add #'x-apply-session-resources :override #'ignore) 24 | 25 | ;; Speed up frame rendering 26 | (setq frame-inhibit-implied-resize t 27 | frame-resize-pixelwise t) 28 | 29 | ;; Suppress certain common warnings 30 | (setq command-error-function 31 | (lambda (data _ _) 32 | (unless (memq (car data) 33 | '(buffer-read-only beginning-of-buffer 34 | end-of-buffer beginning-of-line end-of-line quit)) 35 | (command-error-default-function data context caller)))) 36 | 37 | ;; Temporary disable file name handlers for faster startup 38 | (defvar default-file-name-handler-alist file-name-handler-alist) 39 | (setq file-name-handler-alist nil) 40 | (add-hook 'emacs-startup-hook 41 | (lambda () (setq file-name-handler-alist default-file-name-handler-alist))) 42 | 43 | ;; Store custom settings in a temporary file to keep init.el clean 44 | (setq custom-file (make-temp-file "/tmp/emacs-custom-") 45 | custom-safe-themes t) 46 | 47 | ;; Set background and foreground color early to avoid flashing white screen 48 | (set-face-attribute 'default nil :background "#100f0f" :foreground "#fffcf0") 49 | 50 | ;; Customize modeline thickness 51 | (custom-set-faces 52 | '(mode-line ((t (:background "#1C1B1A" :box (:line-width 5 :color "#1C1B1A"))))) 53 | '(mode-line-inactive ((t (:background "#1C1B1A" :box (:line-width 5 :color "#1C1B1A"))))) 54 | '(vertical-border ((t (:foreground "#575653"))))) 55 | -------------------------------------------------------------------------------- /.config/emacs/init.el: -------------------------------------------------------------------------------- 1 | ;; This is my Emacs config. 2 | ;; It typically takes around 0.22 seconds to load, 3 | ;; which I achieved by abusing the early-init.el file. 4 | 5 | ;; I like knowing how long my Emacs config takes to load. 6 | (defun efs/display-startup-time () 7 | (message "Load time: %.3f seconds" 8 | (float-time 9 | (time-subtract after-init-time before-init-time)))) 10 | (add-hook 'emacs-startup-hook #'efs/display-startup-time) 11 | 12 | ;; We use package.el, the built-in package manager in Emacs. 13 | (require 'package) 14 | (setq package-archives '( 15 | ("elpa" . "https://elpa.gnu.org/packages/") 16 | ("melpa" . "https://melpa.org/packages/") 17 | ("org" . "https://orgmode.org/elpa/"))) 18 | (package-initialize) 19 | 20 | ;; Use-package is a wrapper for package.el which gives 21 | ;; us a clean way to install and configure packages. 22 | (unless (package-installed-p 'use-package) 23 | (package-refresh-contents) 24 | (package-install 'use-package)) 25 | (require 'use-package) 26 | ;; Set some reasonable defaults. 27 | (setq use-package-always-ensure t 28 | package-check-signature nil 29 | package-quickstart t) 30 | 31 | ;; Setting our font 32 | (set-face-attribute 'default nil :font "Iosevka Term NF" :height 140) 33 | 34 | ;; Line number settings 35 | (setq display-line-numbers-type 'relative) 36 | (column-number-mode) 37 | (global-display-line-numbers-mode nil) 38 | 39 | ;; Handle large files and backup files. 40 | (setq 41 | make-backup-files nil 42 | auto-save-default nil 43 | create-lockfiles nil 44 | large-file-warning-threshold 200000000 45 | backup-directory-alist '((".*" . "~/.BACKUP")) 46 | read-process-output-max (* 1024 1024)) 47 | (global-auto-revert-mode t) 48 | (global-so-long-mode 1) 49 | 50 | ;; Smooth scrolling 51 | (setq 52 | redisplay-dont-pause nil 53 | scroll-step 1 54 | mouse-wheel-follow-mouse t 55 | scroll-preserve-screen-position t 56 | mouse-wheel-progressive-speed t) 57 | (pixel-scroll-precision-mode t) 58 | 59 | ;; Other small settings 60 | (setq-default 61 | indent-tabs-mode t 62 | tab-width 4 63 | c-basic-offset tab-width) 64 | (setq 65 | sentence-end-double-space nil 66 | ring-bell-function 'ignore 67 | confirm-kill-processes nil) 68 | (global-set-key (kbd "") 'keyboard-escape-quit) 69 | 70 | ;; I chose the Flexoki colorscheme because its 71 | ;; very simple and doesn't hurt my eyes. 72 | (use-package flexoki-themes 73 | :config 74 | (load-theme 'flexoki-themes-dark) 75 | :custom 76 | (flexoki-themes-use-bold-keywords t) 77 | (flexoki-themes-use-bold-builtins t) 78 | (flexoki-themes-use-italic-comments t)) 79 | 80 | ;; I use simple-modeline because it has a 81 | ;; very minimal look, and is very extensible. 82 | (use-package simple-modeline 83 | :hook (after-init . simple-modeline-mode) 84 | :custom 85 | (mode-line-compact t) 86 | (simple-modeline-segments '(( 87 | simple-modeline-segment-modified 88 | simple-modeline-segment-buffer-name 89 | simple-modeline-segment-evil 90 | simple-modeline-segment-position 91 | simple-modeline-segment-misc-info 92 | simple-modeline-segment-process 93 | simple-modeline-segment-vc) 94 | ()))) 95 | 96 | ;; By default, it doesn't have an evil-mode 97 | ;; segment, so we create our own. 98 | (defun simple-modeline-segment-evil () 99 | (let* ((segment-text (cond 100 | ((eq evil-state 'visual) " Visual") 101 | ((eq evil-state 'normal) " Normal") 102 | ((eq evil-state 'insert) " Insert"))) 103 | (segment-face 'simple-modeline-status-success)) 104 | (propertize segment-text 'face segment-face))) 105 | 106 | ;; The evil package gives us most of the Vim keybindings 107 | (use-package evil 108 | :defer t 109 | :hook (evil-mode . rune/evil-hook) 110 | :init (evil-mode 1) 111 | :config 112 | (setq evil-echo-state nil) 113 | (evil-define-key '(normal visual) 'global "o" "^") 114 | (evil-define-key '(visual) 'global "t" ":comment-or-uncomment-region") 115 | (evil-define-key '(normal) 'global "t" "v:comment-or-uncomment-region")) 116 | 117 | ;; Vertico adds a vertical completion menu for the minibuffer 118 | (use-package vertico 119 | :defer t 120 | :hook (after-init . vertico-mode) 121 | :custom 122 | (vertico-cycle t)) 123 | 124 | ;; Consult provides many functions which help 125 | ;; speed up writing code and file/buffer navigation 126 | (use-package consult 127 | :defer t 128 | :custom 129 | (completion-in-region-function (lambda (&rest args) 130 | (apply (if vertico-mode 131 | #'consult-completion-in-region 132 | #'completion--in-region) 133 | args))) 134 | (find-file-run-dired nil) 135 | :bind (("M-x" . execute-extended-command) 136 | ("M-e" . find-file) 137 | ("M-d" . consult-imenu-multi) 138 | ("M-f" . consult-line))) 139 | 140 | ;; Marginalia provides descriptors for many of 141 | ;; the options displayed inside the minibuffer 142 | ;; (ie. descriptions for variables, commands). 143 | (use-package marginalia 144 | :after vertico 145 | :init (marginalia-mode)) 146 | 147 | ;; Oderless provides a fast completion search for Vertico 148 | (use-package orderless 149 | :custom (completion-styles '(orderless basic))) 150 | 151 | ;; I use the lsp-mode package over the built-in Eglot. 152 | ;; (I might switch) 153 | (use-package lsp-mode 154 | :defer t 155 | :commands (lsp lsp-deferred) 156 | :hook ((c++-mode . lsp-deferred) 157 | (c-mode . lsp-deferred)) 158 | :bind (("M-w" . completion-at-point)) ;; lsp completion 159 | :custom 160 | (lsp-headerline-breadcrumb-enable nil) 161 | (lsp-enable-on-type-formatting nil) 162 | (lsp-idle-delay 1.0) 163 | (lsp-log-io nil) 164 | (lsp-modeline-diagnostics-scope :workspace) 165 | (lsp-modeline-code-actions-segments nil)) 166 | 167 | ;; The tree-sitter package makes code highlighting a lot more readable. 168 | (use-package tree-sitter 169 | :hook (lsp-mode . tree-sitter-hl-mode)) 170 | (use-package tree-sitter-langs :after tree-sitter) 171 | 172 | ;; Vterm is a very fast terminal 173 | ;; which I can just toggle when I need it. 174 | (use-package vterm 175 | :defer t 176 | :hook (vterm-mode . (lambda() 177 | (display-line-numbers-mode 0) 178 | (setq-local mode-line-format nil))) 179 | :bind (("M-t" . vterm-toggle) 180 | :map vterm-mode-map 181 | ("M-t" . vterm-toggle)) 182 | :custom 183 | (vterm-kill-buffer-on-exit t)) 184 | (use-package vterm-toggle 185 | :after vterm 186 | :custom 187 | (vterm-toggle-fullscreen-p nil) 188 | :config 189 | (add-to-list 'display-buffer-alist 190 | '((lambda (buffer-or-name _) 191 | (let ((buffer (get-buffer buffer-or-name))) 192 | (with-current-buffer buffer 193 | (or (equal major-mode 'vterm-mode) 194 | (string-prefix-p vterm-buffer-name (buffer-name buffer)))))) 195 | (display-buffer-reuse-window display-buffer-in-side-window) 196 | (side . bottom) 197 | (dedicated . t) 198 | (reusable-frames . visible) 199 | (window-height. 0.2)))) 200 | 201 | 202 | ;; These are some custom ELisp files I made. 203 | (load "~/git/Taro/taro.el" nil t) 204 | -------------------------------------------------------------------------------- /.config/gtk-3.0/settings.ini: -------------------------------------------------------------------------------- 1 | [Settings] 2 | gtk-font-name=Iosevka Nerd Font 11 3 | gtk-cursor-theme-name=Adwaita 4 | gtk-cursor-theme-size=0 5 | gtk-toolbar-style=GTK_TOOLBAR_BOTH 6 | gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR 7 | gtk-button-images=1 8 | gtk-menu-images=1 9 | gtk-enable-event-sounds=1 10 | gtk-enable-input-feedback-sounds=1 11 | gtk-xft-antialias=1 12 | gtk-xft-hinting=1 13 | gtk-xft-hintstyle=hintfull 14 | gtk-decoration-layout=menu 15 | gtk-theme-name=Orchis-Dark 16 | gtk-icon-theme-name=Adwaita 17 | -------------------------------------------------------------------------------- /.config/neofetch/config.conf: -------------------------------------------------------------------------------- 1 | print_info() { 2 | info "$(color 1)┌ os" distro 3 | info "$(color 1)├$(color 2) pkg" packages 4 | info "$(color 1)├$(color 2) wm" wm 5 | info "$(color 1)├$(color 2) tty" term 6 | info "$(color 1)├$(color 2) cpu" cpu 7 | info "$(color 1)└$(color 2) gpu" gpu 8 | prin 9 | } 10 | 11 | kernel_shorthand="on" 12 | distro_shorthand="off" 13 | os_arch="off" 14 | uptime_shorthand="on" 15 | memory_percent="on" 16 | package_managers="off" 17 | shell_path="off" 18 | shell_version="off" 19 | speed_type="bios_limit" 20 | speed_shorthand="off" 21 | cpu_brand="on" 22 | cpu_speed="off" 23 | cpu_cores="off" 24 | cpu_temp="off" 25 | gpu_brand="on" 26 | gpu_type="off" 27 | refresh_rate="off" 28 | colors=(distro) 29 | bold="on" 30 | underline_enabled="on" 31 | underline_char="―" 32 | separator=" " 33 | block_range=(0 7) 34 | color_blocks="on" 35 | block_width=3 36 | block_height=1 37 | cpu_display="off" 38 | memory_display="small" 39 | battery_display="bar" 40 | disk_display="off" 41 | image_backend="ascii" 42 | ascii_distro="artix_small" 43 | ascii_colors=(distro) 44 | ascii_bold="on" 45 | stdout="off" 46 | -------------------------------------------------------------------------------- /.config/picom.conf: -------------------------------------------------------------------------------- 1 | shadow = true; 2 | shadow-opacity = 1.00; 3 | shadow-color = "#000000"; 4 | shadow-radius = 17; 5 | shadow-offset-x = -16; 6 | shadow-offset-y = -16; 7 | shadow-exclude = [ 8 | # "class_g = 'dmenu'", 9 | ] 10 | fading = true; 11 | fade-in-step = 0.02; 12 | fade-out-step = 0.04; 13 | fade-exclude = [ 14 | # "class_g = 'dmenu'", 15 | ] 16 | #active-opacity = 1.0; 17 | #inactive-opacity = 0.9; 18 | #inactive-dim = 0.4; 19 | focus-exclude = [ 20 | "class_g = 'dwm'" 21 | ]; 22 | # GENERAL 23 | backend = "glx"; 24 | vsync = true; 25 | mark-wmwin-focused = false; 26 | mark-ovredir-focused = false; 27 | # detect-rounded-corners = true; 28 | # detect-client-opacity = true; 29 | use-ewmh-active-win = true; 30 | detect-transient = true; 31 | detect-client-leader = true; 32 | use-damage = true; 33 | log-level = "warn"; 34 | dbe = true; 35 | -------------------------------------------------------------------------------- /.profile: -------------------------------------------------------------------------------- 1 | [[ -f ~/.bashrc ]] && . ~/.bashrc 2 | -------------------------------------------------------------------------------- /.xinitrc: -------------------------------------------------------------------------------- 1 | xrandr \ 2 | --output DisplayPort-2 --mode 2560x1440 --pos 1080x480 --rotate normal --primary \ 3 | --output HDMI-A-1-1 --mode 1920x1080 --pos 0x0 --rotate left 4 | exec dwm 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

My Epic Dotfiles

2 | 3 |

4 | 5 | 6 | 7 | 8 |

9 | 10 |
11 | 12 | My semi-decent linux configs for my computer. 13 | - Window Manager: DWM 14 | - Terminal: Alacritty 15 | - Text Editor: Emacs 16 | 17 | ### Install Guide 18 | 19 | Build and install `dwm` and `dmenu`. 20 | Copy all the config files. 21 | Run `fc-cache` to update all your fonts. 22 | -------------------------------------------------------------------------------- /copy-current-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | function COPY { 4 | set +xe 5 | [ -f $1 ] && mv $1 .bak 6 | [ -d $1 ] && mv $1 .bak 7 | cp ~/$1 $1 -r 8 | set -xe 9 | } 10 | 11 | set -xe 12 | COPY .config/alacritty/ 13 | COPY .bashrc 14 | COPY .profile 15 | COPY .xinitrc 16 | COPY .config/emacs/ 17 | COPY .config/picom.conf 18 | COPY .config/neofetch/ 19 | COPY .config/gtk-3.0/ 20 | COPY .config/WALLPAPER.jpg 21 | COPY dwm_bar.sh 22 | 23 | rm .bak -r 24 | 25 | # remove some files 26 | rm .config/emacs/elpa/ -r 27 | rm .config/emacs/eln-cache/ -r 28 | rm .config/emacs/auto-save-list/ -r 29 | rm .config/emacs/.lsp-session-v1 30 | -------------------------------------------------------------------------------- /dmenu/LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2008 Sander van Dijk 5 | © 2006-2007 Michał Janeczek 6 | © 2007 Kris Maglione 7 | © 2009 Gottox 8 | © 2009 Markus Schnalke 9 | © 2009 Evan Gates 10 | © 2010-2012 Connor Lane Smith 11 | © 2014-2022 Hiltjo Posthuma 12 | © 2015-2019 Quentin Rameau 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a 15 | copy of this software and associated documentation files (the "Software"), 16 | to deal in the Software without restriction, including without limitation 17 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 18 | and/or sell copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 | DEALINGS IN THE SOFTWARE. 31 | -------------------------------------------------------------------------------- /dmenu/Makefile: -------------------------------------------------------------------------------- 1 | # dmenu - dynamic menu 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dmenu.c stest.c util.c 7 | OBJ = $(SRC:.c=.o) 8 | 9 | all: dmenu stest 10 | 11 | .c.o: 12 | $(CC) -c $(CFLAGS) $< 13 | 14 | $(OBJ): arg.h config.def.h config.mk drw.h 15 | 16 | dmenu: dmenu.o drw.o util.o 17 | $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) 18 | 19 | stest: stest.o 20 | $(CC) -o $@ stest.o $(LDFLAGS) 21 | 22 | clean: 23 | rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz 24 | 25 | dist: clean 26 | mkdir -p dmenu-$(VERSION) 27 | cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ 28 | drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ 29 | dmenu-$(VERSION) 30 | tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) 31 | gzip dmenu-$(VERSION).tar 32 | rm -rf dmenu-$(VERSION) 33 | 34 | install: all 35 | mkdir -p $(DESTDIR)$(PREFIX)/bin 36 | cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin 37 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu 38 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path 39 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run 40 | chmod 755 $(DESTDIR)$(PREFIX)/bin/stest 41 | mkdir -p $(DESTDIR)$(MANPREFIX)/man1 42 | sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 43 | sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 44 | chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 45 | chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 46 | 47 | uninstall: 48 | rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ 49 | $(DESTDIR)$(PREFIX)/bin/dmenu_path\ 50 | $(DESTDIR)$(PREFIX)/bin/dmenu_run\ 51 | $(DESTDIR)$(PREFIX)/bin/stest\ 52 | $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ 53 | $(DESTDIR)$(MANPREFIX)/man1/stest.1 54 | 55 | .PHONY: all clean dist install uninstall 56 | -------------------------------------------------------------------------------- /dmenu/README: -------------------------------------------------------------------------------- 1 | dmenu - dynamic menu 2 | ==================== 3 | dmenu is an efficient dynamic menu for X. 4 | 5 | 6 | Requirements 7 | ------------ 8 | In order to build dmenu you need the Xlib header files. 9 | 10 | 11 | Installation 12 | ------------ 13 | Edit config.mk to match your local setup (dmenu is installed into 14 | the /usr/local namespace by default). 15 | 16 | Afterwards enter the following command to build and install dmenu 17 | (if necessary as root): 18 | 19 | make clean install 20 | 21 | 22 | Running dmenu 23 | ------------- 24 | See the man page for details. 25 | -------------------------------------------------------------------------------- /dmenu/arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copy me if you can. 3 | * by 20h 4 | */ 5 | 6 | #ifndef ARG_H__ 7 | #define ARG_H__ 8 | 9 | extern char *argv0; 10 | 11 | /* use main(int argc, char *argv[]) */ 12 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ 13 | argv[0] && argv[0][0] == '-'\ 14 | && argv[0][1];\ 15 | argc--, argv++) {\ 16 | char argc_;\ 17 | char **argv_;\ 18 | int brk_;\ 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ 20 | argv++;\ 21 | argc--;\ 22 | break;\ 23 | }\ 24 | for (brk_ = 0, argv[0]++, argv_ = argv;\ 25 | argv[0][0] && !brk_;\ 26 | argv[0]++) {\ 27 | if (argv_ != argv)\ 28 | break;\ 29 | argc_ = argv[0][0];\ 30 | switch (argc_) 31 | 32 | #define ARGEND }\ 33 | } 34 | 35 | #define ARGC() argc_ 36 | 37 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ 38 | ((x), abort(), (char *)0) :\ 39 | (brk_ = 1, (argv[0][1] != '\0')?\ 40 | (&argv[0][1]) :\ 41 | (argc--, argv++, argv[0]))) 42 | 43 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ 44 | (char *)0 :\ 45 | (brk_ = 1, (argv[0][1] != '\0')?\ 46 | (&argv[0][1]) :\ 47 | (argc--, argv++, argv[0]))) 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /dmenu/config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | /* Default settings; can be overriden by command line. */ 3 | 4 | static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ 5 | static const int user_bh = 18; 6 | 7 | /* -fn option overrides fonts[0]; default X11 font or font set */ 8 | static const char *fonts[] = { 9 | "Iosevka Term NF:size=12:style=Bold" 10 | }; 11 | static const char *prompt = "  "; /* -p option; prompt to the left of input field */ 12 | 13 | static const char bg[] = "#100f0f"; 14 | static const char fg[] = "#fffcf0"; 15 | static const char accent[] = "#ad8301"; 16 | 17 | /* 18 | static const char bg[] = "#232726"; 19 | static const char fg[] = "#ffffff"; 20 | static const char accent[] = "#ce5d97"; 21 | */ 22 | 23 | static const char *colors[SchemeLast][2] = { 24 | /* fg bg */ 25 | [SchemeNorm] = { fg, bg }, 26 | [SchemeSel] = { accent, bg }, 27 | [SchemeOut] = { "#000000", "#00ffff" }, 28 | }; 29 | 30 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 31 | static unsigned int lines = 0; 32 | 33 | /* 34 | * Characters not considered part of a word while deleting words 35 | * for example: " /?\"&[]" 36 | */ 37 | static const char worddelimiters[] = " "; 38 | -------------------------------------------------------------------------------- /dmenu/config.mk: -------------------------------------------------------------------------------- 1 | # dmenu version 2 | VERSION = 5.3 3 | 4 | # paths 5 | PREFIX = /usr/local 6 | MANPREFIX = $(PREFIX)/share/man 7 | 8 | X11INC = /usr/X11R6/include 9 | X11LIB = /usr/X11R6/lib 10 | 11 | # Xinerama, comment if you don't want it 12 | XINERAMALIBS = -lXinerama 13 | XINERAMAFLAGS = -DXINERAMA 14 | 15 | # freetype 16 | FREETYPELIBS = -lfontconfig -lXft 17 | FREETYPEINC = /usr/include/freetype2 18 | # OpenBSD (uncomment) 19 | #FREETYPEINC = $(X11INC)/freetype2 20 | #MANPREFIX = ${PREFIX}/man 21 | 22 | # includes and libs 23 | INCS = -I$(X11INC) -I$(FREETYPEINC) 24 | LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) 25 | 26 | # flags 27 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) 28 | CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) 29 | LDFLAGS = $(LIBS) 30 | 31 | # compiler and linker 32 | CC = cc 33 | -------------------------------------------------------------------------------- /dmenu/dmenu.1: -------------------------------------------------------------------------------- 1 | .TH DMENU 1 dmenu\-VERSION 2 | .SH NAME 3 | dmenu \- dynamic menu 4 | .SH SYNOPSIS 5 | .B dmenu 6 | .RB [ \-bfiv ] 7 | .RB [ \-l 8 | .IR lines ] 9 | .RB [ \-m 10 | .IR monitor ] 11 | .RB [ \-p 12 | .IR prompt ] 13 | .RB [ \-fn 14 | .IR font ] 15 | .RB [ \-nb 16 | .IR color ] 17 | .RB [ \-nf 18 | .IR color ] 19 | .RB [ \-sb 20 | .IR color ] 21 | .RB [ \-sf 22 | .IR color ] 23 | .RB [ \-w 24 | .IR windowid ] 25 | .P 26 | .BR dmenu_run " ..." 27 | .SH DESCRIPTION 28 | .B dmenu 29 | is a dynamic menu for X, which reads a list of newline\-separated items from 30 | stdin. When the user selects an item and presses Return, their choice is printed 31 | to stdout and dmenu terminates. Entering text will narrow the items to those 32 | matching the tokens in the input. 33 | .P 34 | .B dmenu_run 35 | is a script used by 36 | .IR dwm (1) 37 | which lists programs in the user's $PATH and runs the result in their $SHELL. 38 | .SH OPTIONS 39 | .TP 40 | .B \-b 41 | dmenu appears at the bottom of the screen. 42 | .TP 43 | .B \-f 44 | dmenu grabs the keyboard before reading stdin if not reading from a tty. This 45 | is faster, but will lock up X until stdin reaches end\-of\-file. 46 | .TP 47 | .B \-i 48 | dmenu matches menu items case insensitively. 49 | .TP 50 | .BI \-l " lines" 51 | dmenu lists items vertically, with the given number of lines. 52 | .TP 53 | .BI \-m " monitor" 54 | dmenu is displayed on the monitor number supplied. Monitor numbers are starting 55 | from 0. 56 | .TP 57 | .BI \-p " prompt" 58 | defines the prompt to be displayed to the left of the input field. 59 | .TP 60 | .BI \-fn " font" 61 | defines the font or font set used. 62 | .TP 63 | .BI \-nb " color" 64 | defines the normal background color. 65 | .IR #RGB , 66 | .IR #RRGGBB , 67 | and X color names are supported. 68 | .TP 69 | .BI \-nf " color" 70 | defines the normal foreground color. 71 | .TP 72 | .BI \-sb " color" 73 | defines the selected background color. 74 | .TP 75 | .BI \-sf " color" 76 | defines the selected foreground color. 77 | .TP 78 | .B \-v 79 | prints version information to stdout, then exits. 80 | .TP 81 | .BI \-w " windowid" 82 | embed into windowid. 83 | .SH USAGE 84 | dmenu is completely controlled by the keyboard. Items are selected using the 85 | arrow keys, page up, page down, home, and end. 86 | .TP 87 | .B Tab 88 | Copy the selected item to the input field. 89 | .TP 90 | .B Return 91 | Confirm selection. Prints the selected item to stdout and exits, returning 92 | success. 93 | .TP 94 | .B Ctrl-Return 95 | Confirm selection. Prints the selected item to stdout and continues. 96 | .TP 97 | .B Shift\-Return 98 | Confirm input. Prints the input text to stdout and exits, returning success. 99 | .TP 100 | .B Escape 101 | Exit without selecting an item, returning failure. 102 | .TP 103 | .B Ctrl-Left 104 | Move cursor to the start of the current word 105 | .TP 106 | .B Ctrl-Right 107 | Move cursor to the end of the current word 108 | .TP 109 | .B C\-a 110 | Home 111 | .TP 112 | .B C\-b 113 | Left 114 | .TP 115 | .B C\-c 116 | Escape 117 | .TP 118 | .B C\-d 119 | Delete 120 | .TP 121 | .B C\-e 122 | End 123 | .TP 124 | .B C\-f 125 | Right 126 | .TP 127 | .B C\-g 128 | Escape 129 | .TP 130 | .B C\-h 131 | Backspace 132 | .TP 133 | .B C\-i 134 | Tab 135 | .TP 136 | .B C\-j 137 | Return 138 | .TP 139 | .B C\-J 140 | Shift-Return 141 | .TP 142 | .B C\-k 143 | Delete line right 144 | .TP 145 | .B C\-m 146 | Return 147 | .TP 148 | .B C\-M 149 | Shift-Return 150 | .TP 151 | .B C\-n 152 | Down 153 | .TP 154 | .B C\-p 155 | Up 156 | .TP 157 | .B C\-u 158 | Delete line left 159 | .TP 160 | .B C\-w 161 | Delete word left 162 | .TP 163 | .B C\-y 164 | Paste from primary X selection 165 | .TP 166 | .B C\-Y 167 | Paste from X clipboard 168 | .TP 169 | .B M\-b 170 | Move cursor to the start of the current word 171 | .TP 172 | .B M\-f 173 | Move cursor to the end of the current word 174 | .TP 175 | .B M\-g 176 | Home 177 | .TP 178 | .B M\-G 179 | End 180 | .TP 181 | .B M\-h 182 | Up 183 | .TP 184 | .B M\-j 185 | Page down 186 | .TP 187 | .B M\-k 188 | Page up 189 | .TP 190 | .B M\-l 191 | Down 192 | .SH SEE ALSO 193 | .IR dwm (1), 194 | .IR stest (1) 195 | -------------------------------------------------------------------------------- /dmenu/dmenu.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #ifdef XINERAMA 15 | #include 16 | #endif 17 | #include 18 | 19 | #include "drw.h" 20 | #include "util.h" 21 | 22 | /* macros */ 23 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ 24 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) 25 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 26 | 27 | /* enums */ 28 | enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ 29 | 30 | struct item { 31 | char *text; 32 | struct item *left, *right; 33 | int out; 34 | }; 35 | 36 | static char text[BUFSIZ] = ""; 37 | static char *embed; 38 | static int bh, mw, mh; 39 | static int dmx = 20; 40 | static int dmy = 50; //520 41 | static unsigned int dmw = 600; 42 | static int inputw = 0, promptw; 43 | static int lrpad; /* sum of left and right padding */ 44 | static size_t cursor; 45 | static struct item *items = NULL; 46 | static struct item *matches, *matchend; 47 | static struct item *prev, *curr, *next, *sel; 48 | static int mon = -1, screen; 49 | 50 | static Atom clip, utf8; 51 | static Display *dpy; 52 | static Window root, parentwin, win; 53 | static XIC xic; 54 | 55 | static Drw *drw; 56 | static Clr *scheme[SchemeLast]; 57 | 58 | #include "config.def.h" 59 | 60 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 61 | static char *(*fstrstr)(const char *, const char *) = strstr; 62 | 63 | static unsigned int 64 | textw_clamp(const char *str, unsigned int n) 65 | { 66 | unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; 67 | return MIN(w, n); 68 | } 69 | 70 | static void 71 | appenditem(struct item *item, struct item **list, struct item **last) 72 | { 73 | if (*last) 74 | (*last)->right = item; 75 | else 76 | *list = item; 77 | 78 | item->left = *last; 79 | item->right = NULL; 80 | *last = item; 81 | } 82 | 83 | static void 84 | calcoffsets(void) 85 | { 86 | int i, n; 87 | 88 | if (lines > 0) 89 | n = lines * bh; 90 | else 91 | n = mw - (promptw + inputw + TEXTW("") + TEXTW(" ")); 92 | /* calculate which items will begin the next page and previous page */ 93 | for (i = 0, next = curr; next; next = next->right) 94 | if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) 95 | break; 96 | for (i = 0, prev = curr; prev && prev->left; prev = prev->left) 97 | if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) 98 | break; 99 | } 100 | 101 | static void 102 | cleanup(void) 103 | { 104 | size_t i; 105 | 106 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 107 | for (i = 0; i < SchemeLast; i++) 108 | free(scheme[i]); 109 | for (i = 0; items && items[i].text; ++i) 110 | free(items[i].text); 111 | free(items); 112 | drw_free(drw); 113 | XSync(dpy, False); 114 | XCloseDisplay(dpy); 115 | } 116 | 117 | static char * 118 | cistrstr(const char *h, const char *n) 119 | { 120 | size_t i; 121 | 122 | if (!n[0]) 123 | return (char *)h; 124 | 125 | for (; *h; ++h) { 126 | for (i = 0; n[i] && tolower((unsigned char)n[i]) == 127 | tolower((unsigned char)h[i]); ++i) 128 | ; 129 | if (n[i] == '\0') 130 | return (char *)h; 131 | } 132 | return NULL; 133 | } 134 | 135 | static int 136 | drawitem(struct item *item, int x, int y, int w) 137 | { 138 | if (item == sel) { 139 | drw_setscheme(drw, scheme[SchemeSel]); 140 | int ret = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 141 | drw_rect(drw, x + 4, bh-3, w - (2*4), 3, 1, 0); 142 | return ret; 143 | } 144 | else if (item->out) { 145 | drw_setscheme(drw, scheme[SchemeOut]); 146 | return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 147 | } 148 | else { 149 | drw_setscheme(drw, scheme[SchemeNorm]); 150 | return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 151 | } 152 | /* 153 | if (item == sel) 154 | drw_setscheme(drw, scheme[SchemeSel]); 155 | else if (item->out) 156 | drw_setscheme(drw, scheme[SchemeOut]); 157 | else 158 | drw_setscheme(drw, scheme[SchemeNorm]); 159 | 160 | return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 161 | */ 162 | } 163 | 164 | static void 165 | drawmenu(void) 166 | { 167 | unsigned int curpos; 168 | struct item *item; 169 | int x = 0, y = 0, w; 170 | 171 | drw_setscheme(drw, scheme[SchemeNorm]); 172 | drw_rect(drw, 0, 0, mw, mh, 1, 1); 173 | 174 | if (prompt && *prompt) { 175 | drw_setscheme(drw, scheme[SchemeSel]); 176 | x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); 177 | } 178 | /* draw input field */ 179 | w = (lines > 0 || !matches) ? mw - x : inputw; 180 | drw_setscheme(drw, scheme[SchemeNorm]); 181 | drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); 182 | 183 | curpos = TEXTW(text) - TEXTW(&text[cursor]); 184 | if ((curpos += lrpad / 2 - 1) < w) { 185 | drw_setscheme(drw, scheme[SchemeNorm]); 186 | drw_rect(drw, x + curpos, 10, 2, bh - 20, 1, 0); 187 | } 188 | 189 | if (lines > 0) { 190 | /* draw vertical list */ 191 | for (item = curr; item != next; item = item->right) 192 | drawitem(item, x, y += bh, mw - x); 193 | } else if (matches) { 194 | /* draw horizontal list */ 195 | x += inputw; 196 | w = TEXTW(""); 197 | if (curr->left) { 198 | drw_setscheme(drw, scheme[SchemeNorm]); 199 | drw_text(drw, x, 0, w, bh, lrpad / 2, "", 0); 200 | } 201 | x += w; 202 | for (item = curr; item != next; item = item->right) 203 | x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(" "))); 204 | if (next) { 205 | w = TEXTW(" "); 206 | drw_setscheme(drw, scheme[SchemeNorm]); 207 | drw_text(drw, mw - w, 0, w, bh, lrpad / 2, " ", 0); 208 | } 209 | } 210 | drw_map(drw, win, 0, 0, mw, mh); 211 | } 212 | 213 | static void 214 | grabfocus(void) 215 | { 216 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; 217 | Window focuswin; 218 | int i, revertwin; 219 | 220 | for (i = 0; i < 100; ++i) { 221 | XGetInputFocus(dpy, &focuswin, &revertwin); 222 | if (focuswin == win) 223 | return; 224 | XSetInputFocus(dpy, win, RevertToParent, CurrentTime); 225 | nanosleep(&ts, NULL); 226 | } 227 | die("cannot grab focus"); 228 | } 229 | 230 | static void 231 | grabkeyboard(void) 232 | { 233 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; 234 | int i; 235 | 236 | if (embed) 237 | return; 238 | /* try to grab keyboard, we may have to wait for another process to ungrab */ 239 | for (i = 0; i < 1000; i++) { 240 | if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, 241 | GrabModeAsync, CurrentTime) == GrabSuccess) 242 | return; 243 | nanosleep(&ts, NULL); 244 | } 245 | die("cannot grab keyboard"); 246 | } 247 | 248 | static void 249 | match(void) 250 | { 251 | static char **tokv = NULL; 252 | static int tokn = 0; 253 | 254 | char buf[sizeof text], *s; 255 | int i, tokc = 0; 256 | size_t len, textsize; 257 | struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; 258 | 259 | strcpy(buf, text); 260 | /* separate input text into tokens to be matched individually */ 261 | for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) 262 | if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) 263 | die("cannot realloc %zu bytes:", tokn * sizeof *tokv); 264 | len = tokc ? strlen(tokv[0]) : 0; 265 | 266 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 267 | textsize = strlen(text) + 1; 268 | for (item = items; item && item->text; item++) { 269 | for (i = 0; i < tokc; i++) 270 | if (!fstrstr(item->text, tokv[i])) 271 | break; 272 | if (i != tokc) /* not all tokens match */ 273 | continue; 274 | /* exact matches go first, then prefixes, then substrings */ 275 | if (!tokc || !fstrncmp(text, item->text, textsize)) 276 | appenditem(item, &matches, &matchend); 277 | else if (!fstrncmp(tokv[0], item->text, len)) 278 | appenditem(item, &lprefix, &prefixend); 279 | else 280 | appenditem(item, &lsubstr, &substrend); 281 | } 282 | if (lprefix) { 283 | if (matches) { 284 | matchend->right = lprefix; 285 | lprefix->left = matchend; 286 | } else 287 | matches = lprefix; 288 | matchend = prefixend; 289 | } 290 | if (lsubstr) { 291 | if (matches) { 292 | matchend->right = lsubstr; 293 | lsubstr->left = matchend; 294 | } else 295 | matches = lsubstr; 296 | matchend = substrend; 297 | } 298 | curr = sel = matches; 299 | calcoffsets(); 300 | } 301 | 302 | static void 303 | insert(const char *str, ssize_t n) 304 | { 305 | if (strlen(text) + n > sizeof text - 1) 306 | return; 307 | /* move existing text out of the way, insert new text, and update cursor */ 308 | memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); 309 | if (n > 0) 310 | memcpy(&text[cursor], str, n); 311 | cursor += n; 312 | match(); 313 | } 314 | 315 | static size_t 316 | nextrune(int inc) 317 | { 318 | ssize_t n; 319 | 320 | /* return location of next utf8 rune in the given direction (+1 or -1) */ 321 | for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) 322 | ; 323 | return n; 324 | } 325 | 326 | static void 327 | movewordedge(int dir) 328 | { 329 | if (dir < 0) { /* move cursor to the start of the word*/ 330 | while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 331 | cursor = nextrune(-1); 332 | while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 333 | cursor = nextrune(-1); 334 | } else { /* move cursor to the end of the word */ 335 | while (text[cursor] && strchr(worddelimiters, text[cursor])) 336 | cursor = nextrune(+1); 337 | while (text[cursor] && !strchr(worddelimiters, text[cursor])) 338 | cursor = nextrune(+1); 339 | } 340 | } 341 | 342 | static void 343 | keypress(XKeyEvent *ev) 344 | { 345 | char buf[64]; 346 | int len; 347 | KeySym ksym = NoSymbol; 348 | Status status; 349 | 350 | len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); 351 | switch (status) { 352 | default: /* XLookupNone, XBufferOverflow */ 353 | return; 354 | case XLookupChars: /* composed string from input method */ 355 | goto insert; 356 | case XLookupKeySym: 357 | case XLookupBoth: /* a KeySym and a string are returned: use keysym */ 358 | break; 359 | } 360 | 361 | if (ev->state & ControlMask) { 362 | switch(ksym) { 363 | case XK_a: ksym = XK_Home; break; 364 | case XK_b: ksym = XK_Left; break; 365 | case XK_c: ksym = XK_Escape; break; 366 | case XK_d: ksym = XK_Delete; break; 367 | case XK_e: ksym = XK_End; break; 368 | case XK_f: ksym = XK_Right; break; 369 | case XK_g: ksym = XK_Escape; break; 370 | case XK_h: ksym = XK_BackSpace; break; 371 | case XK_i: ksym = XK_Tab; break; 372 | case XK_j: /* fallthrough */ 373 | case XK_J: /* fallthrough */ 374 | case XK_m: /* fallthrough */ 375 | case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; 376 | case XK_n: ksym = XK_Down; break; 377 | case XK_p: ksym = XK_Up; break; 378 | 379 | case XK_k: /* delete right */ 380 | text[cursor] = '\0'; 381 | match(); 382 | break; 383 | case XK_u: /* delete left */ 384 | insert(NULL, 0 - cursor); 385 | break; 386 | case XK_w: /* delete word */ 387 | while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 388 | insert(NULL, nextrune(-1) - cursor); 389 | while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 390 | insert(NULL, nextrune(-1) - cursor); 391 | break; 392 | case XK_y: /* paste selection */ 393 | case XK_Y: 394 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 395 | utf8, utf8, win, CurrentTime); 396 | return; 397 | case XK_Left: 398 | case XK_KP_Left: 399 | movewordedge(-1); 400 | goto draw; 401 | case XK_Right: 402 | case XK_KP_Right: 403 | movewordedge(+1); 404 | goto draw; 405 | case XK_Return: 406 | case XK_KP_Enter: 407 | break; 408 | case XK_bracketleft: 409 | cleanup(); 410 | exit(1); 411 | default: 412 | return; 413 | } 414 | } else if (ev->state & Mod1Mask) { 415 | switch(ksym) { 416 | case XK_b: 417 | movewordedge(-1); 418 | goto draw; 419 | case XK_f: 420 | movewordedge(+1); 421 | goto draw; 422 | case XK_g: ksym = XK_Home; break; 423 | case XK_G: ksym = XK_End; break; 424 | case XK_h: ksym = XK_Up; break; 425 | case XK_j: ksym = XK_Next; break; 426 | case XK_k: ksym = XK_Prior; break; 427 | case XK_l: ksym = XK_Down; break; 428 | default: 429 | return; 430 | } 431 | } 432 | 433 | switch(ksym) { 434 | default: 435 | insert: 436 | if (!iscntrl((unsigned char)*buf)) 437 | insert(buf, len); 438 | break; 439 | case XK_Delete: 440 | case XK_KP_Delete: 441 | if (text[cursor] == '\0') 442 | return; 443 | cursor = nextrune(+1); 444 | /* fallthrough */ 445 | case XK_BackSpace: 446 | if (cursor == 0) 447 | return; 448 | insert(NULL, nextrune(-1) - cursor); 449 | break; 450 | case XK_End: 451 | case XK_KP_End: 452 | if (text[cursor] != '\0') { 453 | cursor = strlen(text); 454 | break; 455 | } 456 | if (next) { 457 | /* jump to end of list and position items in reverse */ 458 | curr = matchend; 459 | calcoffsets(); 460 | curr = prev; 461 | calcoffsets(); 462 | while (next && (curr = curr->right)) 463 | calcoffsets(); 464 | } 465 | sel = matchend; 466 | break; 467 | case XK_Escape: 468 | cleanup(); 469 | exit(1); 470 | case XK_Home: 471 | case XK_KP_Home: 472 | if (sel == matches) { 473 | cursor = 0; 474 | break; 475 | } 476 | sel = curr = matches; 477 | calcoffsets(); 478 | break; 479 | case XK_Left: 480 | case XK_KP_Left: 481 | if (cursor > 0 && (!sel || !sel->left || lines > 0)) { 482 | cursor = nextrune(-1); 483 | break; 484 | } 485 | if (lines > 0) 486 | return; 487 | /* fallthrough */ 488 | case XK_Up: 489 | case XK_KP_Up: 490 | if (sel && sel->left && (sel = sel->left)->right == curr) { 491 | curr = prev; 492 | calcoffsets(); 493 | } 494 | break; 495 | case XK_Next: 496 | case XK_KP_Next: 497 | if (!next) 498 | return; 499 | sel = curr = next; 500 | calcoffsets(); 501 | break; 502 | case XK_Prior: 503 | case XK_KP_Prior: 504 | if (!prev) 505 | return; 506 | sel = curr = prev; 507 | calcoffsets(); 508 | break; 509 | case XK_Return: 510 | case XK_KP_Enter: 511 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); 512 | if (!(ev->state & ControlMask)) { 513 | cleanup(); 514 | exit(0); 515 | } 516 | if (sel) 517 | sel->out = 1; 518 | break; 519 | case XK_Right: 520 | case XK_KP_Right: 521 | if (text[cursor] != '\0') { 522 | cursor = nextrune(+1); 523 | break; 524 | } 525 | if (lines > 0) 526 | return; 527 | /* fallthrough */ 528 | case XK_Down: 529 | case XK_KP_Down: 530 | if (sel && sel->right && (sel = sel->right) == next) { 531 | curr = next; 532 | calcoffsets(); 533 | } 534 | break; 535 | case XK_Tab: 536 | if (!sel) 537 | return; 538 | cursor = strnlen(sel->text, sizeof text - 1); 539 | memcpy(text, sel->text, cursor); 540 | text[cursor] = '\0'; 541 | match(); 542 | break; 543 | } 544 | 545 | draw: 546 | drawmenu(); 547 | } 548 | 549 | static void 550 | paste(void) 551 | { 552 | char *p, *q; 553 | int di; 554 | unsigned long dl; 555 | Atom da; 556 | 557 | /* we have been given the current selection, now insert it into input */ 558 | if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, 559 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p) 560 | == Success && p) { 561 | insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); 562 | XFree(p); 563 | } 564 | drawmenu(); 565 | } 566 | 567 | static void 568 | readstdin(void) 569 | { 570 | char *line = NULL; 571 | size_t i, itemsiz = 0, linesiz = 0; 572 | ssize_t len; 573 | 574 | /* read each line from stdin and add it to the item list */ 575 | for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { 576 | if (i + 1 >= itemsiz) { 577 | itemsiz += 256; 578 | if (!(items = realloc(items, itemsiz * sizeof(*items)))) 579 | die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); 580 | } 581 | if (line[len - 1] == '\n') 582 | line[len - 1] = '\0'; 583 | if (!(items[i].text = strdup(line))) 584 | die("strdup:"); 585 | 586 | items[i].out = 0; 587 | } 588 | free(line); 589 | if (items) 590 | items[i].text = NULL; 591 | lines = MIN(lines, i); 592 | } 593 | 594 | static void 595 | run(void) 596 | { 597 | XEvent ev; 598 | 599 | while (!XNextEvent(dpy, &ev)) { 600 | if (XFilterEvent(&ev, win)) 601 | continue; 602 | switch(ev.type) { 603 | case DestroyNotify: 604 | if (ev.xdestroywindow.window != win) 605 | break; 606 | cleanup(); 607 | exit(1); 608 | case Expose: 609 | if (ev.xexpose.count == 0) 610 | drw_map(drw, win, 0, 0, mw, mh); 611 | break; 612 | case FocusIn: 613 | /* regrab focus from parent window */ 614 | if (ev.xfocus.window != win) 615 | grabfocus(); 616 | break; 617 | case KeyPress: 618 | keypress(&ev.xkey); 619 | break; 620 | case SelectionNotify: 621 | if (ev.xselection.property == utf8) 622 | paste(); 623 | break; 624 | case VisibilityNotify: 625 | if (ev.xvisibility.state != VisibilityUnobscured) 626 | XRaiseWindow(dpy, win); 627 | break; 628 | } 629 | } 630 | } 631 | 632 | static void 633 | setup(void) 634 | { 635 | int x, y, i, j; 636 | unsigned int du; 637 | XSetWindowAttributes swa; 638 | XIM xim; 639 | Window w, dw, *dws; 640 | XWindowAttributes wa; 641 | XClassHint ch = {"dmenu", "dmenu"}; 642 | #ifdef XINERAMA 643 | XineramaScreenInfo *info; 644 | Window pw; 645 | int a, di, n, area = 0; 646 | #endif 647 | /* init appearance */ 648 | for (j = 0; j < SchemeLast; j++) 649 | scheme[j] = drw_scm_create(drw, colors[j], 2); 650 | 651 | clip = XInternAtom(dpy, "CLIPBOARD", False); 652 | utf8 = XInternAtom(dpy, "UTF8_STRING", False); 653 | 654 | /* calculate menu geometry */ 655 | bh = drw->fonts->h; 656 | bh = user_bh ? bh + user_bh : bh + 2; 657 | lines = MAX(lines, 0); 658 | mh = (lines + 1) * bh; 659 | #ifdef XINERAMA 660 | i = 0; 661 | if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { 662 | XGetInputFocus(dpy, &w, &di); 663 | if (mon >= 0 && mon < n) 664 | i = mon; 665 | else if (w != root && w != PointerRoot && w != None) { 666 | /* find top-level window containing current input focus */ 667 | do { 668 | if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) 669 | XFree(dws); 670 | } while (w != root && w != pw); 671 | /* find xinerama screen with which the window intersects most */ 672 | if (XGetWindowAttributes(dpy, pw, &wa)) 673 | for (j = 0; j < n; j++) 674 | if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { 675 | area = a; 676 | i = j; 677 | } 678 | } 679 | /* no focused window is on screen, so use pointer location instead */ 680 | if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) 681 | for (i = 0; i < n; i++) 682 | if (INTERSECT(x, y, 1, 1, info[i]) != 0) 683 | break; 684 | 685 | //x = info[i].x_org; 686 | //y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 687 | //mw = info[i].width; 688 | x = info[i].x_org + dmx; 689 | y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy); 690 | mw = (dmw>0 ? dmw : info[i].width);; 691 | 692 | XFree(info); 693 | } else 694 | #endif 695 | { 696 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 697 | die("could not get embedding window attributes: 0x%lx", 698 | parentwin); 699 | //x = 0; 700 | //y = topbar ? 0 : wa.height - mh; 701 | //mw = wa.width; 702 | x = dmx; 703 | y = topbar ? dmy : wa.height - mh - dmy; 704 | mw = (dmw>0 ? dmw : wa.width); 705 | } 706 | promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 707 | inputw = mw / 3; /* input width: ~33% of monitor width */ 708 | match(); 709 | 710 | /* create menu window */ 711 | swa.override_redirect = True; 712 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 713 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; 714 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, 715 | CopyFromParent, CopyFromParent, CopyFromParent, 716 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 717 | XSetClassHint(dpy, win, &ch); 718 | 719 | 720 | /* input methods */ 721 | if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) 722 | die("XOpenIM failed: could not open input device"); 723 | 724 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 725 | XNClientWindow, win, XNFocusWindow, win, NULL); 726 | 727 | XMapRaised(dpy, win); 728 | if (embed) { 729 | XReparentWindow(dpy, win, parentwin, x, y); 730 | XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); 731 | if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { 732 | for (i = 0; i < du && dws[i] != win; ++i) 733 | XSelectInput(dpy, dws[i], FocusChangeMask); 734 | XFree(dws); 735 | } 736 | grabfocus(); 737 | } 738 | drw_resize(drw, mw, mh); 739 | drawmenu(); 740 | } 741 | 742 | static void 743 | usage(void) 744 | { 745 | die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 746 | " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); 747 | } 748 | 749 | int 750 | main(int argc, char *argv[]) 751 | { 752 | XWindowAttributes wa; 753 | int i, fast = 0; 754 | 755 | for (i = 1; i < argc; i++) 756 | /* these options take no arguments */ 757 | if (!strcmp(argv[i], "-v")) { /* prints version information */ 758 | puts("dmenu-"VERSION); 759 | exit(0); 760 | } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ 761 | topbar = 0; 762 | else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ 763 | fast = 1; 764 | else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ 765 | fstrncmp = strncasecmp; 766 | fstrstr = cistrstr; 767 | } else if (i + 1 == argc) 768 | usage(); 769 | /* these options take one argument */ 770 | else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ 771 | lines = atoi(argv[++i]); 772 | else if (!strcmp(argv[i], "-m")) 773 | mon = atoi(argv[++i]); 774 | else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ 775 | prompt = argv[++i]; 776 | else if (!strcmp(argv[i], "-fn")) /* font or font set */ 777 | fonts[0] = argv[++i]; 778 | else if (!strcmp(argv[i], "-nb")) /* normal background color */ 779 | colors[SchemeNorm][ColBg] = argv[++i]; 780 | else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ 781 | colors[SchemeNorm][ColFg] = argv[++i]; 782 | else if (!strcmp(argv[i], "-sb")) /* selected background color */ 783 | colors[SchemeSel][ColBg] = argv[++i]; 784 | else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ 785 | colors[SchemeSel][ColFg] = argv[++i]; 786 | else if (!strcmp(argv[i], "-w")) /* embedding window id */ 787 | embed = argv[++i]; 788 | else 789 | usage(); 790 | 791 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 792 | fputs("warning: no locale support\n", stderr); 793 | if (!(dpy = XOpenDisplay(NULL))) 794 | die("cannot open display"); 795 | screen = DefaultScreen(dpy); 796 | root = RootWindow(dpy, screen); 797 | if (!embed || !(parentwin = strtol(embed, NULL, 0))) 798 | parentwin = root; 799 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 800 | die("could not get embedding window attributes: 0x%lx", 801 | parentwin); 802 | drw = drw_create(dpy, screen, root, wa.width, wa.height); 803 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 804 | die("no fonts could be loaded."); 805 | lrpad = drw->fonts->h; 806 | 807 | #ifdef __OpenBSD__ 808 | if (pledge("stdio rpath", NULL) == -1) 809 | die("pledge"); 810 | #endif 811 | 812 | if (fast && !isatty(0)) { 813 | grabkeyboard(); 814 | readstdin(); 815 | } else { 816 | readstdin(); 817 | grabkeyboard(); 818 | } 819 | setup(); 820 | run(); 821 | 822 | return 1; /* unreachable */ 823 | } 824 | -------------------------------------------------------------------------------- /dmenu/dmenu_path: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" 4 | cache="$cachedir/dmenu_run" 5 | 6 | [ ! -e "$cachedir" ] && mkdir -p "$cachedir" 7 | 8 | IFS=: 9 | if stest -dqr -n "$cache" $PATH; then 10 | stest -flx $PATH | sort -u | tee "$cache" 11 | else 12 | cat "$cache" 13 | fi 14 | -------------------------------------------------------------------------------- /dmenu/dmenu_run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & 3 | -------------------------------------------------------------------------------- /dmenu/drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | drw_fontset_free(drw->fonts); 99 | free(drw); 100 | } 101 | 102 | /* This function is an implementation detail. Library users should use 103 | * drw_fontset_create instead. 104 | */ 105 | static Fnt * 106 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 107 | { 108 | Fnt *font; 109 | XftFont *xfont = NULL; 110 | FcPattern *pattern = NULL; 111 | 112 | if (fontname) { 113 | /* Using the pattern found at font->xfont->pattern does not yield the 114 | * same substitution results as using the pattern returned by 115 | * FcNameParse; using the latter results in the desired fallback 116 | * behaviour whereas the former just results in missing-character 117 | * rectangles being drawn, at least with some fonts. */ 118 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 119 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 120 | return NULL; 121 | } 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 123 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 124 | XftFontClose(drw->dpy, xfont); 125 | return NULL; 126 | } 127 | } else if (fontpattern) { 128 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 129 | fprintf(stderr, "error, cannot load font from pattern.\n"); 130 | return NULL; 131 | } 132 | } else { 133 | die("no font specified."); 134 | } 135 | 136 | font = ecalloc(1, sizeof(Fnt)); 137 | font->xfont = xfont; 138 | font->pattern = pattern; 139 | font->h = xfont->ascent + xfont->descent; 140 | font->dpy = drw->dpy; 141 | 142 | return font; 143 | } 144 | 145 | static void 146 | xfont_free(Fnt *font) 147 | { 148 | if (!font) 149 | return; 150 | if (font->pattern) 151 | FcPatternDestroy(font->pattern); 152 | XftFontClose(font->dpy, font->xfont); 153 | free(font); 154 | } 155 | 156 | Fnt* 157 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 158 | { 159 | Fnt *cur, *ret = NULL; 160 | size_t i; 161 | 162 | if (!drw || !fonts) 163 | return NULL; 164 | 165 | for (i = 1; i <= fontcount; i++) { 166 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 167 | cur->next = ret; 168 | ret = cur; 169 | } 170 | } 171 | return (drw->fonts = ret); 172 | } 173 | 174 | void 175 | drw_fontset_free(Fnt *font) 176 | { 177 | if (font) { 178 | drw_fontset_free(font->next); 179 | xfont_free(font); 180 | } 181 | } 182 | 183 | void 184 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 185 | { 186 | if (!drw || !dest || !clrname) 187 | return; 188 | 189 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 190 | DefaultColormap(drw->dpy, drw->screen), 191 | clrname, dest)) 192 | die("error, cannot allocate color '%s'", clrname); 193 | } 194 | 195 | /* Wrapper to create color schemes. The caller has to call free(3) on the 196 | * returned color scheme when done using it. */ 197 | Clr * 198 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 199 | { 200 | size_t i; 201 | Clr *ret; 202 | 203 | /* need at least two colors for a scheme */ 204 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 205 | return NULL; 206 | 207 | for (i = 0; i < clrcount; i++) 208 | drw_clr_create(drw, &ret[i], clrnames[i]); 209 | return ret; 210 | } 211 | 212 | void 213 | drw_setfontset(Drw *drw, Fnt *set) 214 | { 215 | if (drw) 216 | drw->fonts = set; 217 | } 218 | 219 | void 220 | drw_setscheme(Drw *drw, Clr *scm) 221 | { 222 | if (drw) 223 | drw->scheme = scm; 224 | } 225 | 226 | void 227 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 228 | { 229 | if (!drw || !drw->scheme) 230 | return; 231 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 232 | if (filled) 233 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 234 | else 235 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 236 | } 237 | 238 | int 239 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 240 | { 241 | int ty, ellipsis_x = 0; 242 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; 243 | XftDraw *d = NULL; 244 | Fnt *usedfont, *curfont, *nextfont; 245 | int utf8strlen, utf8charlen, render = x || y || w || h; 246 | long utf8codepoint = 0; 247 | const char *utf8str; 248 | FcCharSet *fccharset; 249 | FcPattern *fcpattern; 250 | FcPattern *match; 251 | XftResult result; 252 | int charexists = 0, overflow = 0; 253 | /* keep track of a couple codepoints for which we have no match. */ 254 | static unsigned int nomatches[128], ellipsis_width; 255 | 256 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 257 | return 0; 258 | 259 | if (!render) { 260 | w = invert ? invert : ~invert; 261 | } else { 262 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 263 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 264 | d = XftDrawCreate(drw->dpy, drw->drawable, 265 | DefaultVisual(drw->dpy, drw->screen), 266 | DefaultColormap(drw->dpy, drw->screen)); 267 | x += lpad; 268 | w -= lpad; 269 | } 270 | 271 | usedfont = drw->fonts; 272 | if (!ellipsis_width && render) 273 | ellipsis_width = drw_fontset_getwidth(drw, "..."); 274 | while (1) { 275 | ew = ellipsis_len = utf8strlen = 0; 276 | utf8str = text; 277 | nextfont = NULL; 278 | while (*text) { 279 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 280 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 281 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 282 | if (charexists) { 283 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 284 | if (ew + ellipsis_width <= w) { 285 | /* keep track where the ellipsis still fits */ 286 | ellipsis_x = x + ew; 287 | ellipsis_w = w - ew; 288 | ellipsis_len = utf8strlen; 289 | } 290 | 291 | if (ew + tmpw > w) { 292 | overflow = 1; 293 | /* called from drw_fontset_getwidth_clamp(): 294 | * it wants the width AFTER the overflow 295 | */ 296 | if (!render) 297 | x += tmpw; 298 | else 299 | utf8strlen = ellipsis_len; 300 | } else if (curfont == usedfont) { 301 | utf8strlen += utf8charlen; 302 | text += utf8charlen; 303 | ew += tmpw; 304 | } else { 305 | nextfont = curfont; 306 | } 307 | break; 308 | } 309 | } 310 | 311 | if (overflow || !charexists || nextfont) 312 | break; 313 | else 314 | charexists = 0; 315 | } 316 | 317 | if (utf8strlen) { 318 | if (render) { 319 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 320 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 321 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 322 | } 323 | x += ew; 324 | w -= ew; 325 | } 326 | if (render && overflow) 327 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); 328 | 329 | if (!*text || overflow) { 330 | break; 331 | } else if (nextfont) { 332 | charexists = 0; 333 | usedfont = nextfont; 334 | } else { 335 | /* Regardless of whether or not a fallback font is found, the 336 | * character must be drawn. */ 337 | charexists = 1; 338 | 339 | hash = (unsigned int)utf8codepoint; 340 | hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; 341 | hash = ((hash >> 15) ^ hash) * 0xD35A2D97; 342 | h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); 343 | h1 = (hash >> 17) % LENGTH(nomatches); 344 | /* avoid expensive XftFontMatch call when we know we won't find a match */ 345 | if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) 346 | goto no_match; 347 | 348 | fccharset = FcCharSetCreate(); 349 | FcCharSetAddChar(fccharset, utf8codepoint); 350 | 351 | if (!drw->fonts->pattern) { 352 | /* Refer to the comment in xfont_create for more information. */ 353 | die("the first font in the cache must be loaded from a font string."); 354 | } 355 | 356 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 357 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 358 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 359 | 360 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 361 | FcDefaultSubstitute(fcpattern); 362 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 363 | 364 | FcCharSetDestroy(fccharset); 365 | FcPatternDestroy(fcpattern); 366 | 367 | if (match) { 368 | usedfont = xfont_create(drw, NULL, match); 369 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 370 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 371 | ; /* NOP */ 372 | curfont->next = usedfont; 373 | } else { 374 | xfont_free(usedfont); 375 | nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; 376 | no_match: 377 | usedfont = drw->fonts; 378 | } 379 | } 380 | } 381 | } 382 | if (d) 383 | XftDrawDestroy(d); 384 | 385 | return x + (render ? w : 0); 386 | } 387 | 388 | void 389 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 390 | { 391 | if (!drw) 392 | return; 393 | 394 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 395 | XSync(drw->dpy, False); 396 | } 397 | 398 | unsigned int 399 | drw_fontset_getwidth(Drw *drw, const char *text) 400 | { 401 | if (!drw || !drw->fonts || !text) 402 | return 0; 403 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 404 | } 405 | 406 | unsigned int 407 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 408 | { 409 | unsigned int tmp = 0; 410 | if (drw && drw->fonts && text && n) 411 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 412 | return MIN(n, tmp); 413 | } 414 | 415 | void 416 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 417 | { 418 | XGlyphInfo ext; 419 | 420 | if (!font || !text) 421 | return; 422 | 423 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 424 | if (w) 425 | *w = ext.xOff; 426 | if (h) 427 | *h = font->h; 428 | } 429 | 430 | Cur * 431 | drw_cur_create(Drw *drw, int shape) 432 | { 433 | Cur *cur; 434 | 435 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 436 | return NULL; 437 | 438 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 439 | 440 | return cur; 441 | } 442 | 443 | void 444 | drw_cur_free(Drw *drw, Cur *cursor) 445 | { 446 | if (!cursor) 447 | return; 448 | 449 | XFreeCursor(drw->dpy, cursor->cursor); 450 | free(cursor); 451 | } 452 | -------------------------------------------------------------------------------- /dmenu/drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); 39 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 40 | 41 | /* Colorscheme abstraction */ 42 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 43 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 44 | 45 | /* Cursor abstraction */ 46 | Cur *drw_cur_create(Drw *drw, int shape); 47 | void drw_cur_free(Drw *drw, Cur *cursor); 48 | 49 | /* Drawing context manipulation */ 50 | void drw_setfontset(Drw *drw, Fnt *set); 51 | void drw_setscheme(Drw *drw, Clr *scm); 52 | 53 | /* Drawing functions */ 54 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 55 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 56 | 57 | /* Map functions */ 58 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 59 | -------------------------------------------------------------------------------- /dmenu/stest.1: -------------------------------------------------------------------------------- 1 | .TH STEST 1 dmenu\-VERSION 2 | .SH NAME 3 | stest \- filter a list of files by properties 4 | .SH SYNOPSIS 5 | .B stest 6 | .RB [ -abcdefghlpqrsuwx ] 7 | .RB [ -n 8 | .IR file ] 9 | .RB [ -o 10 | .IR file ] 11 | .RI [ file ...] 12 | .SH DESCRIPTION 13 | .B stest 14 | takes a list of files and filters by the files' properties, analogous to 15 | .IR test (1). 16 | Files which pass all tests are printed to stdout. If no files are given, stest 17 | reads files from stdin. 18 | .SH OPTIONS 19 | .TP 20 | .B \-a 21 | Test hidden files. 22 | .TP 23 | .B \-b 24 | Test that files are block specials. 25 | .TP 26 | .B \-c 27 | Test that files are character specials. 28 | .TP 29 | .B \-d 30 | Test that files are directories. 31 | .TP 32 | .B \-e 33 | Test that files exist. 34 | .TP 35 | .B \-f 36 | Test that files are regular files. 37 | .TP 38 | .B \-g 39 | Test that files have their set-group-ID flag set. 40 | .TP 41 | .B \-h 42 | Test that files are symbolic links. 43 | .TP 44 | .B \-l 45 | Test the contents of a directory given as an argument. 46 | .TP 47 | .BI \-n " file" 48 | Test that files are newer than 49 | .IR file . 50 | .TP 51 | .BI \-o " file" 52 | Test that files are older than 53 | .IR file . 54 | .TP 55 | .B \-p 56 | Test that files are named pipes. 57 | .TP 58 | .B \-q 59 | No files are printed, only the exit status is returned. 60 | .TP 61 | .B \-r 62 | Test that files are readable. 63 | .TP 64 | .B \-s 65 | Test that files are not empty. 66 | .TP 67 | .B \-u 68 | Test that files have their set-user-ID flag set. 69 | .TP 70 | .B \-v 71 | Invert the sense of tests, only failing files pass. 72 | .TP 73 | .B \-w 74 | Test that files are writable. 75 | .TP 76 | .B \-x 77 | Test that files are executable. 78 | .SH EXIT STATUS 79 | .TP 80 | .B 0 81 | At least one file passed all tests. 82 | .TP 83 | .B 1 84 | No files passed all tests. 85 | .TP 86 | .B 2 87 | An error occurred. 88 | .SH SEE ALSO 89 | .IR dmenu (1), 90 | .IR test (1) 91 | -------------------------------------------------------------------------------- /dmenu/stest.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "arg.h" 12 | char *argv0; 13 | 14 | #define FLAG(x) (flag[(x)-'a']) 15 | 16 | static void test(const char *, const char *); 17 | static void usage(void); 18 | 19 | static int match = 0; 20 | static int flag[26]; 21 | static struct stat old, new; 22 | 23 | static void 24 | test(const char *path, const char *name) 25 | { 26 | struct stat st, ln; 27 | 28 | if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ 29 | && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ 30 | && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ 31 | && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ 32 | && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ 33 | && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ 34 | && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ 35 | && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ 36 | && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ 37 | && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ 38 | && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ 39 | && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ 40 | && (!FLAG('s') || st.st_size > 0) /* not empty */ 41 | && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ 42 | && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ 43 | && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ 44 | if (FLAG('q')) 45 | exit(0); 46 | match = 1; 47 | puts(name); 48 | } 49 | } 50 | 51 | static void 52 | usage(void) 53 | { 54 | fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " 55 | "[-n file] [-o file] [file...]\n", argv0); 56 | exit(2); /* like test(1) return > 1 on error */ 57 | } 58 | 59 | int 60 | main(int argc, char *argv[]) 61 | { 62 | struct dirent *d; 63 | char path[PATH_MAX], *line = NULL, *file; 64 | size_t linesiz = 0; 65 | ssize_t n; 66 | DIR *dir; 67 | int r; 68 | 69 | ARGBEGIN { 70 | case 'n': /* newer than file */ 71 | case 'o': /* older than file */ 72 | file = EARGF(usage()); 73 | if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) 74 | perror(file); 75 | break; 76 | default: 77 | /* miscellaneous operators */ 78 | if (strchr("abcdefghlpqrsuvwx", ARGC())) 79 | FLAG(ARGC()) = 1; 80 | else 81 | usage(); /* unknown flag */ 82 | } ARGEND; 83 | 84 | if (!argc) { 85 | /* read list from stdin */ 86 | while ((n = getline(&line, &linesiz, stdin)) > 0) { 87 | if (line[n - 1] == '\n') 88 | line[n - 1] = '\0'; 89 | test(line, line); 90 | } 91 | free(line); 92 | } else { 93 | for (; argc; argc--, argv++) { 94 | if (FLAG('l') && (dir = opendir(*argv))) { 95 | /* test directory contents */ 96 | while ((d = readdir(dir))) { 97 | r = snprintf(path, sizeof path, "%s/%s", 98 | *argv, d->d_name); 99 | if (r >= 0 && (size_t)r < sizeof path) 100 | test(path, d->d_name); 101 | } 102 | closedir(dir); 103 | } else { 104 | test(*argv, *argv); 105 | } 106 | } 107 | } 108 | return match ? 0 : 1; 109 | } 110 | -------------------------------------------------------------------------------- /dmenu/util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void 10 | die(const char *fmt, ...) 11 | { 12 | va_list ap; 13 | 14 | va_start(ap, fmt); 15 | vfprintf(stderr, fmt, ap); 16 | va_end(ap); 17 | 18 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 19 | fputc(' ', stderr); 20 | perror(NULL); 21 | } else { 22 | fputc('\n', stderr); 23 | } 24 | 25 | exit(1); 26 | } 27 | 28 | void * 29 | ecalloc(size_t nmemb, size_t size) 30 | { 31 | void *p; 32 | 33 | if (!(p = calloc(nmemb, size))) 34 | die("calloc:"); 35 | return p; 36 | } 37 | -------------------------------------------------------------------------------- /dmenu/util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | #define LENGTH(X) (sizeof (X) / sizeof (X)[0]) 7 | 8 | void die(const char *fmt, ...); 9 | void *ecalloc(size_t nmemb, size_t size); 10 | -------------------------------------------------------------------------------- /dwm/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | dwm 3 | *.rej 4 | *.orig 5 | -------------------------------------------------------------------------------- /dwm/LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2009 Jukka Salmi 5 | © 2006-2007 Sander van Dijk 6 | © 2007-2011 Peter Hartlich 7 | © 2007-2009 Szabolcs Nagy 8 | © 2007-2009 Christof Musik 9 | © 2007-2009 Premysl Hruby 10 | © 2007-2008 Enno Gottox Boland 11 | © 2008 Martin Hurton 12 | © 2008 Neale Pickett 13 | © 2009 Mate Nagy 14 | © 2010-2016 Hiltjo Posthuma 15 | © 2010-2012 Connor Lane Smith 16 | © 2011 Christoph Lohmann <20h@r-36.net> 17 | © 2015-2016 Quentin Rameau 18 | © 2015-2016 Eric Pruitt 19 | © 2016-2017 Markus Teich 20 | © 2020-2022 Chris Down 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a 23 | copy of this software and associated documentation files (the "Software"), 24 | to deal in the Software without restriction, including without limitation 25 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | and/or sell copies of the Software, and to permit persons to whom the 27 | Software is furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 35 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 37 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 38 | DEALINGS IN THE SOFTWARE. 39 | -------------------------------------------------------------------------------- /dwm/Makefile: -------------------------------------------------------------------------------- 1 | # dwm - dynamic window manager 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dwm.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options dwm 10 | 11 | options: 12 | @echo dwm build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | ${CC} -c ${CFLAGS} $< 19 | 20 | ${OBJ}: config.def.h config.mk 21 | 22 | dwm: ${OBJ} 23 | ${CC} -o $@ ${OBJ} ${LDFLAGS} 24 | 25 | clean: 26 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz 27 | 28 | dist: clean 29 | mkdir -p dwm-${VERSION} 30 | cp -R LICENSE Makefile README config.def.h config.mk\ 31 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} 32 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} 33 | gzip dwm-${VERSION}.tar 34 | rm -rf dwm-${VERSION} 35 | 36 | install: all 37 | mkdir -p ${DESTDIR}${PREFIX}/bin 38 | cp -f dwm ${DESTDIR}${PREFIX}/bin 39 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm 40 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 41 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 42 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 43 | 44 | uninstall: 45 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ 46 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 47 | 48 | .PHONY: all options clean dist install uninstall 49 | -------------------------------------------------------------------------------- /dwm/README: -------------------------------------------------------------------------------- 1 | dwm - dynamic window manager 2 | ============================ 3 | dwm is an extremely fast, small, and dynamic window manager for X. 4 | 5 | 6 | Requirements 7 | ------------ 8 | In order to build dwm you need the Xlib header files. 9 | 10 | 11 | Installation 12 | ------------ 13 | Edit config.mk to match your local setup (dwm is installed into 14 | the /usr/local namespace by default). 15 | 16 | Afterwards enter the following command to build and install dwm (if 17 | necessary as root): 18 | 19 | make clean install 20 | 21 | 22 | Running dwm 23 | ----------- 24 | Add the following line to your .xinitrc to start dwm using startx: 25 | 26 | exec dwm 27 | 28 | In order to connect dwm to a specific display, make sure that 29 | the DISPLAY environment variable is set correctly, e.g.: 30 | 31 | DISPLAY=foo.bar:1 exec dwm 32 | 33 | (This will start dwm on display :1 of the host foo.bar.) 34 | 35 | In order to display status info in the bar, you can do something 36 | like this in your .xinitrc: 37 | 38 | while xsetroot -name "`date` `uptime | sed 's/.*,//'`" 39 | do 40 | sleep 1 41 | done & 42 | exec dwm 43 | 44 | 45 | Configuration 46 | ------------- 47 | The configuration of dwm is done by creating a custom config.h 48 | and (re)compiling the source code. 49 | -------------------------------------------------------------------------------- /dwm/config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance */ 4 | static const unsigned int borderpx = 0; /* border pixel of windows */ 5 | static const unsigned int snap = 0; /* snap pixel */ 6 | static const int showbar = 1; /* 0 means no bar */ 7 | static const int topbar = 1; /* 0 means bottom bar */ 8 | static const int focusonwheel = 0; 9 | static const int horizpadbar = 0; /* horizontal padding for statusbar */ 10 | static const int vertpadbar = 14; /* vertical padding for statusbar */ 11 | static const int statusvertpadbar = vertpadbar; /* padding for status specifically */ 12 | static const int seltagindheight = 5; // height of bar under selected tag 13 | static const int seltagindpad = 4; // amount of padding surrounding selected tag 14 | static const char *fonts[] = { "Iosevka Term NF:size=11:style=Bold" }; 15 | 16 | static const char bg[] = "#100f0f"; 17 | static const char fg[] = "#fffcf0"; 18 | static const char accent[] = "#ad8301"; 19 | static const char *colors[][3] = { 20 | /* fg bg border */ 21 | [SchemeNorm] = { fg, bg, fg}, 22 | [SchemeSel] = { accent, bg, accent}, 23 | }; 24 | 25 | static const char *const autostart[] = { 26 | "feh", "--bg-fill", "/home/mespyr/.config/WALLPAPER.jpg", NULL, 27 | "picom", NULL, 28 | "setxkbmap", "-option", "ctrl:nocaps", NULL, 29 | "xset", "-b", NULL, 30 | "/home/mespyr/dwm_bar.sh", NULL, 31 | NULL /* terminate */ 32 | }; 33 | 34 | /* tagging */ 35 | static const char *tags[] = { "1", "2", "3", "4", "5" }; 36 | //static const char *alttags[] = { "[1]", "[2]", "[3]", "[4]", "[5]" }; 37 | static const char *alttags[] = { "1̰", "2̰", "3̰", "4̰", "5̰" }; 38 | 39 | static const Rule rules[] = { 40 | /* xprop(1): 41 | * WM_CLASS(STRING) = instance, class 42 | * WM_NAME(STRING) = title 43 | */ 44 | /* class instance title tags mask isfloating mon x,y,w,h*/ 45 | { "steam", NULL, NULL, 1 << 2, 1, 0, 1100, 520, 1200, 1350 }, 46 | { "vesktop", NULL, NULL, 1 << 3, 1, 1, -1, -1, -1, -1 }, 47 | { "obs", NULL, NULL, 1 << 4, 1, 1, -1, -1, -1, -1 }, 48 | { "pavucontrol", NULL, NULL, 0, 1, -1, 15, 40, 600, 400 }, 49 | { "BoltLauncher", NULL, NULL, 1 << 2, 1, 0, -1, -1, -1, -1 }, 50 | }; 51 | 52 | /* layout(s) */ 53 | static const float mfact = 0.65; /* factor of master area size [0.05..0.95] */ 54 | static const int nmaster = 1; /* number of clients in master area */ 55 | static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ 56 | static const int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */ 57 | 58 | static const Layout layouts[] = { 59 | /* symbol arrange function */ 60 | /* { "", centeredmaster }, /\* first entry is default *\/ */ 61 | { "", tile}, /* first entry is default */ 62 | { "", NULL }, /* no layout function means floating behavior */ 63 | // { "[M]", monocle }, 64 | }; 65 | 66 | /* key definitions */ 67 | #define MODKEY Mod4Mask 68 | #define TAGKEYS(KEY,TAG) \ 69 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ 70 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ 71 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ 72 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, 73 | 74 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ 75 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } 76 | 77 | /* commands */ 78 | static const char *dmenucmd[] = { "dmenu_run", NULL }; 79 | static const char *termcmd[] = { "tabbed", "alacritty", "--embed", NULL }; 80 | static const char *editorcmd[] = { "emacs", NULL }; 81 | static const char *volumecmd[] = { "pavucontrol", NULL }; 82 | 83 | static const Key keys[] = { 84 | /* modifier key function argument */ 85 | { MODKEY, XK_d, spawn, {.v = dmenucmd } }, 86 | { MODKEY, XK_Return, spawn, {.v = termcmd } }, 87 | { MODKEY, XK_w, spawn, {.v = editorcmd } }, 88 | { MODKEY, XK_v, spawn, {.v = volumecmd } }, 89 | 90 | { MODKEY, XK_b, togglebar, {0} }, 91 | { MODKEY, XK_j, focusstack, {.i = +1 } }, 92 | //{ MODKEY, XK_k, focusstack, {.i = -1 } }, 93 | //{ MODKEY, XK_i, incnmaster, {.i = +1 } }, 94 | //{ MODKEY, XK_d, incnmaster, {.i = -1 } }, 95 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, 96 | { MODKEY, XK_l, setmfact, {.f = +0.05} }, 97 | { MODKEY|ShiftMask, XK_Return, zoom, {0} }, 98 | { MODKEY, XK_Tab, view, {0} }, 99 | { MODKEY, XK_c, killclient, {0} }, 100 | //{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, 101 | //{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, 102 | //{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, 103 | { MODKEY, XK_space, setlayout, {0} }, 104 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 105 | //{ MODKEY, XK_0, view, {.ui = ~0 } }, 106 | //{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 107 | { MODKEY, XK_comma, focusmon, {.i = -1 } }, 108 | //{ MODKEY, XK_period, focusmon, {.i = +1 } }, 109 | /* { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, */ 110 | //{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, 111 | TAGKEYS( XK_1, 0) 112 | TAGKEYS( XK_2, 1) 113 | TAGKEYS( XK_3, 2) 114 | TAGKEYS( XK_4, 3) 115 | TAGKEYS( XK_5, 4) 116 | { MODKEY|ShiftMask, XK_q, quit, {0} }, 117 | }; 118 | 119 | /* button definitions */ 120 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ 121 | static const Button buttons[] = { 122 | /* click event mask button function argument */ 123 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, 124 | // { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, 125 | { ClkWinTitle, 0, Button2, zoom, {0} }, 126 | { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, 127 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, 128 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, 129 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, 130 | { ClkTagBar, 0, Button1, view, {0} }, 131 | { ClkTagBar, 0, Button3, toggleview, {0} }, 132 | { ClkTagBar, MODKEY, Button1, tag, {0} }, 133 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, 134 | }; 135 | -------------------------------------------------------------------------------- /dwm/config.mk: -------------------------------------------------------------------------------- 1 | # dwm version 2 | VERSION = 6.4 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | X11INC = /usr/X11R6/include 11 | X11LIB = /usr/X11R6/lib 12 | 13 | # Xinerama, comment if you don't want it 14 | XINERAMALIBS = -lXinerama 15 | XINERAMAFLAGS = -DXINERAMA 16 | 17 | # freetype 18 | FREETYPELIBS = -lfontconfig -lXft 19 | FREETYPEINC = /usr/include/freetype2 20 | # OpenBSD (uncomment) 21 | #FREETYPEINC = ${X11INC}/freetype2 22 | #MANPREFIX = ${PREFIX}/man 23 | 24 | # includes and libs 25 | INCS = -I${X11INC} -I${FREETYPEINC} 26 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 27 | 28 | # flags 29 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 30 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 31 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 32 | LDFLAGS = ${LIBS} 33 | 34 | # Solaris 35 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 36 | #LDFLAGS = ${LIBS} 37 | 38 | # compiler and linker 39 | CC = cc 40 | -------------------------------------------------------------------------------- /dwm/drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | drw_fontset_free(drw->fonts); 99 | free(drw); 100 | } 101 | 102 | /* This function is an implementation detail. Library users should use 103 | * drw_fontset_create instead. 104 | */ 105 | static Fnt * 106 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 107 | { 108 | Fnt *font; 109 | XftFont *xfont = NULL; 110 | FcPattern *pattern = NULL; 111 | 112 | if (fontname) { 113 | /* Using the pattern found at font->xfont->pattern does not yield the 114 | * same substitution results as using the pattern returned by 115 | * FcNameParse; using the latter results in the desired fallback 116 | * behaviour whereas the former just results in missing-character 117 | * rectangles being drawn, at least with some fonts. */ 118 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 119 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 120 | return NULL; 121 | } 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 123 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 124 | XftFontClose(drw->dpy, xfont); 125 | return NULL; 126 | } 127 | } else if (fontpattern) { 128 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 129 | fprintf(stderr, "error, cannot load font from pattern.\n"); 130 | return NULL; 131 | } 132 | } else { 133 | die("no font specified."); 134 | } 135 | 136 | font = ecalloc(1, sizeof(Fnt)); 137 | font->xfont = xfont; 138 | font->pattern = pattern; 139 | font->h = xfont->ascent + xfont->descent; 140 | font->dpy = drw->dpy; 141 | 142 | return font; 143 | } 144 | 145 | static void 146 | xfont_free(Fnt *font) 147 | { 148 | if (!font) 149 | return; 150 | if (font->pattern) 151 | FcPatternDestroy(font->pattern); 152 | XftFontClose(font->dpy, font->xfont); 153 | free(font); 154 | } 155 | 156 | Fnt* 157 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 158 | { 159 | Fnt *cur, *ret = NULL; 160 | size_t i; 161 | 162 | if (!drw || !fonts) 163 | return NULL; 164 | 165 | for (i = 1; i <= fontcount; i++) { 166 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 167 | cur->next = ret; 168 | ret = cur; 169 | } 170 | } 171 | return (drw->fonts = ret); 172 | } 173 | 174 | void 175 | drw_fontset_free(Fnt *font) 176 | { 177 | if (font) { 178 | drw_fontset_free(font->next); 179 | xfont_free(font); 180 | } 181 | } 182 | 183 | void 184 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 185 | { 186 | if (!drw || !dest || !clrname) 187 | return; 188 | 189 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 190 | DefaultColormap(drw->dpy, drw->screen), 191 | clrname, dest)) 192 | die("error, cannot allocate color '%s'", clrname); 193 | dest->pixel |= 0xff << 24; 194 | } 195 | 196 | /* Wrapper to create color schemes. The caller has to call free(3) on the 197 | * returned color scheme when done using it. */ 198 | Clr * 199 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 200 | { 201 | size_t i; 202 | Clr *ret; 203 | 204 | /* need at least two colors for a scheme */ 205 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 206 | return NULL; 207 | 208 | for (i = 0; i < clrcount; i++) 209 | drw_clr_create(drw, &ret[i], clrnames[i]); 210 | return ret; 211 | } 212 | 213 | void 214 | drw_setfontset(Drw *drw, Fnt *set) 215 | { 216 | if (drw) 217 | drw->fonts = set; 218 | } 219 | 220 | void 221 | drw_setscheme(Drw *drw, Clr *scm) 222 | { 223 | if (drw) 224 | drw->scheme = scm; 225 | } 226 | 227 | void 228 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 229 | { 230 | if (!drw || !drw->scheme) 231 | return; 232 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 233 | if (filled) 234 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 235 | else 236 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 237 | } 238 | 239 | int 240 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 241 | { 242 | int i, ty, ellipsis_x = 0; 243 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; 244 | XftDraw *d = NULL; 245 | Fnt *usedfont, *curfont, *nextfont; 246 | int utf8strlen, utf8charlen, render = x || y || w || h; 247 | long utf8codepoint = 0; 248 | const char *utf8str; 249 | FcCharSet *fccharset; 250 | FcPattern *fcpattern; 251 | FcPattern *match; 252 | XftResult result; 253 | int charexists = 0, overflow = 0; 254 | /* keep track of a couple codepoints for which we have no match. */ 255 | enum { nomatches_len = 64 }; 256 | static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; 257 | static unsigned int ellipsis_width = 0; 258 | 259 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 260 | return 0; 261 | 262 | if (!render) { 263 | w = invert ? invert : ~invert; 264 | } else { 265 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 266 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 267 | d = XftDrawCreate(drw->dpy, drw->drawable, 268 | DefaultVisual(drw->dpy, drw->screen), 269 | DefaultColormap(drw->dpy, drw->screen)); 270 | x += lpad; 271 | w -= lpad; 272 | } 273 | 274 | usedfont = drw->fonts; 275 | if (!ellipsis_width && render) 276 | ellipsis_width = drw_fontset_getwidth(drw, "..."); 277 | while (1) { 278 | ew = ellipsis_len = utf8strlen = 0; 279 | utf8str = text; 280 | nextfont = NULL; 281 | while (*text) { 282 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 283 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 284 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 285 | if (charexists) { 286 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 287 | if (ew + ellipsis_width <= w) { 288 | /* keep track where the ellipsis still fits */ 289 | ellipsis_x = x + ew; 290 | ellipsis_w = w - ew; 291 | ellipsis_len = utf8strlen; 292 | } 293 | 294 | if (ew + tmpw > w) { 295 | overflow = 1; 296 | /* called from drw_fontset_getwidth_clamp(): 297 | * it wants the width AFTER the overflow 298 | */ 299 | if (!render) 300 | x += tmpw; 301 | else 302 | utf8strlen = ellipsis_len; 303 | } else if (curfont == usedfont) { 304 | utf8strlen += utf8charlen; 305 | text += utf8charlen; 306 | ew += tmpw; 307 | } else { 308 | nextfont = curfont; 309 | } 310 | break; 311 | } 312 | } 313 | 314 | if (overflow || !charexists || nextfont) 315 | break; 316 | else 317 | charexists = 0; 318 | } 319 | 320 | if (utf8strlen) { 321 | if (render) { 322 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 323 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 324 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 325 | } 326 | x += ew; 327 | w -= ew; 328 | } 329 | if (render && overflow) 330 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); 331 | 332 | if (!*text || overflow) { 333 | break; 334 | } else if (nextfont) { 335 | charexists = 0; 336 | usedfont = nextfont; 337 | } else { 338 | /* Regardless of whether or not a fallback font is found, the 339 | * character must be drawn. */ 340 | charexists = 1; 341 | 342 | for (i = 0; i < nomatches_len; ++i) { 343 | /* avoid calling XftFontMatch if we know we won't find a match */ 344 | if (utf8codepoint == nomatches.codepoint[i]) 345 | goto no_match; 346 | } 347 | 348 | fccharset = FcCharSetCreate(); 349 | FcCharSetAddChar(fccharset, utf8codepoint); 350 | 351 | if (!drw->fonts->pattern) { 352 | /* Refer to the comment in xfont_create for more information. */ 353 | die("the first font in the cache must be loaded from a font string."); 354 | } 355 | 356 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 357 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 358 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 359 | 360 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 361 | FcDefaultSubstitute(fcpattern); 362 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 363 | 364 | FcCharSetDestroy(fccharset); 365 | FcPatternDestroy(fcpattern); 366 | 367 | if (match) { 368 | usedfont = xfont_create(drw, NULL, match); 369 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 370 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 371 | ; /* NOP */ 372 | curfont->next = usedfont; 373 | } else { 374 | xfont_free(usedfont); 375 | nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; 376 | no_match: 377 | usedfont = drw->fonts; 378 | } 379 | } 380 | } 381 | } 382 | if (d) 383 | XftDrawDestroy(d); 384 | 385 | return x + (render ? w : 0); 386 | } 387 | 388 | void 389 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 390 | { 391 | if (!drw) 392 | return; 393 | 394 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 395 | XSync(drw->dpy, False); 396 | } 397 | 398 | unsigned int 399 | drw_fontset_getwidth(Drw *drw, const char *text) 400 | { 401 | if (!drw || !drw->fonts || !text) 402 | return 0; 403 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 404 | } 405 | 406 | unsigned int 407 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 408 | { 409 | unsigned int tmp = 0; 410 | if (drw && drw->fonts && text && n) 411 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 412 | return MIN(n, tmp); 413 | } 414 | 415 | void 416 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 417 | { 418 | XGlyphInfo ext; 419 | 420 | if (!font || !text) 421 | return; 422 | 423 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 424 | if (w) 425 | *w = ext.xOff; 426 | if (h) 427 | *h = font->h; 428 | } 429 | 430 | Cur * 431 | drw_cur_create(Drw *drw, int shape) 432 | { 433 | Cur *cur; 434 | 435 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 436 | return NULL; 437 | 438 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 439 | 440 | return cur; 441 | } 442 | 443 | void 444 | drw_cur_free(Drw *drw, Cur *cursor) 445 | { 446 | if (!cursor) 447 | return; 448 | 449 | XFreeCursor(drw->dpy, cursor->cursor); 450 | free(cursor); 451 | } 452 | -------------------------------------------------------------------------------- /dwm/drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); 39 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 40 | 41 | /* Colorscheme abstraction */ 42 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 43 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 44 | 45 | /* Cursor abstraction */ 46 | Cur *drw_cur_create(Drw *drw, int shape); 47 | void drw_cur_free(Drw *drw, Cur *cursor); 48 | 49 | /* Drawing context manipulation */ 50 | void drw_setfontset(Drw *drw, Fnt *set); 51 | void drw_setscheme(Drw *drw, Clr *scm); 52 | 53 | /* Drawing functions */ 54 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 55 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 56 | 57 | /* Map functions */ 58 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 59 | -------------------------------------------------------------------------------- /dwm/dwm.1: -------------------------------------------------------------------------------- 1 | .TH DWM 1 dwm\-VERSION 2 | .SH NAME 3 | dwm \- dynamic window manager 4 | .SH SYNOPSIS 5 | .B dwm 6 | .RB [ \-v ] 7 | .SH DESCRIPTION 8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle 9 | and floating layouts. Either layout can be applied dynamically, optimising the 10 | environment for the application in use and the task performed. 11 | .P 12 | In tiled layouts windows are managed in a master and stacking area. The master 13 | area on the left contains one window by default, and the stacking area on the 14 | right contains all other windows. The number of master area windows can be 15 | adjusted from zero to an arbitrary number. In monocle layout all windows are 16 | maximised to the screen size. In floating layout windows can be resized and 17 | moved freely. Dialog windows are always managed floating, regardless of the 18 | layout applied. 19 | .P 20 | Windows are grouped by tags. Each window can be tagged with one or multiple 21 | tags. Selecting certain tags displays all windows with these tags. 22 | .P 23 | Each screen contains a small status bar which displays all available tags, the 24 | layout, the title of the focused window, and the text read from the root window 25 | name property, if the screen is focused. A floating window is indicated with an 26 | empty square and a maximised floating window is indicated with a filled square 27 | before the windows title. The selected tags are indicated with a different 28 | color. The tags of the focused window are indicated with a filled square in the 29 | top left corner. The tags which are applied to one or more windows are 30 | indicated with an empty square in the top left corner. 31 | .P 32 | dwm draws a small border around windows to indicate the focus state. 33 | .SH OPTIONS 34 | .TP 35 | .B \-v 36 | prints version information to stderr, then exits. 37 | .SH USAGE 38 | .SS Status bar 39 | .TP 40 | .B X root window name 41 | is read and displayed in the status text area. It can be set with the 42 | .BR xsetroot (1) 43 | command. 44 | .TP 45 | .B Button1 46 | click on a tag label to display all windows with that tag, click on the layout 47 | label toggles between tiled and floating layout. 48 | .TP 49 | .B Button3 50 | click on a tag label adds/removes all windows with that tag to/from the view. 51 | .TP 52 | .B Mod1\-Button1 53 | click on a tag label applies that tag to the focused window. 54 | .TP 55 | .B Mod1\-Button3 56 | click on a tag label adds/removes that tag to/from the focused window. 57 | .SS Keyboard commands 58 | .TP 59 | .B Mod1\-Shift\-Return 60 | Start 61 | .BR st(1). 62 | .TP 63 | .B Mod1\-p 64 | Spawn 65 | .BR dmenu(1) 66 | for launching other programs. 67 | .TP 68 | .B Mod1\-, 69 | Focus previous screen, if any. 70 | .TP 71 | .B Mod1\-. 72 | Focus next screen, if any. 73 | .TP 74 | .B Mod1\-Shift\-, 75 | Send focused window to previous screen, if any. 76 | .TP 77 | .B Mod1\-Shift\-. 78 | Send focused window to next screen, if any. 79 | .TP 80 | .B Mod1\-b 81 | Toggles bar on and off. 82 | .TP 83 | .B Mod1\-t 84 | Sets tiled layout. 85 | .TP 86 | .B Mod1\-f 87 | Sets floating layout. 88 | .TP 89 | .B Mod1\-m 90 | Sets monocle layout. 91 | .TP 92 | .B Mod1\-space 93 | Toggles between current and previous layout. 94 | .TP 95 | .B Mod1\-j 96 | Focus next window. 97 | .TP 98 | .B Mod1\-k 99 | Focus previous window. 100 | .TP 101 | .B Mod1\-i 102 | Increase number of windows in master area. 103 | .TP 104 | .B Mod1\-d 105 | Decrease number of windows in master area. 106 | .TP 107 | .B Mod1\-l 108 | Increase master area size. 109 | .TP 110 | .B Mod1\-h 111 | Decrease master area size. 112 | .TP 113 | .B Mod1\-Return 114 | Zooms/cycles focused window to/from master area (tiled layouts only). 115 | .TP 116 | .B Mod1\-Shift\-c 117 | Close focused window. 118 | .TP 119 | .B Mod1\-Shift\-space 120 | Toggle focused window between tiled and floating state. 121 | .TP 122 | .B Mod1\-Tab 123 | Toggles to the previously selected tags. 124 | .TP 125 | .B Mod1\-Shift\-[1..n] 126 | Apply nth tag to focused window. 127 | .TP 128 | .B Mod1\-Shift\-0 129 | Apply all tags to focused window. 130 | .TP 131 | .B Mod1\-Control\-Shift\-[1..n] 132 | Add/remove nth tag to/from focused window. 133 | .TP 134 | .B Mod1\-[1..n] 135 | View all windows with nth tag. 136 | .TP 137 | .B Mod1\-0 138 | View all windows with any tag. 139 | .TP 140 | .B Mod1\-Control\-[1..n] 141 | Add/remove all windows with nth tag to/from the view. 142 | .TP 143 | .B Mod1\-Shift\-q 144 | Quit dwm. 145 | .SS Mouse commands 146 | .TP 147 | .B Mod1\-Button1 148 | Move focused window while dragging. Tiled windows will be toggled to the floating state. 149 | .TP 150 | .B Mod1\-Button2 151 | Toggles focused window between floating and tiled state. 152 | .TP 153 | .B Mod1\-Button3 154 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. 155 | .SH CUSTOMIZATION 156 | dwm is customized by creating a custom config.h and (re)compiling the source 157 | code. This keeps it fast, secure and simple. 158 | .SH SEE ALSO 159 | .BR dmenu (1), 160 | .BR st (1) 161 | .SH ISSUES 162 | Java applications which use the XToolkit/XAWT backend may draw grey windows 163 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early 164 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds 165 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the 166 | environment variable 167 | .BR AWT_TOOLKIT=MToolkit 168 | (to use the older Motif backend instead) or running 169 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D 170 | or 171 | .B wmname LG3D 172 | (to pretend that a non-reparenting window manager is running that the 173 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable 174 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . 175 | .SH BUGS 176 | Send all bug reports with a patch to hackers@suckless.org. 177 | -------------------------------------------------------------------------------- /dwm/dwm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mespyr/dotfiles/f75874642649d2b886a5e3a66fc9ed8fb4714941/dwm/dwm.png -------------------------------------------------------------------------------- /dwm/patches/dwm-activetagindicatorbar-6.2.diff: -------------------------------------------------------------------------------- 1 | diff -up dwm-a/dwm.c dwm-b/dwm.c 2 | --- dwm-a/dwm.c 2019-02-02 06:55:28.000000000 -0600 3 | +++ dwm-b/dwm.c 2019-02-23 21:43:13.359179100 -0600 4 | @@ -719,9 +719,10 @@ drawbar(Monitor *m) 5 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); 6 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 7 | if (occ & 1 << i) 8 | - drw_rect(drw, x + boxs, boxs, boxw, boxw, 9 | - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 10 | - urg & 1 << i); 11 | + drw_rect(drw, x + boxw, 0, w - ( 2 * boxw + 1), boxw, 12 | + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 13 | + urg & 1 << i); 14 | + 15 | x += w; 16 | } 17 | w = blw = TEXTW(m->ltsymbol); 18 | -------------------------------------------------------------------------------- /dwm/patches/dwm-alttagsdecoration-2020010304-cb3f58a.diff: -------------------------------------------------------------------------------- 1 | diff --git a/config.def.h b/config.def.h 2 | index 1c0b587..d4b11fc 100644 3 | --- a/config.def.h 4 | +++ b/config.def.h 5 | @@ -20,6 +20,7 @@ static const char *colors[][3] = { 6 | 7 | /* tagging */ 8 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 9 | +static const char *alttags[] = { "<01>", "<02>", "<03>", "<04>", "<05>" }; 10 | 11 | static const Rule rules[] = { 12 | /* xprop(1): 13 | diff --git a/dwm.c b/dwm.c 14 | index 4465af1..a394159 100644 15 | --- a/dwm.c 16 | +++ b/dwm.c 17 | @@ -416,7 +416,7 @@ attachstack(Client *c) 18 | void 19 | buttonpress(XEvent *e) 20 | { 21 | - unsigned int i, x, click; 22 | + unsigned int i, x, click, occ; 23 | Arg arg = {0}; 24 | Client *c; 25 | Monitor *m; 26 | @@ -430,9 +430,13 @@ buttonpress(XEvent *e) 27 | focus(NULL); 28 | } 29 | if (ev->window == selmon->barwin) { 30 | - i = x = 0; 31 | + i = x = occ = 0; 32 | + /* Bitmask of occupied tags */ 33 | + for (c = m->clients; c; c = c->next) 34 | + occ |= c->tags; 35 | + 36 | do 37 | - x += TEXTW(tags[i]); 38 | + x += TEXTW(occ & 1 << i ? alttags[i] : tags[i]); 39 | while (ev->x >= x && ++i < LENGTH(tags)); 40 | if (i < LENGTH(tags)) { 41 | click = ClkTagBar; 42 | @@ -699,6 +703,7 @@ drawbar(Monitor *m) 43 | int boxs = drw->fonts->h / 9; 44 | int boxw = drw->fonts->h / 6 + 2; 45 | unsigned int i, occ = 0, urg = 0; 46 | + const char *tagtext; 47 | Client *c; 48 | 49 | /* draw status first so it can be overdrawn by tags later */ 50 | @@ -715,13 +720,10 @@ drawbar(Monitor *m) 51 | } 52 | x = 0; 53 | for (i = 0; i < LENGTH(tags); i++) { 54 | - w = TEXTW(tags[i]); 55 | - drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); 56 | - drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); 57 | - if (occ & 1 << i) 58 | - drw_rect(drw, x + boxs, boxs, boxw, boxw, 59 | - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, 60 | - urg & 1 << i); 61 | + tagtext = occ & 1 << i ? alttags[i] : tags[i]; 62 | + w = TEXTW(tagtext); 63 | + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); 64 | + drw_text(drw, x, 0, w, bh, lrpad / 2, tagtext, urg & 1 << i); 65 | x += w; 66 | } 67 | w = blw = TEXTW(m->ltsymbol); 68 | -------------------------------------------------------------------------------- /dwm/patches/dwm-cool-autostart-6.2.diff: -------------------------------------------------------------------------------- 1 | diff --git a/config.def.h b/config.def.h 2 | index 1c0b587..ed056a4 100644 3 | --- a/config.def.h 4 | +++ b/config.def.h 5 | @@ -18,6 +18,11 @@ static const char *colors[][3] = { 6 | [SchemeSel] = { col_gray4, col_cyan, col_cyan }, 7 | }; 8 | 9 | +static const char *const autostart[] = { 10 | + "st", NULL, 11 | + NULL /* terminate */ 12 | +}; 13 | + 14 | /* tagging */ 15 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 16 | 17 | diff --git a/dwm.c b/dwm.c 18 | index 9fd0286..1facd56 100644 19 | --- a/dwm.c 20 | +++ b/dwm.c 21 | @@ -234,6 +234,7 @@ static int xerror(Display *dpy, XErrorEvent *ee); 22 | static int xerrordummy(Display *dpy, XErrorEvent *ee); 23 | static int xerrorstart(Display *dpy, XErrorEvent *ee); 24 | static void zoom(const Arg *arg); 25 | +static void autostart_exec(void); 26 | 27 | /* variables */ 28 | static const char broken[] = "broken"; 29 | @@ -275,6 +276,34 @@ static Window root, wmcheckwin; 30 | /* compile-time check if all tags fit into an unsigned int bit array. */ 31 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; 32 | 33 | +/* dwm will keep pid's of processes from autostart array and kill them at quit */ 34 | +static pid_t *autostart_pids; 35 | +static size_t autostart_len; 36 | + 37 | +/* execute command from autostart array */ 38 | +static void 39 | +autostart_exec() { 40 | + const char *const *p; 41 | + size_t i = 0; 42 | + 43 | + /* count entries */ 44 | + for (p = autostart; *p; autostart_len++, p++) 45 | + while (*++p); 46 | + 47 | + autostart_pids = malloc(autostart_len * sizeof(pid_t)); 48 | + for (p = autostart; *p; i++, p++) { 49 | + if ((autostart_pids[i] = fork()) == 0) { 50 | + setsid(); 51 | + execvp(*p, (char *const *)p); 52 | + fprintf(stderr, "dwm: execvp %s\n", *p); 53 | + perror(" failed"); 54 | + _exit(EXIT_FAILURE); 55 | + } 56 | + /* skip arguments */ 57 | + while (*++p); 58 | + } 59 | +} 60 | + 61 | /* function implementations */ 62 | void 63 | applyrules(Client *c) 64 | @@ -1249,6 +1278,16 @@ propertynotify(XEvent *e) 65 | void 66 | quit(const Arg *arg) 67 | { 68 | + size_t i; 69 | + 70 | + /* kill child processes */ 71 | + for (i = 0; i < autostart_len; i++) { 72 | + if (0 < autostart_pids[i]) { 73 | + kill(autostart_pids[i], SIGTERM); 74 | + waitpid(autostart_pids[i], NULL, 0); 75 | + } 76 | + } 77 | + 78 | running = 0; 79 | } 80 | 81 | @@ -1632,9 +1671,25 @@ showhide(Client *c) 82 | void 83 | sigchld(int unused) 84 | { 85 | + pid_t pid; 86 | + 87 | if (signal(SIGCHLD, sigchld) == SIG_ERR) 88 | die("can't install SIGCHLD handler:"); 89 | - while (0 < waitpid(-1, NULL, WNOHANG)); 90 | + while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { 91 | + pid_t *p, *lim; 92 | + 93 | + if (!(p = autostart_pids)) 94 | + continue; 95 | + lim = &p[autostart_len]; 96 | + 97 | + for (; p < lim; p++) { 98 | + if (*p == pid) { 99 | + *p = -1; 100 | + break; 101 | + } 102 | + } 103 | + 104 | + } 105 | } 106 | 107 | void 108 | @@ -2139,6 +2194,7 @@ main(int argc, char *argv[]) 109 | if (!(dpy = XOpenDisplay(NULL))) 110 | die("dwm: cannot open display"); 111 | checkotherwm(); 112 | + autostart_exec(); 113 | setup(); 114 | #ifdef __OpenBSD__ 115 | if (pledge("stdio rpath proc exec", NULL) == -1) 116 | 117 | -------------------------------------------------------------------------------- /dwm/patches/dwm-focusonclick-20200110-61bb8b2.diff: -------------------------------------------------------------------------------- 1 | From 7ac0b812540e21b470f2f6947c6cc1e30bf24b42 Mon Sep 17 00:00:00 2001 2 | From: iofq 3 | Date: Sun, 10 Jan 2021 22:43:16 -0600 4 | Subject: [PATCH] tweak fixes floating window mouse controls 5 | 6 | --- 7 | config.def.h | 1 + 8 | dwm.c | 47 ++++------------------------------------------- 9 | 2 files changed, 5 insertions(+), 43 deletions(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 1c0b587..4f2c946 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -5,6 +5,7 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ 16 | static const unsigned int snap = 32; /* snap pixel */ 17 | static const int showbar = 1; /* 0 means no bar */ 18 | static const int topbar = 1; /* 0 means bottom bar */ 19 | +static const int focusonwheel = 0; 20 | static const char *fonts[] = { "monospace:size=10" }; 21 | static const char dmenufont[] = "monospace:size=10"; 22 | static const char col_gray1[] = "#222222"; 23 | diff --git a/dwm.c b/dwm.c 24 | index 664c527..de3e883 100644 25 | --- a/dwm.c 26 | +++ b/dwm.c 27 | @@ -163,7 +163,6 @@ static void detachstack(Client *c); 28 | static Monitor *dirtomon(int dir); 29 | static void drawbar(Monitor *m); 30 | static void drawbars(void); 31 | -static void enternotify(XEvent *e); 32 | static void expose(XEvent *e); 33 | static void focus(Client *c); 34 | static void focusin(XEvent *e); 35 | @@ -182,7 +181,6 @@ static void manage(Window w, XWindowAttributes *wa); 36 | static void mappingnotify(XEvent *e); 37 | static void maprequest(XEvent *e); 38 | static void monocle(Monitor *m); 39 | -static void motionnotify(XEvent *e); 40 | static void movemouse(const Arg *arg); 41 | static Client *nexttiled(Client *c); 42 | static void pop(Client *); 43 | @@ -250,13 +248,11 @@ static void (*handler[LASTEvent]) (XEvent *) = { 44 | [ConfigureRequest] = configurerequest, 45 | [ConfigureNotify] = configurenotify, 46 | [DestroyNotify] = destroynotify, 47 | - [EnterNotify] = enternotify, 48 | [Expose] = expose, 49 | [FocusIn] = focusin, 50 | [KeyPress] = keypress, 51 | [MappingNotify] = mappingnotify, 52 | [MapRequest] = maprequest, 53 | - [MotionNotify] = motionnotify, 54 | [PropertyNotify] = propertynotify, 55 | [UnmapNotify] = unmapnotify 56 | }; 57 | @@ -425,7 +421,8 @@ buttonpress(XEvent *e) 58 | 59 | click = ClkRootWin; 60 | /* focus monitor if necessary */ 61 | - if ((m = wintomon(ev->window)) && m != selmon) { 62 | + if ((m = wintomon(ev->window)) && m != selmon 63 | + && (focusonwheel || (ev->button != Button4 && ev->button != Button5))) { 64 | unfocus(selmon->sel, 1); 65 | selmon = m; 66 | focus(NULL); 67 | @@ -445,8 +442,8 @@ buttonpress(XEvent *e) 68 | else 69 | click = ClkWinTitle; 70 | } else if ((c = wintoclient(ev->window))) { 71 | - focus(c); 72 | - restack(selmon); 73 | + if (focusonwheel || (ev->button != Button4 && ev->button != Button5)) 74 | + focus(c); 75 | XAllowEvents(dpy, ReplayPointer, CurrentTime); 76 | click = ClkClientWin; 77 | } 78 | @@ -752,25 +749,6 @@ drawbars(void) 79 | drawbar(m); 80 | } 81 | 82 | -void 83 | -enternotify(XEvent *e) 84 | -{ 85 | - Client *c; 86 | - Monitor *m; 87 | - XCrossingEvent *ev = &e->xcrossing; 88 | - 89 | - if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) 90 | - return; 91 | - c = wintoclient(ev->window); 92 | - m = c ? c->mon : wintomon(ev->window); 93 | - if (m != selmon) { 94 | - unfocus(selmon->sel, 1); 95 | - selmon = m; 96 | - } else if (!c || c == selmon->sel) 97 | - return; 98 | - focus(c); 99 | -} 100 | - 101 | void 102 | expose(XEvent *e) 103 | { 104 | @@ -1116,23 +1094,6 @@ monocle(Monitor *m) 105 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 106 | } 107 | 108 | -void 109 | -motionnotify(XEvent *e) 110 | -{ 111 | - static Monitor *mon = NULL; 112 | - Monitor *m; 113 | - XMotionEvent *ev = &e->xmotion; 114 | - 115 | - if (ev->window != root) 116 | - return; 117 | - if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { 118 | - unfocus(selmon->sel, 1); 119 | - selmon = m; 120 | - focus(NULL); 121 | - } 122 | - mon = m; 123 | -} 124 | - 125 | void 126 | movemouse(const Arg *arg) 127 | { 128 | -- 129 | 2.30.0 130 | 131 | -------------------------------------------------------------------------------- /dwm/patches/dwm-moveresize-6.2.diff: -------------------------------------------------------------------------------- 1 | From 0ac50d43c5a48de34a53db8240143e4fb39239d3 Mon Sep 17 00:00:00 2001 2 | From: bakkeby 3 | Date: Fri, 22 May 2020 13:51:06 +0200 4 | Subject: [PATCH] The moveresize patch allows floating windows to be resized 5 | and moved using keyboard shortcuts. 6 | 7 | This example keybinding reduces the y position with 25 pixels. 8 | 9 | { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, 10 | 11 | Use capital letters to specify absolute size and position should you need it. 12 | 13 | { MODKEY, XK_Up, moveresize, {.v = "0x 0y 500W 300H" } }, 14 | 15 | The above example would set the size of the client to 300x500 pixels, but leave the position as-is. 16 | 17 | Refer to: 18 | https://dwm.suckless.org/patches/moveresize/ 19 | --- 20 | config.def.h | 8 +++++++ 21 | dwm.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 22 | 2 files changed, 74 insertions(+) 23 | 24 | diff --git a/config.def.h b/config.def.h 25 | index 1c0b587..ff863c9 100644 26 | --- a/config.def.h 27 | +++ b/config.def.h 28 | @@ -78,6 +78,14 @@ static Key keys[] = { 29 | { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, 30 | { MODKEY, XK_space, setlayout, {0} }, 31 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, 32 | + { MODKEY, XK_Down, moveresize, {.v = "0x 25y 0w 0h" } }, 33 | + { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, 34 | + { MODKEY, XK_Right, moveresize, {.v = "25x 0y 0w 0h" } }, 35 | + { MODKEY, XK_Left, moveresize, {.v = "-25x 0y 0w 0h" } }, 36 | + { MODKEY|ShiftMask, XK_Down, moveresize, {.v = "0x 0y 0w 25h" } }, 37 | + { MODKEY|ShiftMask, XK_Up, moveresize, {.v = "0x 0y 0w -25h" } }, 38 | + { MODKEY|ShiftMask, XK_Right, moveresize, {.v = "0x 0y 25w 0h" } }, 39 | + { MODKEY|ShiftMask, XK_Left, moveresize, {.v = "0x 0y -25w 0h" } }, 40 | { MODKEY, XK_0, view, {.ui = ~0 } }, 41 | { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, 42 | { MODKEY, XK_comma, focusmon, {.i = -1 } }, 43 | diff --git a/dwm.c b/dwm.c 44 | index 4465af1..89483c1 100644 45 | --- a/dwm.c 46 | +++ b/dwm.c 47 | @@ -182,6 +182,7 @@ static void mappingnotify(XEvent *e); 48 | static void maprequest(XEvent *e); 49 | static void monocle(Monitor *m); 50 | static void motionnotify(XEvent *e); 51 | +static void moveresize(const Arg *arg); 52 | static void movemouse(const Arg *arg); 53 | static Client *nexttiled(Client *c); 54 | static void pop(Client *); 55 | @@ -1192,6 +1193,71 @@ movemouse(const Arg *arg) 56 | } 57 | } 58 | 59 | +void 60 | +moveresize(const Arg *arg) { 61 | + /* only floating windows can be moved */ 62 | + Client *c; 63 | + c = selmon->sel; 64 | + int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; 65 | + char xAbs, yAbs, wAbs, hAbs; 66 | + int msx, msy, dx, dy, nmx, nmy; 67 | + unsigned int dui; 68 | + Window dummy; 69 | + 70 | + if (!c || !arg) 71 | + return; 72 | + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) 73 | + return; 74 | + if (sscanf((char *)arg->v, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) 75 | + return; 76 | + 77 | + /* compute new window position; prevent window from be positioned outside the current monitor */ 78 | + nw = c->w + w; 79 | + if (wAbs == 'W') 80 | + nw = w < selmon->mw - 2 * c->bw ? w : selmon->mw - 2 * c->bw; 81 | + 82 | + nh = c->h + h; 83 | + if (hAbs == 'H') 84 | + nh = h < selmon->mh - 2 * c->bw ? h : selmon->mh - 2 * c->bw; 85 | + 86 | + nx = c->x + x; 87 | + if (xAbs == 'X') { 88 | + if (x < selmon->mx) 89 | + nx = selmon->mx; 90 | + else if (x > selmon->mx + selmon->mw) 91 | + nx = selmon->mx + selmon->mw - nw - 2 * c->bw; 92 | + else 93 | + nx = x; 94 | + } 95 | + 96 | + ny = c->y + y; 97 | + if (yAbs == 'Y') { 98 | + if (y < selmon->my) 99 | + ny = selmon->my; 100 | + else if (y > selmon->my + selmon->mh) 101 | + ny = selmon->my + selmon->mh - nh - 2 * c->bw; 102 | + else 103 | + ny = y; 104 | + } 105 | + 106 | + ox = c->x; 107 | + oy = c->y; 108 | + ow = c->w; 109 | + oh = c->h; 110 | + 111 | + XRaiseWindow(dpy, c->win); 112 | + Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &msx, &msy, &dx, &dy, &dui); 113 | + resize(c, nx, ny, nw, nh, True); 114 | + 115 | + /* move cursor along with the window to avoid problems caused by the sloppy focus */ 116 | + if (xqp && ox <= msx && (ox + ow) >= msx && oy <= msy && (oy + oh) >= msy) 117 | + { 118 | + nmx = c->x - ox + c->w - ow; 119 | + nmy = c->y - oy + c->h - oh; 120 | + XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); 121 | + } 122 | +} 123 | + 124 | Client * 125 | nexttiled(Client *c) 126 | { 127 | -- 128 | 2.19.1 129 | 130 | -------------------------------------------------------------------------------- /dwm/patches/dwm-smartborders-6.2.diff: -------------------------------------------------------------------------------- 1 | diff --git a/dwm.c b/dwm.c 2 | index 4465af1..3c94e4b 100644 3 | --- a/dwm.c 4 | +++ b/dwm.c 5 | @@ -143,7 +143,7 @@ typedef struct { 6 | 7 | /* function declarations */ 8 | static void applyrules(Client *c); 9 | -static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 10 | +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int *bw, int interact); 11 | static void arrange(Monitor *m); 12 | static void arrangemon(Monitor *m); 13 | static void attach(Client *c); 14 | @@ -188,8 +188,8 @@ static void pop(Client *); 15 | static void propertynotify(XEvent *e); 16 | static void quit(const Arg *arg); 17 | static Monitor *recttomon(int x, int y, int w, int h); 18 | -static void resize(Client *c, int x, int y, int w, int h, int interact); 19 | -static void resizeclient(Client *c, int x, int y, int w, int h); 20 | +static void resize(Client *c, int x, int y, int w, int h, int bw, int interact); 21 | +static void resizeclient(Client *c, int x, int y, int w, int h, int bw); 22 | static void resizemouse(const Arg *arg); 23 | static void restack(Monitor *m); 24 | static void run(void); 25 | @@ -312,7 +312,7 @@ applyrules(Client *c) 26 | } 27 | 28 | int 29 | -applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 30 | +applysizehints(Client *c, int *x, int *y, int *w, int *h, int *bw, int interact) 31 | { 32 | int baseismin; 33 | Monitor *m = c->mon; 34 | @@ -325,18 +325,18 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 35 | *x = sw - WIDTH(c); 36 | if (*y > sh) 37 | *y = sh - HEIGHT(c); 38 | - if (*x + *w + 2 * c->bw < 0) 39 | + if (*x + *w + 2 * *bw < 0) 40 | *x = 0; 41 | - if (*y + *h + 2 * c->bw < 0) 42 | + if (*y + *h + 2 * *bw < 0) 43 | *y = 0; 44 | } else { 45 | if (*x >= m->wx + m->ww) 46 | *x = m->wx + m->ww - WIDTH(c); 47 | if (*y >= m->wy + m->wh) 48 | *y = m->wy + m->wh - HEIGHT(c); 49 | - if (*x + *w + 2 * c->bw <= m->wx) 50 | + if (*x + *w + 2 * *bw <= m->wx) 51 | *x = m->wx; 52 | - if (*y + *h + 2 * c->bw <= m->wy) 53 | + if (*y + *h + 2 * *bw <= m->wy) 54 | *y = m->wy; 55 | } 56 | if (*h < bh) 57 | @@ -374,7 +374,7 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) 58 | if (c->maxh) 59 | *h = MIN(*h, c->maxh); 60 | } 61 | - return *x != c->x || *y != c->y || *w != c->w || *h != c->h; 62 | + return *x != c->x || *y != c->y || *w != c->w || *h != c->h || *bw != c->bw; 63 | } 64 | 65 | void 66 | @@ -394,9 +394,16 @@ arrange(Monitor *m) 67 | void 68 | arrangemon(Monitor *m) 69 | { 70 | + Client *c; 71 | + 72 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); 73 | if (m->lt[m->sellt]->arrange) 74 | m->lt[m->sellt]->arrange(m); 75 | + else 76 | + /* <>< case; rather than providing an arrange function and upsetting other logic that tests for its presence, simply add borders here */ 77 | + for (c = selmon->clients; c; c = c->next) 78 | + if (ISVISIBLE(c) && c->bw == 0) 79 | + resize(c, c->x, c->y, c->w - 2*borderpx, c->h - 2*borderpx, borderpx, 0); 80 | } 81 | 82 | void 83 | @@ -566,7 +573,7 @@ configurenotify(XEvent *e) 84 | for (m = mons; m; m = m->next) { 85 | for (c = m->clients; c; c = c->next) 86 | if (c->isfullscreen) 87 | - resizeclient(c, m->mx, m->my, m->mw, m->mh); 88 | + resizeclient(c, m->mx, m->my, m->mw, m->mh, 0); 89 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 90 | } 91 | focus(NULL); 92 | @@ -1112,7 +1119,7 @@ monocle(Monitor *m) 93 | if (n > 0) /* override layout symbol */ 94 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 95 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 96 | - resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 97 | + resize(c, m->wx, m->wy, m->ww, m->wh, 0, 0); 98 | } 99 | 100 | void 101 | @@ -1180,7 +1187,7 @@ movemouse(const Arg *arg) 102 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) 103 | togglefloating(NULL); 104 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 105 | - resize(c, nx, ny, c->w, c->h, 1); 106 | + resize(c, nx, ny, c->w, c->h, c->bw, 1); 107 | break; 108 | } 109 | } while (ev.type != ButtonRelease); 110 | @@ -1266,14 +1273,14 @@ recttomon(int x, int y, int w, int h) 111 | } 112 | 113 | void 114 | -resize(Client *c, int x, int y, int w, int h, int interact) 115 | +resize(Client *c, int x, int y, int w, int h, int bw, int interact) 116 | { 117 | - if (applysizehints(c, &x, &y, &w, &h, interact)) 118 | - resizeclient(c, x, y, w, h); 119 | + if (applysizehints(c, &x, &y, &w, &h, &bw, interact)) 120 | + resizeclient(c, x, y, w, h, bw); 121 | } 122 | 123 | void 124 | -resizeclient(Client *c, int x, int y, int w, int h) 125 | +resizeclient(Client *c, int x, int y, int w, int h, int bw) 126 | { 127 | XWindowChanges wc; 128 | 129 | @@ -1281,7 +1288,7 @@ resizeclient(Client *c, int x, int y, int w, int h) 130 | c->oldy = c->y; c->y = wc.y = y; 131 | c->oldw = c->w; c->w = wc.width = w; 132 | c->oldh = c->h; c->h = wc.height = h; 133 | - wc.border_width = c->bw; 134 | + c->oldbw = c->bw; c->bw = wc.border_width = bw; 135 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 136 | configure(c); 137 | XSync(dpy, False); 138 | @@ -1330,7 +1337,7 @@ resizemouse(const Arg *arg) 139 | togglefloating(NULL); 140 | } 141 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) 142 | - resize(c, c->x, c->y, nw, nh, 1); 143 | + resize(c, c->x, c->y, nw, nh, c->bw, 1); 144 | break; 145 | } 146 | } while (ev.type != ButtonRelease); 147 | @@ -1477,22 +1484,20 @@ setfullscreen(Client *c, int fullscreen) 148 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); 149 | c->isfullscreen = 1; 150 | c->oldstate = c->isfloating; 151 | - c->oldbw = c->bw; 152 | - c->bw = 0; 153 | c->isfloating = 1; 154 | - resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); 155 | + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh, 0); 156 | XRaiseWindow(dpy, c->win); 157 | } else if (!fullscreen && c->isfullscreen){ 158 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, 159 | PropModeReplace, (unsigned char*)0, 0); 160 | c->isfullscreen = 0; 161 | c->isfloating = c->oldstate; 162 | - c->bw = c->oldbw; 163 | c->x = c->oldx; 164 | c->y = c->oldy; 165 | c->w = c->oldw; 166 | c->h = c->oldh; 167 | - resizeclient(c, c->x, c->y, c->w, c->h); 168 | + c->bw = c->oldbw; 169 | + resizeclient(c, c->x, c->y, c->w, c->h, c->bw); 170 | arrange(c->mon); 171 | } 172 | } 173 | @@ -1619,7 +1624,7 @@ showhide(Client *c) 174 | /* show clients top down */ 175 | XMoveWindow(dpy, c->win, c->x, c->y); 176 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 177 | - resize(c, c->x, c->y, c->w, c->h, 0); 178 | + resize(c, c->x, c->y, c->w, c->h, c->bw, 0); 179 | showhide(c->snext); 180 | } else { 181 | /* hide clients bottom up */ 182 | @@ -1673,13 +1678,17 @@ tagmon(const Arg *arg) 183 | void 184 | tile(Monitor *m) 185 | { 186 | - unsigned int i, n, h, mw, my, ty; 187 | + unsigned int i, n, h, mw, my, ty, bw; 188 | Client *c; 189 | 190 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 191 | if (n == 0) 192 | return; 193 | 194 | + if (n == 1) 195 | + bw = 0; 196 | + else 197 | + bw = borderpx; 198 | if (n > m->nmaster) 199 | mw = m->nmaster ? m->ww * m->mfact : 0; 200 | else 201 | @@ -1687,11 +1696,11 @@ tile(Monitor *m) 202 | for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 203 | if (i < m->nmaster) { 204 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 205 | - resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 206 | + resize(c, m->wx, m->wy + my, mw - 2*bw, h - 2*bw, bw, 0); 207 | my += HEIGHT(c); 208 | } else { 209 | h = (m->wh - ty) / (n - i); 210 | - resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); 211 | + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - 2*bw, h - 2*bw, bw, 0); 212 | ty += HEIGHT(c); 213 | } 214 | } 215 | @@ -1715,7 +1724,9 @@ togglefloating(const Arg *arg) 216 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; 217 | if (selmon->sel->isfloating) 218 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, 219 | - selmon->sel->w, selmon->sel->h, 0); 220 | + selmon->sel->w - 2 * (borderpx - selmon->sel->bw), 221 | + selmon->sel->h - 2 * (borderpx - selmon->sel->bw), 222 | + borderpx, 0); 223 | arrange(selmon); 224 | } 225 | 226 | -------------------------------------------------------------------------------- /dwm/patches/dwm-status2d-6.3.diff: -------------------------------------------------------------------------------- 1 | diff --git a/dwm.c b/dwm.c 2 | index a96f33c..24b1eeb 100644 3 | --- a/dwm.c 4 | +++ b/dwm.c 5 | @@ -163,6 +163,7 @@ static void detachstack(Client *c); 6 | static Monitor *dirtomon(int dir); 7 | static void drawbar(Monitor *m); 8 | static void drawbars(void); 9 | +static int drawstatusbar(Monitor *m, int bh, char* text); 10 | static void enternotify(XEvent *e); 11 | static void expose(XEvent *e); 12 | static void focus(Client *c); 13 | @@ -237,7 +238,7 @@ static void zoom(const Arg *arg); 14 | 15 | /* variables */ 16 | static const char broken[] = "broken"; 17 | -static char stext[256]; 18 | +static char stext[1024]; 19 | static int screen; 20 | static int sw, sh; /* X display screen geometry width, height */ 21 | static int bh, blw = 0; /* bar geometry */ 22 | @@ -485,7 +486,7 @@ cleanup(void) 23 | cleanupmon(mons); 24 | for (i = 0; i < CurLast; i++) 25 | drw_cur_free(drw, cursor[i]); 26 | - for (i = 0; i < LENGTH(colors); i++) 27 | + for (i = 0; i < LENGTH(colors) + 1; i++) 28 | free(scheme[i]); 29 | XDestroyWindow(dpy, wmcheckwin); 30 | drw_free(drw); 31 | @@ -693,6 +694,114 @@ dirtomon(int dir) 32 | return m; 33 | } 34 | 35 | +int 36 | +drawstatusbar(Monitor *m, int bh, char* stext) { 37 | + int ret, i, w, x, len; 38 | + short isCode = 0; 39 | + char *text; 40 | + char *p; 41 | + 42 | + len = strlen(stext) + 1 ; 43 | + if (!(text = (char*) malloc(sizeof(char)*len))) 44 | + die("malloc"); 45 | + p = text; 46 | + memcpy(text, stext, len); 47 | + 48 | + /* compute width of the status text */ 49 | + w = 0; 50 | + i = -1; 51 | + while (text[++i]) { 52 | + if (text[i] == '^') { 53 | + if (!isCode) { 54 | + isCode = 1; 55 | + text[i] = '\0'; 56 | + w += TEXTW(text) - lrpad; 57 | + text[i] = '^'; 58 | + if (text[++i] == 'f') 59 | + w += atoi(text + ++i); 60 | + } else { 61 | + isCode = 0; 62 | + text = text + i + 1; 63 | + i = -1; 64 | + } 65 | + } 66 | + } 67 | + if (!isCode) 68 | + w += TEXTW(text) - lrpad; 69 | + else 70 | + isCode = 0; 71 | + text = p; 72 | + 73 | + w += 2; /* 1px padding on both sides */ 74 | + ret = x = m->ww - w; 75 | + 76 | + drw_setscheme(drw, scheme[LENGTH(colors)]); 77 | + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 78 | + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 79 | + drw_rect(drw, x, 0, w, bh, 1, 1); 80 | + x++; 81 | + 82 | + /* process status text */ 83 | + i = -1; 84 | + while (text[++i]) { 85 | + if (text[i] == '^' && !isCode) { 86 | + isCode = 1; 87 | + 88 | + text[i] = '\0'; 89 | + w = TEXTW(text) - lrpad; 90 | + drw_text(drw, x, 0, w, bh, 0, text, 0); 91 | + 92 | + x += w; 93 | + 94 | + /* process code */ 95 | + while (text[++i] != '^') { 96 | + if (text[i] == 'c') { 97 | + char buf[8]; 98 | + memcpy(buf, (char*)text+i+1, 7); 99 | + buf[7] = '\0'; 100 | + drw_clr_create(drw, &drw->scheme[ColFg], buf); 101 | + i += 7; 102 | + } else if (text[i] == 'b') { 103 | + char buf[8]; 104 | + memcpy(buf, (char*)text+i+1, 7); 105 | + buf[7] = '\0'; 106 | + drw_clr_create(drw, &drw->scheme[ColBg], buf); 107 | + i += 7; 108 | + } else if (text[i] == 'd') { 109 | + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; 110 | + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; 111 | + } else if (text[i] == 'r') { 112 | + int rx = atoi(text + ++i); 113 | + while (text[++i] != ','); 114 | + int ry = atoi(text + ++i); 115 | + while (text[++i] != ','); 116 | + int rw = atoi(text + ++i); 117 | + while (text[++i] != ','); 118 | + int rh = atoi(text + ++i); 119 | + 120 | + drw_rect(drw, rx + x, ry, rw, rh, 1, 0); 121 | + } else if (text[i] == 'f') { 122 | + x += atoi(text + ++i); 123 | + } 124 | + } 125 | + 126 | + text = text + i + 1; 127 | + i=-1; 128 | + isCode = 0; 129 | + } 130 | + } 131 | + 132 | + if (!isCode) { 133 | + w = TEXTW(text) - lrpad; 134 | + drw_text(drw, x, 0, w, bh, 0, text, 0); 135 | + } 136 | + 137 | + drw_setscheme(drw, scheme[SchemeNorm]); 138 | + free(p); 139 | + 140 | + return ret; 141 | +} 142 | + 143 | void 144 | drawbar(Monitor *m) 145 | { 146 | @@ -707,9 +816,7 @@ drawbar(Monitor *m) 147 | 148 | /* draw status first so it can be overdrawn by tags later */ 149 | if (m == selmon) { /* status is only drawn on selected monitor */ 150 | - drw_setscheme(drw, scheme[SchemeNorm]); 151 | - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 152 | - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 153 | + tw = m->ww - drawstatusbar(m, bh, stext); 154 | } 155 | 156 | for (c = m->clients; c; c = c->next) { 157 | @@ -1571,7 +1678,8 @@ setup(void) 158 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); 159 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); 160 | /* init appearance */ 161 | - scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 162 | + scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); 163 | + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); 164 | for (i = 0; i < LENGTH(colors); i++) 165 | scheme[i] = drw_scm_create(drw, colors[i], 3); 166 | /* init bars */ 167 | -------------------------------------------------------------------------------- /dwm/patches/dwm-statuspadding-20150524-c8e9479.diff: -------------------------------------------------------------------------------- 1 | From 75d5edbe16ee2fc060ff8b05eea17791d6334a59 Mon Sep 17 00:00:00 2001 2 | From: Christopher Drelich 3 | Date: Thu, 24 May 2018 23:24:12 -0400 4 | Subject: [PATCH] Replaces magic numbers in statusbar with configurable 5 | variables. 6 | 7 | horizpadbar for horizontal statusbar padding 8 | vertpadbar for vertical statusbar padding 9 | 10 | StatusText now has both left and right padding, 11 | as well as the vertical padding that all of the statusbar shares. 12 | 13 | Other than the addition of left padding to StatusText, appearance 14 | of the statusbar is identical to pre-patch when using the defaults 15 | in config.def.h 16 | --- 17 | config.def.h | 2 ++ 18 | dwm.c | 8 ++++---- 19 | 2 files changed, 6 insertions(+), 4 deletions(-) 20 | 21 | diff --git a/config.def.h b/config.def.h 22 | index a9ac303..5819399 100644 23 | --- a/config.def.h 24 | +++ b/config.def.h 25 | @@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ 26 | static const unsigned int snap = 32; /* snap pixel */ 27 | static const int showbar = 1; /* 0 means no bar */ 28 | static const int topbar = 1; /* 0 means bottom bar */ 29 | +static const int horizpadbar = 2; /* horizontal padding for statusbar */ 30 | +static const int vertpadbar = 0; /* vertical padding for statusbar */ 31 | static const char *fonts[] = { "monospace:size=10" }; 32 | static const char dmenufont[] = "monospace:size=10"; 33 | static const char col_gray1[] = "#222222"; 34 | diff --git a/dwm.c b/dwm.c 35 | index bb95e26..7b9ed42 100644 36 | --- a/dwm.c 37 | +++ b/dwm.c 38 | @@ -704,8 +704,8 @@ drawbar(Monitor *m) 39 | /* draw status first so it can be overdrawn by tags later */ 40 | if (m == selmon) { /* status is only drawn on selected monitor */ 41 | drw_setscheme(drw, scheme[SchemeNorm]); 42 | - sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 43 | - drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); 44 | + sw = TEXTW(stext); 45 | + drw_text(drw, m->ww - sw, 0, sw, bh, lrpad / 2, stext, 0); 46 | } 47 | 48 | for (c = m->clients; c; c = c->next) { 49 | @@ -1544,8 +1544,8 @@ setup(void) 50 | drw = drw_create(dpy, screen, root, sw, sh); 51 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 52 | die("no fonts could be loaded."); 53 | - lrpad = drw->fonts->h; 54 | - bh = drw->fonts->h + 2; 55 | + lrpad = drw->fonts->h + horizpadbar; 56 | + bh = drw->fonts->h + vertpadbar; 57 | updategeom(); 58 | /* init atoms */ 59 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); 60 | -- 61 | 2.7.4 62 | 63 | -------------------------------------------------------------------------------- /dwm/patches/dwm-uselessgap-20211119-58414bee958f2.diff: -------------------------------------------------------------------------------- 1 | From 58414bee958f2e7ed91d6fe31f503ec4a406981b Mon Sep 17 00:00:00 2001 2 | From: cirala 3 | Date: Fri, 19 Nov 2021 18:14:07 +0100 4 | Subject: [PATCH] Fix for dwm-uselessgap 5 | Previous versions of the patch doubles the 6 | gap between the master and slave stacks. 7 | 8 | --- 9 | config.def.h | 3 ++- 10 | dwm.c | 38 +++++++++++++++++++++++++++++++------- 11 | 2 files changed, 33 insertions(+), 8 deletions(-) 12 | 13 | diff --git a/config.def.h b/config.def.h 14 | index a2ac963..17a205f 100644 15 | --- a/config.def.h 16 | +++ b/config.def.h 17 | @@ -2,6 +2,7 @@ 18 | 19 | /* appearance */ 20 | static const unsigned int borderpx = 1; /* border pixel of windows */ 21 | +static const unsigned int gappx = 6; /* gaps between windows */ 22 | static const unsigned int snap = 32; /* snap pixel */ 23 | static const int showbar = 1; /* 0 means no bar */ 24 | static const int topbar = 1; /* 0 means bottom bar */ 25 | @@ -34,7 +35,7 @@ static const Rule rules[] = { 26 | /* layout(s) */ 27 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ 28 | static const int nmaster = 1; /* number of clients in master area */ 29 | -static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ 30 | +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ 31 | static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ 32 | 33 | static const Layout layouts[] = { 34 | diff --git a/dwm.c b/dwm.c 35 | index 5e4d494..b626e89 100644 36 | --- a/dwm.c 37 | +++ b/dwm.c 38 | @@ -52,8 +52,8 @@ 39 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 40 | #define LENGTH(X) (sizeof X / sizeof X[0]) 41 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 42 | -#define WIDTH(X) ((X)->w + 2 * (X)->bw) 43 | -#define HEIGHT(X) ((X)->h + 2 * (X)->bw) 44 | +#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx) 45 | +#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx) 46 | #define TAGMASK ((1 << LENGTH(tags)) - 1) 47 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 48 | 49 | @@ -1277,12 +1277,36 @@ void 50 | resizeclient(Client *c, int x, int y, int w, int h) 51 | { 52 | XWindowChanges wc; 53 | + unsigned int n; 54 | + unsigned int gapoffset; 55 | + unsigned int gapincr; 56 | + Client *nbc; 57 | 58 | - c->oldx = c->x; c->x = wc.x = x; 59 | - c->oldy = c->y; c->y = wc.y = y; 60 | - c->oldw = c->w; c->w = wc.width = w; 61 | - c->oldh = c->h; c->h = wc.height = h; 62 | wc.border_width = c->bw; 63 | + 64 | + /* Get number of clients for the client's monitor */ 65 | + for (n = 0, nbc = nexttiled(c->mon->clients); nbc; nbc = nexttiled(nbc->next), n++); 66 | + 67 | + /* Do nothing if layout is floating */ 68 | + if (c->isfloating || c->mon->lt[c->mon->sellt]->arrange == NULL) { 69 | + gapincr = gapoffset = 0; 70 | + } else { 71 | + /* Remove border and gap if layout is monocle or only one client */ 72 | + if (c->mon->lt[c->mon->sellt]->arrange == monocle || n == 1) { 73 | + gapoffset = 0; 74 | + gapincr = -2 * borderpx; 75 | + wc.border_width = 0; 76 | + } else { 77 | + gapoffset = gappx; 78 | + gapincr = 2 * gappx; 79 | + } 80 | + } 81 | + 82 | + c->oldx = c->x; c->x = wc.x = x + gapoffset; 83 | + c->oldy = c->y; c->y = wc.y = y + gapoffset; 84 | + c->oldw = c->w; c->w = wc.width = w - gapincr; 85 | + c->oldh = c->h; c->h = wc.height = h - gapincr; 86 | + 87 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); 88 | configure(c); 89 | XSync(dpy, False); 90 | @@ -1688,7 +1712,7 @@ tile(Monitor *m) 91 | for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 92 | if (i < m->nmaster) { 93 | h = (m->wh - my) / (MIN(n, m->nmaster) - i); 94 | - resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 95 | + resize(c, m->wx, m->wy + my, mw - (2*c->bw) + (n > 1 ? gappx : 0), h - (2*c->bw), 0); 96 | if (my + HEIGHT(c) < m->wh) 97 | my += HEIGHT(c); 98 | } else { 99 | -- 100 | 2.33.1 101 | 102 | -------------------------------------------------------------------------------- /dwm/transient.c: -------------------------------------------------------------------------------- 1 | /* cc transient.c -o transient -lX11 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | Display *d; 10 | Window r, f, t = None; 11 | XSizeHints h; 12 | XEvent e; 13 | 14 | d = XOpenDisplay(NULL); 15 | if (!d) 16 | exit(1); 17 | r = DefaultRootWindow(d); 18 | 19 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); 20 | h.min_width = h.max_width = h.min_height = h.max_height = 400; 21 | h.flags = PMinSize | PMaxSize; 22 | XSetWMNormalHints(d, f, &h); 23 | XStoreName(d, f, "floating"); 24 | XMapWindow(d, f); 25 | 26 | XSelectInput(d, f, ExposureMask); 27 | while (1) { 28 | XNextEvent(d, &e); 29 | 30 | if (t == None) { 31 | sleep(5); 32 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); 33 | XSetTransientForHint(d, t, f); 34 | XStoreName(d, t, "transient"); 35 | XMapWindow(d, t); 36 | XSelectInput(d, t, ExposureMask); 37 | } 38 | } 39 | 40 | XCloseDisplay(d); 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /dwm/util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void 10 | die(const char *fmt, ...) 11 | { 12 | va_list ap; 13 | 14 | va_start(ap, fmt); 15 | vfprintf(stderr, fmt, ap); 16 | va_end(ap); 17 | 18 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 19 | fputc(' ', stderr); 20 | perror(NULL); 21 | } else { 22 | fputc('\n', stderr); 23 | } 24 | 25 | exit(1); 26 | } 27 | 28 | void * 29 | ecalloc(size_t nmemb, size_t size) 30 | { 31 | void *p; 32 | 33 | if (!(p = calloc(nmemb, size))) 34 | die("calloc:"); 35 | return p; 36 | } 37 | -------------------------------------------------------------------------------- /dwm/util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | 7 | void die(const char *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | -------------------------------------------------------------------------------- /dwm_bar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | get_time() { 4 | echo "^c#d0a215^$(date '+%I:%M%p')^d^" 5 | } 6 | 7 | get_ram() { 8 | USED_RAM=$(free -mh --si | awk {'print $3'} | head -n 2 | tail -1) 9 | echo "^c#d0a215^RAM^d^ $USED_RAM" 10 | } 11 | 12 | WIFI_FULL_ICON=' ' 13 | WIFI_MID_ICON=' ' 14 | WIFI_LOW_ICON=' ' 15 | NO_WIFI_ICON='' 16 | get_wifi() { 17 | if grep -q wl* "/proc/net/wireless"; then 18 | # Wifi quality percentage 19 | percentage=$(grep "^\s*w" /proc/net/wireless | awk '{ print "", int($3 * 100 / 70)}'| xargs) 20 | case $percentage in 21 | 0) echo $NO_WIFI_ICON;; 22 | 100|9[0-9]|8[0-9]|7[0-9]) echo "$WIFI_FULL_ICON" ;; 23 | 6[0-9]|5[0-9]|4[0-9]|3[0-9]) echo "$WIFI_MID_ICON" ;; 24 | 2[0-9]|1[0-9]|[0-9]) echo "$WIFI_LOW_ICON" ;; 25 | esac 26 | fi 27 | } 28 | 29 | VOLUME_ON_ICON='^c#d0a215^ ^d^' 30 | VOLUME_MUTED_ICON='^c#d0a215^! ^d^' 31 | 32 | get_volume() { 33 | curStatus=$(pactl get-sink-mute @DEFAULT_SINK@) 34 | volume=$(pactl get-sink-volume @DEFAULT_SINK@ | tail -n 2 | sed -e 's,.* \([0-9][0-9]*\)%.*,\1,' | head -n 1) 35 | if [ "${curStatus}" = 'Mute: yes' ] 36 | then 37 | echo "$VOLUME_MUTED_ICON$volume%" 38 | else 39 | echo "$VOLUME_ON_ICON$volume%" 40 | fi 41 | } 42 | 43 | get_uptime() { 44 | UPTIME=$(echo $(uptime | sed 's/.*up \([^,]*\), .*/\1/')) 45 | echo "^c#d0a215^UPTIME^d^ $UPTIME" 46 | } 47 | 48 | S="^c#ad8301^/^d^" 49 | while true; do 50 | xsetroot -name "$(get_wifi) $S $(get_volume) $S $(get_ram) $S $(get_uptime) $S $(get_time) " && sleep 2 51 | done 52 | -------------------------------------------------------------------------------- /packages.txt: -------------------------------------------------------------------------------- 1 | alacritty 2 | brave-bin 3 | clang 4 | discord 5 | emacs 6 | fasm 7 | feh 8 | minecraft-launcher 9 | mpv 10 | musescore 11 | obs-studio 12 | openshot 13 | orchis-theme 14 | pavucontrol 15 | picom 16 | steam 17 | ttf-iosevka-nerd 18 | wine 19 | -------------------------------------------------------------------------------- /tabbed/LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2009-2011 Enno Boland 4 | © 2011,2015 Connor Lane Smith 5 | © 2012-2015 Christoph Lohmann <20h@r-36.net> 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a 8 | copy of this software and associated documentation files (the "Software"), 9 | to deal in the Software without restriction, including without limitation 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | and/or sell copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /tabbed/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | NAME = tabbed 4 | VERSION = 0.8 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | DOCPREFIX = ${PREFIX}/share/doc/${NAME} 10 | 11 | # use system flags. 12 | TABBED_CFLAGS = -I/usr/X11R6/include -I/usr/include/freetype2 ${CFLAGS} 13 | TABBED_LDFLAGS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft ${LDFLAGS} 14 | TABBED_CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700L 15 | 16 | # OpenBSD (uncomment) 17 | #TABBED_CFLAGS = -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 ${CFLAGS} 18 | 19 | SRC = tabbed.c xembed.c 20 | OBJ = ${SRC:.c=.o} 21 | BIN = ${OBJ:.o=} 22 | MAN1 = ${BIN:=.1} 23 | HDR = arg.h config.def.h 24 | DOC = LICENSE README 25 | 26 | all: ${BIN} 27 | 28 | .c.o: 29 | ${CC} -o $@ -c $< ${TABBED_CFLAGS} ${TABBED_CPPFLAGS} 30 | 31 | ${OBJ}: config.def.h 32 | 33 | .o: 34 | ${CC} -o $@ $< ${TABBED_LDFLAGS} 35 | 36 | clean: 37 | rm -f ${BIN} ${OBJ} "${NAME}-${VERSION}.tar.gz" 38 | 39 | dist: clean 40 | mkdir -p "${NAME}-${VERSION}" 41 | cp -fR Makefile ${MAN1} ${DOC} ${HDR} ${SRC} "${NAME}-${VERSION}" 42 | tar -cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz" 43 | rm -rf ${NAME}-${VERSION} 44 | 45 | install: all 46 | # installing executable files. 47 | mkdir -p "${DESTDIR}${PREFIX}/bin" 48 | cp -f ${BIN} "${DESTDIR}${PREFIX}/bin" 49 | for f in ${BIN}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done 50 | # installing doc files. 51 | mkdir -p "${DESTDIR}${DOCPREFIX}" 52 | cp -f README "${DESTDIR}${DOCPREFIX}" 53 | # installing manual pages for general commands: section 1. 54 | mkdir -p "${DESTDIR}${MANPREFIX}/man1" 55 | for m in ${MAN1}; do sed "s/VERSION/${VERSION}/g" < $$m > "${DESTDIR}${MANPREFIX}/man1/$$m"; done 56 | 57 | uninstall: 58 | # removing executable files. 59 | for f in ${BIN}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done 60 | # removing doc files. 61 | rm -f "${DESTDIR}${DOCPREFIX}/README" 62 | # removing manual pages. 63 | for m in ${MAN1}; do rm -f "${DESTDIR}${MANPREFIX}/man1/$$m"; done 64 | -rmdir "${DESTDIR}${DOCPREFIX}" 65 | 66 | .PHONY: all clean dist install uninstall 67 | -------------------------------------------------------------------------------- /tabbed/README: -------------------------------------------------------------------------------- 1 | tabbed - generic tabbed interface 2 | ================================= 3 | tabbed is a simple tabbed X window container. 4 | 5 | Requirements 6 | ------------ 7 | In order to build tabbed you need the Xlib header files. 8 | 9 | Installation 10 | ------------ 11 | Edit config.mk to match your local setup (tabbed is installed into 12 | the /usr/local namespace by default). 13 | 14 | Afterwards enter the following command to build and install tabbed 15 | (if necessary as root): 16 | 17 | make clean install 18 | 19 | Running tabbed 20 | -------------- 21 | See the man page for details. 22 | 23 | -------------------------------------------------------------------------------- /tabbed/arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copy me if you can. 3 | * by 20h 4 | */ 5 | 6 | #ifndef ARG_H__ 7 | #define ARG_H__ 8 | 9 | extern char *argv0; 10 | 11 | /* use main(int argc, char *argv[]) */ 12 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ 13 | argv[0] && argv[0][0] == '-'\ 14 | && argv[0][1];\ 15 | argc--, argv++) {\ 16 | char argc_;\ 17 | char **argv_;\ 18 | int brk_;\ 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ 20 | argv++;\ 21 | argc--;\ 22 | break;\ 23 | }\ 24 | for (brk_ = 0, argv[0]++, argv_ = argv;\ 25 | argv[0][0] && !brk_;\ 26 | argv[0]++) {\ 27 | if (argv_ != argv)\ 28 | break;\ 29 | argc_ = argv[0][0];\ 30 | switch (argc_) 31 | #define ARGEND }\ 32 | } 33 | 34 | #define ARGC() argc_ 35 | 36 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ 37 | ((x), abort(), (char *)0) :\ 38 | (brk_ = 1, (argv[0][1] != '\0')?\ 39 | (&argv[0][1]) :\ 40 | (argc--, argv++, argv[0]))) 41 | 42 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ 43 | (char *)0 :\ 44 | (brk_ = 1, (argv[0][1] != '\0')?\ 45 | (&argv[0][1]) :\ 46 | (argc--, argv++, argv[0]))) 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /tabbed/config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance */ 4 | static const char font[] = "Iosevka Term NF:size=12"; 5 | static const char* normbgcolor = "#232726"; 6 | static const char* normfgcolor = "#6f6e69"; 7 | static const char* selbgcolor = "#100f0f"; 8 | static const char* selfgcolor = "#ad8301"; 9 | 10 | static const char* urgbgcolor = "#111111"; 11 | static const char* urgfgcolor = "#cc0000"; 12 | 13 | static const char before[] = ""; 14 | static const char after[] = ""; 15 | static const char titletrim[] = "..."; 16 | static const int tabwidth = 100; 17 | static const Bool foreground = True; 18 | static Bool urgentswitch = False; 19 | static const int separator = 4; 20 | static const int barheight = 30; 21 | /* 22 | * Where to place a new tab when it is opened. When npisrelative is True, 23 | * then the current position is changed + newposition. If npisrelative 24 | * is False, then newposition is an absolute position. 25 | */ 26 | static int newposition = 0; 27 | static Bool npisrelative = False; 28 | 29 | #define SETPROP(p) { \ 30 | .v = (char *[]){ "/bin/sh", "-c", \ 31 | "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ 32 | "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ 33 | "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ 34 | "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ 35 | p, winid, NULL \ 36 | } \ 37 | } 38 | 39 | #define MODKEY Mod1Mask 40 | static const Key keys[] = { 41 | /* modifier key function argument */ 42 | { MODKEY, XK_Return, focusonce, { 0 } }, 43 | { MODKEY, XK_Return, spawn, { 0 } }, 44 | 45 | { MODKEY, XK_Tab, rotate, { .i = +1 } }, 46 | 47 | //{ MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, 48 | { MODKEY, XK_1, move, { .i = 0 } }, 49 | { MODKEY, XK_2, move, { .i = 1 } }, 50 | { MODKEY, XK_3, move, { .i = 2 } }, 51 | { MODKEY, XK_4, move, { .i = 3 } }, 52 | { MODKEY, XK_5, move, { .i = 4 } }, 53 | { MODKEY, XK_6, move, { .i = 5 } }, 54 | { MODKEY, XK_7, move, { .i = 6 } }, 55 | { MODKEY, XK_8, move, { .i = 7 } }, 56 | { MODKEY, XK_9, move, { .i = 8 } }, 57 | { MODKEY, XK_0, move, { .i = 9 } }, 58 | 59 | { MODKEY, XK_c, killclient, { 0 } }, 60 | //{ 0, XK_F11, fullscreen, { 0 } }, 61 | }; 62 | -------------------------------------------------------------------------------- /tabbed/patches/tabbed-autohide-20201222-dabf6a2.diff: -------------------------------------------------------------------------------- 1 | diff --git a/tabbed.c b/tabbed.c 2 | index eafe28a..b0b9662 100644 3 | --- a/tabbed.c 4 | +++ b/tabbed.c 5 | @@ -152,7 +152,7 @@ static void (*handler[LASTEvent]) (const XEvent *) = { 6 | [MapRequest] = maprequest, 7 | [PropertyNotify] = propertynotify, 8 | }; 9 | -static int bh, obh, wx, wy, ww, wh; 10 | +static int bh, obh, wx, wy, ww, wh, vbh; 11 | static unsigned int numlockmask; 12 | static Bool running = True, nextfocus, doinitspawn = True, 13 | fillagain = False, closelastclient = False, 14 | @@ -324,7 +324,7 @@ void 15 | drawbar(void) 16 | { 17 | XftColor *col; 18 | - int c, cc, fc, width; 19 | + int c, cc, fc, width, nbh, i; 20 | char *name = NULL; 21 | 22 | if (nclients == 0) { 23 | @@ -332,12 +332,21 @@ drawbar(void) 24 | dc.w = ww; 25 | XFetchName(dpy, win, &name); 26 | drawtext(name ? name : "", dc.norm); 27 | - XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 28 | + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, vbh, 0, 0); 29 | XSync(dpy, False); 30 | 31 | return; 32 | } 33 | 34 | + nbh = nclients > 1 ? vbh : 0; 35 | + if (bh != nbh) { 36 | + bh = nbh; 37 | + for (i = 0; i < nclients; i++) 38 | + XMoveResizeWindow(dpy, clients[i]->win, 0, bh, ww, wh - bh); 39 | + } 40 | + if (bh == 0) 41 | + return; 42 | + 43 | width = ww; 44 | cc = ww / tabwidth; 45 | if (nclients > cc) 46 | @@ -984,7 +993,7 @@ setup(void) 47 | screen = DefaultScreen(dpy); 48 | root = RootWindow(dpy, screen); 49 | initfont(font); 50 | - bh = dc.h = dc.font.height + 2; 51 | + vbh = dc.h = dc.font.height + 2; 52 | 53 | /* init atoms */ 54 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 55 | -------------------------------------------------------------------------------- /tabbed/patches/tabbed-bar-height-0.6.diff: -------------------------------------------------------------------------------- 1 | diff --color -up tabbed-0.6-clean/config.def.h tabbed-0.6-modified/config.def.h 2 | --- tabbed-0.6-clean/config.def.h 2014-01-21 10:22:03.000000000 -0800 3 | +++ tabbed-0.6-modified/config.def.h 2021-03-30 20:23:45.752478278 -0700 4 | @@ -10,7 +10,7 @@ static const char before[] = "<"; 5 | static const char after[] = ">"; 6 | static const int tabwidth = 200; 7 | static const Bool foreground = True; 8 | - 9 | +static const int barHeight = 24; 10 | /* 11 | * Where to place a new tab when it is opened. When npisrelative is True, 12 | * then the current position is changed + newposition. If npisrelative 13 | diff --color -up tabbed-0.6-clean/tabbed.c tabbed-0.6-modified/tabbed.c 14 | --- tabbed-0.6-clean/tabbed.c 2014-01-21 10:22:03.000000000 -0800 15 | +++ tabbed-0.6-modified/tabbed.c 2021-03-30 20:24:23.712477426 -0700 16 | @@ -920,7 +920,7 @@ setup(void) { 17 | screen = DefaultScreen(dpy); 18 | root = RootWindow(dpy, screen); 19 | initfont(font); 20 | - bh = dc.h = dc.font.height + 2; 21 | + bh = dc.h = barHeight; 22 | 23 | /* init atoms */ 24 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 25 | -------------------------------------------------------------------------------- /tabbed/patches/tabbed-cwd-20230128-41e2b8f.diff: -------------------------------------------------------------------------------- 1 | From 50753359eb7a760bcadd54611431c0888867c21c Mon Sep 17 00:00:00 2001 2 | From: Casey Fitzpatrick 3 | Date: Sat, 28 Jan 2023 09:46:53 -0500 4 | Subject: [PATCH] Spawn new tabs in working directory of selected clients child 5 | 6 | When used with st or xterm the working directory of the new tab 7 | should be the same as the currently selected tab. 8 | --- 9 | tabbed.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 10 | 1 file changed, 56 insertions(+), 1 deletion(-) 11 | 12 | diff --git a/tabbed.c b/tabbed.c 13 | index eafe28a..eb046d0 100644 14 | --- a/tabbed.c 15 | +++ b/tabbed.c 16 | @@ -10,6 +10,7 @@ 17 | #include 18 | #include 19 | #include 20 | +#include 21 | #include 22 | #include 23 | #include 24 | @@ -84,6 +85,7 @@ typedef struct { 25 | int tabx; 26 | Bool urgent; 27 | Bool closed; 28 | + pid_t pid; 29 | } Client; 30 | 31 | /* function declarations */ 32 | @@ -168,6 +170,7 @@ static int cmd_append_pos; 33 | static char winid[64]; 34 | static char **cmd; 35 | static char *wmname = "tabbed"; 36 | +static pid_t nextpid; 37 | static const char *geometry; 38 | 39 | char *argv0; 40 | @@ -175,6 +178,49 @@ char *argv0; 41 | /* configuration, allows nested code to access above variables */ 42 | #include "config.h" 43 | 44 | +// Given a pid, return its cwd to buf 45 | +int getpidcwd(pid_t pid, char* buf, size_t bufsiz) { 46 | + static const int proc_max = 20; // '/proc/4194304/cwd' 47 | + int sn_ret; 48 | + ssize_t rl_ret; 49 | + char path[proc_max]; 50 | + 51 | + sn_ret = snprintf(path, proc_max, "/proc/%d/cwd", pid); 52 | + if(sn_ret < 0 || sn_ret >= proc_max) 53 | + return -1; 54 | + 55 | + rl_ret = readlink(path, buf, bufsiz); 56 | + if(rl_ret < 0 || rl_ret == bufsiz) 57 | + return -1; 58 | + 59 | + buf[rl_ret] = 0; 60 | + return 0; 61 | +} 62 | + 63 | +// Given a pid, return a reasonable guess at its child pid 64 | +pid_t getchildpid(pid_t pid) { 65 | + // '/proc/4194304/task/4194304/children' 66 | + static const int proc_max = 40; 67 | + int sn_ret; 68 | + char path[proc_max]; 69 | + FILE* f; 70 | + 71 | + // guessing tid == pid 72 | + sn_ret = snprintf(path, proc_max, "/proc/%d/task/%d/children", pid, pid); 73 | + if (sn_ret < 0 || sn_ret >= proc_max) 74 | + return -1; 75 | + 76 | + f = fopen(path, "r"); 77 | + if(f == NULL) 78 | + return -1; 79 | + 80 | + // guess first child 81 | + if(fscanf(f, "%d ", &pid) != 1) 82 | + return -1; 83 | + 84 | + return pid; 85 | +} 86 | + 87 | void 88 | buttonpress(const XEvent *e) 89 | { 90 | @@ -725,6 +771,7 @@ manage(Window w) 91 | 92 | c = ecalloc(1, sizeof *c); 93 | c->win = w; 94 | + c->pid = nextpid; 95 | 96 | nclients++; 97 | clients = erealloc(clients, sizeof(Client *) * nclients); 98 | @@ -1090,11 +1137,17 @@ sigchld(int unused) 99 | void 100 | spawn(const Arg *arg) 101 | { 102 | - if (fork() == 0) { 103 | + char sel_cwd[PATH_MAX]; 104 | + 105 | + pid_t pid = fork(); 106 | + if (pid == 0) { 107 | if(dpy) 108 | close(ConnectionNumber(dpy)); 109 | 110 | setsid(); 111 | + if (sel >= 0 && clients[sel] && clients[sel]->pid > 0 && getpidcwd(getchildpid(clients[sel]->pid), sel_cwd, PATH_MAX) == 0) { 112 | + chdir(sel_cwd); 113 | + } 114 | if (arg && arg->v) { 115 | execvp(((char **)arg->v)[0], (char **)arg->v); 116 | fprintf(stderr, "%s: execvp %s", argv0, 117 | @@ -1106,6 +1159,8 @@ spawn(const Arg *arg) 118 | } 119 | perror(" failed"); 120 | exit(0); 121 | + } else { 122 | + nextpid = pid; 123 | } 124 | } 125 | 126 | -- 127 | 2.25.1 128 | 129 | -------------------------------------------------------------------------------- /tabbed/tabbed.1: -------------------------------------------------------------------------------- 1 | .TH TABBED 1 tabbed\-VERSION 2 | .SH NAME 3 | tabbed \- generic tabbed interface 4 | .SH SYNOPSIS 5 | .B tabbed 6 | .RB [ \-c ] 7 | .RB [ \-d ] 8 | .RB [ \-k ] 9 | .RB [ \-s ] 10 | .RB [ \-v ] 11 | .RB [ \-g 12 | .IR geometry ] 13 | .RB [ \-n 14 | .IR name ] 15 | .RB [ \-p 16 | .RB [ s {+/-} ] \fIpos\fR ] 17 | .RB [ \-o 18 | .IR normbgcol ] 19 | .RB [ \-O 20 | .IR normfgcol ] 21 | .RB [ \-t 22 | .IR selbgcol ] 23 | .RB [ \-T 24 | .IR selfgcol ] 25 | .RB [ \-u 26 | .IR urgbgcol ] 27 | .RB [ \-U 28 | .IR urgfgcol ] 29 | .RB [ \-r 30 | .IR narg ] 31 | .RI [ "command ..." ] 32 | .SH DESCRIPTION 33 | .B tabbed 34 | is a simple tabbed container for applications which support XEmbed. Tabbed 35 | will then run the provided command with the xid of tabbed as appended 36 | argument. (See EXAMPLES.) The automatic spawning of the command can be 37 | disabled by providing the -s parameter. If no command is provided 38 | tabbed will just print its xid and run no command. 39 | .SH OPTIONS 40 | .TP 41 | .B \-c 42 | close tabbed when the last tab is closed. Mutually exclusive with -f. 43 | .TP 44 | .B \-d 45 | detaches tabbed from the terminal and prints its XID to stdout. 46 | .TP 47 | .B \-f 48 | fill up tabbed again by spawning the provided command, when the last tab is 49 | closed. Mutually exclusive with -c. 50 | .TP 51 | .BI \-g " geometry" 52 | defines the X11 geometry string, which will fixate the height and width of 53 | tabbed. 54 | The syntax is 55 | .RI [=][ width {xX} height ][{+-} xoffset {+-} yoffset ]. 56 | See 57 | .BR XParseGeometry (3) 58 | for further details. 59 | .TP 60 | .B \-k 61 | close foreground tabbed client (instead of tabbed and all clients) when 62 | WM_DELETE_WINDOW is sent. 63 | .TP 64 | .BI \-n " name" 65 | will set the WM_CLASS attribute to 66 | .I name. 67 | .TP 68 | .BR \-p " [" s {+-}] \fIpos\fR 69 | will set the absolute or relative position of where to start a new tab. When 70 | .I pos 71 | is is given without 's' in front it is an absolute position. Then negative 72 | numbers will be the position from the last tab, where -1 is the last tab. 73 | If 's' is given, then 74 | .I pos 75 | is a relative position to the current selected tab. If this reaches the limits 76 | of the tabs; those limits then apply. 77 | .TP 78 | .BI \-r " narg" 79 | will replace the 80 | .I narg 81 | th argument in 82 | .I command 83 | with the window id, rather than appending it to the end. 84 | .TP 85 | .B \-s 86 | will disable automatic spawning of the command. 87 | .TP 88 | .BI \-o " normbgcol" 89 | defines the normal background color. 90 | .RI # RGB , 91 | .RI # RRGGBB , 92 | and X color names are supported. 93 | .TP 94 | .BI \-O " normfgcol" 95 | defines the normal foreground color. 96 | .TP 97 | .BI \-t " selbgcol" 98 | defines the selected background color. 99 | .TP 100 | .BI \-T " selfgbcol" 101 | defines the selected foreground color. 102 | .TP 103 | .BI \-u " urgbgcol" 104 | defines the urgent background color. 105 | .TP 106 | .BI \-U " urgfgbcol" 107 | defines the urgent foreground color. 108 | .TP 109 | .B \-v 110 | prints version information to stderr, then exits. 111 | .SH USAGE 112 | .TP 113 | .B Ctrl\-Shift\-Return 114 | open new tab 115 | .TP 116 | .B Ctrl\-Shift\-h 117 | previous tab 118 | .TP 119 | .B Ctrl\-Shift\-l 120 | next tab 121 | .TP 122 | .B Ctrl\-Shift\-j 123 | move selected tab one to the left 124 | .TP 125 | .B Ctrl\-Shift\-k 126 | move selected tab one to the right 127 | .TP 128 | .B Ctrl\-Shift\-u 129 | toggle autofocus of urgent tabs 130 | .TP 131 | .B Ctrl\-Tab 132 | toggle between the selected and last selected tab 133 | .TP 134 | .B Ctrl\-` 135 | open dmenu to either create a new tab appending the entered string or select 136 | an already existing tab. 137 | .TP 138 | .B Ctrl\-q 139 | close tab 140 | .TP 141 | .B Ctrl\-u 142 | focus next urgent tab 143 | .TP 144 | .B Ctrl\-[0..9] 145 | jumps to nth tab 146 | .TP 147 | .B F11 148 | Toggle fullscreen mode. 149 | .SH EXAMPLES 150 | $ tabbed surf -e 151 | .TP 152 | $ tabbed urxvt -embed 153 | .TP 154 | $ tabbed xterm -into 155 | .TP 156 | $ $(tabbed -d >/tmp/tabbed.xid); urxvt -embed $( 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "arg.h" 22 | 23 | /* XEMBED messages */ 24 | #define XEMBED_EMBEDDED_NOTIFY 0 25 | #define XEMBED_WINDOW_ACTIVATE 1 26 | #define XEMBED_WINDOW_DEACTIVATE 2 27 | #define XEMBED_REQUEST_FOCUS 3 28 | #define XEMBED_FOCUS_IN 4 29 | #define XEMBED_FOCUS_OUT 5 30 | #define XEMBED_FOCUS_NEXT 6 31 | #define XEMBED_FOCUS_PREV 7 32 | /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ 33 | #define XEMBED_MODALITY_ON 10 34 | #define XEMBED_MODALITY_OFF 11 35 | #define XEMBED_REGISTER_ACCELERATOR 12 36 | #define XEMBED_UNREGISTER_ACCELERATOR 13 37 | #define XEMBED_ACTIVATE_ACCELERATOR 14 38 | 39 | /* Details for XEMBED_FOCUS_IN: */ 40 | #define XEMBED_FOCUS_CURRENT 0 41 | #define XEMBED_FOCUS_FIRST 1 42 | #define XEMBED_FOCUS_LAST 2 43 | 44 | /* Macros */ 45 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 46 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 47 | #define LENGTH(x) (sizeof((x)) / sizeof(*(x))) 48 | #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) 49 | #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) 50 | 51 | enum { ColFG, ColBG, ColLast }; /* color */ 52 | enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, 53 | XEmbed, WMSelectTab, WMLast }; /* default atoms */ 54 | 55 | typedef union { 56 | int i; 57 | const void *v; 58 | } Arg; 59 | 60 | typedef struct { 61 | unsigned int mod; 62 | KeySym keysym; 63 | void (*func)(const Arg *); 64 | const Arg arg; 65 | } Key; 66 | 67 | typedef struct { 68 | int x, y, w, h; 69 | XftColor norm[ColLast]; 70 | XftColor sel[ColLast]; 71 | XftColor urg[ColLast]; 72 | Drawable drawable; 73 | GC gc; 74 | struct { 75 | int ascent; 76 | int descent; 77 | int height; 78 | XftFont *xfont; 79 | } font; 80 | } DC; /* draw context */ 81 | 82 | typedef struct { 83 | char name[256]; 84 | Window win; 85 | int tabx; 86 | Bool urgent; 87 | Bool closed; 88 | } Client; 89 | 90 | /* function declarations */ 91 | static void buttonpress(const XEvent *e); 92 | static void cleanup(void); 93 | static void clientmessage(const XEvent *e); 94 | static void configurenotify(const XEvent *e); 95 | static void configurerequest(const XEvent *e); 96 | static void createnotify(const XEvent *e); 97 | static void destroynotify(const XEvent *e); 98 | static void die(const char *errstr, ...); 99 | static void drawbar(void); 100 | static void drawtext(const char *text, XftColor col[ColLast]); 101 | static void *ecalloc(size_t n, size_t size); 102 | static void *erealloc(void *o, size_t size); 103 | static void expose(const XEvent *e); 104 | static void focus(int c); 105 | static void focusin(const XEvent *e); 106 | static void focusonce(const Arg *arg); 107 | static void focusurgent(const Arg *arg); 108 | static void fullscreen(const Arg *arg); 109 | static char *getatom(int a); 110 | static int getclient(Window w); 111 | static XftColor getcolor(const char *colstr); 112 | static int getfirsttab(void); 113 | static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); 114 | static void initfont(const char *fontstr); 115 | static Bool isprotodel(int c); 116 | static void keypress(const XEvent *e); 117 | static void killclient(const Arg *arg); 118 | static void manage(Window win); 119 | static void maprequest(const XEvent *e); 120 | static void move(const Arg *arg); 121 | static void movetab(const Arg *arg); 122 | static void propertynotify(const XEvent *e); 123 | static void resize(int c, int w, int h); 124 | static void rotate(const Arg *arg); 125 | static void run(void); 126 | static void sendxembed(int c, long msg, long detail, long d1, long d2); 127 | static void setcmd(int argc, char *argv[], int); 128 | static void setup(void); 129 | static void spawn(const Arg *arg); 130 | static int textnw(const char *text, unsigned int len); 131 | static void toggle(const Arg *arg); 132 | static void unmanage(int c); 133 | static void unmapnotify(const XEvent *e); 134 | static void updatenumlockmask(void); 135 | static void updatetitle(int c); 136 | static int xerror(Display *dpy, XErrorEvent *ee); 137 | static void xsettitle(Window w, const char *str); 138 | 139 | /* variables */ 140 | static int screen; 141 | static void (*handler[LASTEvent]) (const XEvent *) = { 142 | [ButtonPress] = buttonpress, 143 | [ClientMessage] = clientmessage, 144 | [ConfigureNotify] = configurenotify, 145 | [ConfigureRequest] = configurerequest, 146 | [CreateNotify] = createnotify, 147 | [UnmapNotify] = unmapnotify, 148 | [DestroyNotify] = destroynotify, 149 | [Expose] = expose, 150 | [FocusIn] = focusin, 151 | [KeyPress] = keypress, 152 | [MapRequest] = maprequest, 153 | [PropertyNotify] = propertynotify, 154 | }; 155 | static int bh, obh, wx, wy, ww, wh; 156 | static unsigned int numlockmask; 157 | static Bool running = True, nextfocus, doinitspawn = True, 158 | fillagain = False, closelastclient = True, 159 | killclientsfirst = False; 160 | static Display *dpy; 161 | static DC dc; 162 | static Atom wmatom[WMLast]; 163 | static Window root, win; 164 | static Client **clients; 165 | static int nclients, sel = -1, lastsel = -1; 166 | static int (*xerrorxlib)(Display *, XErrorEvent *); 167 | static int cmd_append_pos; 168 | static char winid[64]; 169 | static char **cmd; 170 | static char *wmname = "tabbed"; 171 | static const char *geometry; 172 | 173 | char *argv0; 174 | 175 | /* configuration, allows nested code to access above variables */ 176 | #include "config.def.h" 177 | 178 | void 179 | buttonpress(const XEvent *e) 180 | { 181 | const XButtonPressedEvent *ev = &e->xbutton; 182 | int i, fc; 183 | Arg arg; 184 | 185 | if (ev->y < 0 || ev->y > bh) 186 | return; 187 | 188 | if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) 189 | return; 190 | 191 | for (i = fc; i < nclients; i++) { 192 | if (clients[i]->tabx > ev->x) { 193 | switch (ev->button) { 194 | case Button1: 195 | focus(i); 196 | break; 197 | case Button2: 198 | focus(i); 199 | killclient(NULL); 200 | break; 201 | case Button4: /* FALLTHROUGH */ 202 | case Button5: 203 | arg.i = ev->button == Button4 ? -1 : 1; 204 | rotate(&arg); 205 | break; 206 | } 207 | break; 208 | } 209 | } 210 | } 211 | 212 | void 213 | cleanup(void) 214 | { 215 | int i; 216 | 217 | for (i = 0; i < nclients; i++) { 218 | focus(i); 219 | killclient(NULL); 220 | XReparentWindow(dpy, clients[i]->win, root, 0, 0); 221 | unmanage(i); 222 | } 223 | free(clients); 224 | clients = NULL; 225 | 226 | XFreePixmap(dpy, dc.drawable); 227 | XFreeGC(dpy, dc.gc); 228 | XDestroyWindow(dpy, win); 229 | XSync(dpy, False); 230 | free(cmd); 231 | } 232 | 233 | void 234 | clientmessage(const XEvent *e) 235 | { 236 | const XClientMessageEvent *ev = &e->xclient; 237 | 238 | if (ev->message_type == wmatom[WMProtocols] && 239 | ev->data.l[0] == wmatom[WMDelete]) { 240 | if (nclients > 1 && killclientsfirst) { 241 | killclient(0); 242 | return; 243 | } 244 | running = False; 245 | } 246 | } 247 | 248 | void 249 | configurenotify(const XEvent *e) 250 | { 251 | const XConfigureEvent *ev = &e->xconfigure; 252 | 253 | if (ev->window == win && (ev->width != ww || ev->height != wh)) { 254 | ww = ev->width; 255 | wh = ev->height; 256 | XFreePixmap(dpy, dc.drawable); 257 | dc.drawable = XCreatePixmap(dpy, root, ww, wh, 258 | DefaultDepth(dpy, screen)); 259 | 260 | if (!obh && (wh <= bh)) { 261 | obh = bh; 262 | bh = 0; 263 | } else if (!bh && (wh > obh)) { 264 | bh = obh; 265 | obh = 0; 266 | } 267 | 268 | if (sel > -1) 269 | resize(sel, ww, wh - bh); 270 | XSync(dpy, False); 271 | } 272 | } 273 | 274 | void 275 | configurerequest(const XEvent *e) 276 | { 277 | const XConfigureRequestEvent *ev = &e->xconfigurerequest; 278 | XWindowChanges wc; 279 | int c; 280 | 281 | if ((c = getclient(ev->window)) > -1) { 282 | wc.x = 0; 283 | wc.y = bh; 284 | wc.width = ww; 285 | wc.height = wh - bh; 286 | wc.border_width = 0; 287 | wc.sibling = ev->above; 288 | wc.stack_mode = ev->detail; 289 | XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &wc); 290 | } 291 | } 292 | 293 | void 294 | createnotify(const XEvent *e) 295 | { 296 | const XCreateWindowEvent *ev = &e->xcreatewindow; 297 | 298 | if (ev->window != win && getclient(ev->window) < 0) 299 | manage(ev->window); 300 | } 301 | 302 | void 303 | destroynotify(const XEvent *e) 304 | { 305 | const XDestroyWindowEvent *ev = &e->xdestroywindow; 306 | int c; 307 | 308 | if ((c = getclient(ev->window)) > -1) 309 | unmanage(c); 310 | } 311 | 312 | void 313 | die(const char *errstr, ...) 314 | { 315 | va_list ap; 316 | 317 | va_start(ap, errstr); 318 | vfprintf(stderr, errstr, ap); 319 | va_end(ap); 320 | exit(EXIT_FAILURE); 321 | } 322 | 323 | void 324 | drawbar(void) 325 | { 326 | XftColor *col; 327 | int c, cc, fc, width; 328 | char *name = NULL; 329 | 330 | if (nclients == 0) { 331 | dc.x = 0; 332 | dc.w = ww; 333 | XFetchName(dpy, win, &name); 334 | drawtext(name ? name : "", dc.norm); 335 | XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 336 | XSync(dpy, False); 337 | 338 | return; 339 | } 340 | 341 | width = ww; 342 | cc = ww / tabwidth; 343 | if (nclients > cc) 344 | cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 345 | 346 | if ((fc = getfirsttab()) + cc < nclients) { 347 | dc.w = TEXTW(after); 348 | dc.x = width - dc.w; 349 | drawtext(after, dc.sel); 350 | width -= dc.w; 351 | } 352 | dc.x = 0; 353 | 354 | if (fc > 0) { 355 | dc.w = TEXTW(before); 356 | drawtext(before, dc.sel); 357 | dc.x += dc.w; 358 | width -= dc.w; 359 | } 360 | 361 | cc = MIN(cc, nclients); 362 | for (c = fc; c < fc + cc; c++) { 363 | dc.w = width / cc; 364 | if (c == sel) { 365 | col = dc.sel; 366 | dc.w += width % cc; 367 | } else { 368 | col = clients[c]->urgent ? dc.urg : dc.norm; 369 | } 370 | drawtext(clients[c]->name, col); 371 | dc.x += dc.w; 372 | clients[c]->tabx = dc.x; 373 | } 374 | XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 375 | XSync(dpy, False); 376 | } 377 | 378 | void 379 | drawtext(const char *text, XftColor col[ColLast]) 380 | { 381 | int i, j, x, y, h, len, olen; 382 | char buf[256]; 383 | XftDraw *d; 384 | XRectangle r = { dc.x, dc.y, dc.w, dc.h }; 385 | 386 | XSetForeground(dpy, dc.gc, col[ColBG].pixel); 387 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); 388 | if (!text) 389 | return; 390 | 391 | olen = strlen(text); 392 | h = dc.font.ascent + dc.font.descent; 393 | y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; 394 | x = dc.x + (h / 2); 395 | 396 | /* shorten text if necessary */ 397 | for (len = MIN(olen, sizeof(buf)); 398 | len && textnw(text, len) > dc.w - h; len--); 399 | 400 | if (!len) 401 | return; 402 | 403 | memcpy(buf, text, len); 404 | if (len < olen) { 405 | for (i = len, j = strlen(titletrim); j && i; 406 | buf[--i] = titletrim[--j]) 407 | ; 408 | } 409 | 410 | d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); 411 | XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); 412 | XftDrawDestroy(d); 413 | } 414 | 415 | void * 416 | ecalloc(size_t n, size_t size) 417 | { 418 | void *p; 419 | 420 | if (!(p = calloc(n, size))) 421 | die("%s: cannot calloc\n", argv0); 422 | return p; 423 | } 424 | 425 | void * 426 | erealloc(void *o, size_t size) 427 | { 428 | void *p; 429 | 430 | if (!(p = realloc(o, size))) 431 | die("%s: cannot realloc\n", argv0); 432 | return p; 433 | } 434 | 435 | void 436 | expose(const XEvent *e) 437 | { 438 | const XExposeEvent *ev = &e->xexpose; 439 | 440 | if (ev->count == 0 && win == ev->window) 441 | drawbar(); 442 | } 443 | 444 | void 445 | focus(int c) 446 | { 447 | char buf[BUFSIZ] = "tabbed-"VERSION" ::"; 448 | size_t i, n; 449 | XWMHints* wmh; 450 | 451 | /* If c, sel and clients are -1, raise tabbed-win itself */ 452 | if (nclients == 0) { 453 | cmd[cmd_append_pos] = NULL; 454 | for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) 455 | n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); 456 | 457 | xsettitle(win, buf); 458 | XRaiseWindow(dpy, win); 459 | 460 | return; 461 | } 462 | 463 | if (c < 0 || c >= nclients) 464 | return; 465 | 466 | resize(c, ww, wh - bh); 467 | XRaiseWindow(dpy, clients[c]->win); 468 | XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime); 469 | sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); 470 | sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); 471 | xsettitle(win, clients[c]->name); 472 | 473 | if (sel != c) { 474 | lastsel = sel; 475 | sel = c; 476 | } 477 | 478 | if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->win))) { 479 | wmh->flags &= ~XUrgencyHint; 480 | XSetWMHints(dpy, clients[c]->win, wmh); 481 | clients[c]->urgent = False; 482 | XFree(wmh); 483 | } 484 | 485 | drawbar(); 486 | XSync(dpy, False); 487 | } 488 | 489 | void 490 | focusin(const XEvent *e) 491 | { 492 | const XFocusChangeEvent *ev = &e->xfocus; 493 | int dummy; 494 | Window focused; 495 | 496 | if (ev->mode != NotifyUngrab) { 497 | XGetInputFocus(dpy, &focused, &dummy); 498 | if (focused == win) 499 | focus(sel); 500 | } 501 | } 502 | 503 | void 504 | focusonce(const Arg *arg) 505 | { 506 | nextfocus = True; 507 | } 508 | 509 | void 510 | focusurgent(const Arg *arg) 511 | { 512 | int c; 513 | 514 | if (sel < 0) 515 | return; 516 | 517 | for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients) { 518 | if (clients[c]->urgent) { 519 | focus(c); 520 | return; 521 | } 522 | } 523 | } 524 | 525 | void 526 | fullscreen(const Arg *arg) 527 | { 528 | XEvent e; 529 | 530 | e.type = ClientMessage; 531 | e.xclient.window = win; 532 | e.xclient.message_type = wmatom[WMState]; 533 | e.xclient.format = 32; 534 | e.xclient.data.l[0] = 2; 535 | e.xclient.data.l[1] = wmatom[WMFullscreen]; 536 | e.xclient.data.l[2] = 0; 537 | XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); 538 | } 539 | 540 | char * 541 | getatom(int a) 542 | { 543 | static char buf[BUFSIZ]; 544 | Atom adummy; 545 | int idummy; 546 | unsigned long ldummy; 547 | unsigned char *p = NULL; 548 | 549 | XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_STRING, 550 | &adummy, &idummy, &ldummy, &ldummy, &p); 551 | if (p) 552 | strncpy(buf, (char *)p, LENGTH(buf)-1); 553 | else 554 | buf[0] = '\0'; 555 | XFree(p); 556 | 557 | return buf; 558 | } 559 | 560 | int 561 | getclient(Window w) 562 | { 563 | int i; 564 | 565 | for (i = 0; i < nclients; i++) { 566 | if (clients[i]->win == w) 567 | return i; 568 | } 569 | 570 | return -1; 571 | } 572 | 573 | XftColor 574 | getcolor(const char *colstr) 575 | { 576 | XftColor color; 577 | 578 | if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), colstr, &color)) 579 | die("%s: cannot allocate color '%s'\n", argv0, colstr); 580 | 581 | return color; 582 | } 583 | 584 | int 585 | getfirsttab(void) 586 | { 587 | int cc, ret; 588 | 589 | if (sel < 0) 590 | return 0; 591 | 592 | cc = ww / tabwidth; 593 | if (nclients > cc) 594 | cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 595 | 596 | ret = sel - cc / 2 + (cc + 1) % 2; 597 | return ret < 0 ? 0 : 598 | ret + cc > nclients ? MAX(0, nclients - cc) : 599 | ret; 600 | } 601 | 602 | Bool 603 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 604 | { 605 | char **list = NULL; 606 | int n; 607 | XTextProperty name; 608 | 609 | if (!text || size == 0) 610 | return False; 611 | 612 | text[0] = '\0'; 613 | XGetTextProperty(dpy, w, &name, atom); 614 | if (!name.nitems) 615 | return False; 616 | 617 | if (name.encoding == XA_STRING) { 618 | strncpy(text, (char *)name.value, size - 1); 619 | } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success 620 | && n > 0 && *list) { 621 | strncpy(text, *list, size - 1); 622 | XFreeStringList(list); 623 | } 624 | text[size - 1] = '\0'; 625 | XFree(name.value); 626 | 627 | return True; 628 | } 629 | 630 | void 631 | initfont(const char *fontstr) 632 | { 633 | if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr)) 634 | && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed"))) 635 | die("error, cannot load font: '%s'\n", fontstr); 636 | 637 | dc.font.ascent = dc.font.xfont->ascent; 638 | dc.font.descent = dc.font.xfont->descent; 639 | dc.font.height = dc.font.ascent + dc.font.descent; 640 | } 641 | 642 | Bool 643 | isprotodel(int c) 644 | { 645 | int i, n; 646 | Atom *protocols; 647 | Bool ret = False; 648 | 649 | if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) { 650 | for (i = 0; !ret && i < n; i++) { 651 | if (protocols[i] == wmatom[WMDelete]) 652 | ret = True; 653 | } 654 | XFree(protocols); 655 | } 656 | 657 | return ret; 658 | } 659 | 660 | void 661 | keypress(const XEvent *e) 662 | { 663 | const XKeyEvent *ev = &e->xkey; 664 | unsigned int i; 665 | KeySym keysym; 666 | 667 | keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 668 | for (i = 0; i < LENGTH(keys); i++) { 669 | if (keysym == keys[i].keysym && 670 | CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && 671 | keys[i].func) 672 | keys[i].func(&(keys[i].arg)); 673 | } 674 | } 675 | 676 | void 677 | killclient(const Arg *arg) 678 | { 679 | XEvent ev; 680 | 681 | if (sel < 0) 682 | return; 683 | 684 | if (isprotodel(sel) && !clients[sel]->closed) { 685 | ev.type = ClientMessage; 686 | ev.xclient.window = clients[sel]->win; 687 | ev.xclient.message_type = wmatom[WMProtocols]; 688 | ev.xclient.format = 32; 689 | ev.xclient.data.l[0] = wmatom[WMDelete]; 690 | ev.xclient.data.l[1] = CurrentTime; 691 | XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &ev); 692 | clients[sel]->closed = True; 693 | } else { 694 | XKillClient(dpy, clients[sel]->win); 695 | } 696 | } 697 | 698 | void 699 | manage(Window w) 700 | { 701 | updatenumlockmask(); 702 | { 703 | int i, j, nextpos; 704 | unsigned int modifiers[] = { 0, LockMask, numlockmask, 705 | numlockmask | LockMask }; 706 | KeyCode code; 707 | Client *c; 708 | XEvent e; 709 | 710 | XWithdrawWindow(dpy, w, 0); 711 | XReparentWindow(dpy, w, win, 0, bh); 712 | XSelectInput(dpy, w, PropertyChangeMask | 713 | StructureNotifyMask | EnterWindowMask); 714 | XSync(dpy, False); 715 | 716 | for (i = 0; i < LENGTH(keys); i++) { 717 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { 718 | for (j = 0; j < LENGTH(modifiers); j++) { 719 | XGrabKey(dpy, code, keys[i].mod | 720 | modifiers[j], w, True, 721 | GrabModeAsync, GrabModeAsync); 722 | } 723 | } 724 | } 725 | 726 | c = ecalloc(1, sizeof *c); 727 | c->win = w; 728 | 729 | nclients++; 730 | clients = erealloc(clients, sizeof(Client *) * nclients); 731 | 732 | if(npisrelative) { 733 | nextpos = sel + newposition; 734 | } else { 735 | if (newposition < 0) 736 | nextpos = nclients - newposition; 737 | else 738 | nextpos = newposition; 739 | } 740 | if (nextpos >= nclients) 741 | nextpos = nclients - 1; 742 | if (nextpos < 0) 743 | nextpos = 0; 744 | 745 | if (nclients > 1 && nextpos < nclients - 1) 746 | memmove(&clients[nextpos + 1], &clients[nextpos], 747 | sizeof(Client *) * (nclients - nextpos - 1)); 748 | 749 | clients[nextpos] = c; 750 | updatetitle(nextpos); 751 | 752 | XLowerWindow(dpy, w); 753 | XMapWindow(dpy, w); 754 | 755 | e.xclient.window = w; 756 | e.xclient.type = ClientMessage; 757 | e.xclient.message_type = wmatom[XEmbed]; 758 | e.xclient.format = 32; 759 | e.xclient.data.l[0] = CurrentTime; 760 | e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; 761 | e.xclient.data.l[2] = 0; 762 | e.xclient.data.l[3] = win; 763 | e.xclient.data.l[4] = 0; 764 | XSendEvent(dpy, root, False, NoEventMask, &e); 765 | 766 | XSync(dpy, False); 767 | 768 | /* Adjust sel before focus does set it to lastsel. */ 769 | if (sel >= nextpos) 770 | sel++; 771 | focus(nextfocus ? nextpos : 772 | sel < 0 ? 0 : 773 | sel); 774 | nextfocus = foreground; 775 | } 776 | } 777 | 778 | void 779 | maprequest(const XEvent *e) 780 | { 781 | const XMapRequestEvent *ev = &e->xmaprequest; 782 | 783 | if (getclient(ev->window) < 0) 784 | manage(ev->window); 785 | } 786 | 787 | void 788 | move(const Arg *arg) 789 | { 790 | if (arg->i >= 0 && arg->i < nclients) 791 | focus(arg->i); 792 | } 793 | 794 | void 795 | movetab(const Arg *arg) 796 | { 797 | int c; 798 | Client *new; 799 | 800 | if (sel < 0) 801 | return; 802 | 803 | c = (sel + arg->i) % nclients; 804 | if (c < 0) 805 | c += nclients; 806 | 807 | if (c == sel) 808 | return; 809 | 810 | new = clients[sel]; 811 | if (sel < c) 812 | memmove(&clients[sel], &clients[sel+1], 813 | sizeof(Client *) * (c - sel)); 814 | else 815 | memmove(&clients[c+1], &clients[c], 816 | sizeof(Client *) * (sel - c)); 817 | clients[c] = new; 818 | sel = c; 819 | 820 | drawbar(); 821 | } 822 | 823 | void 824 | propertynotify(const XEvent *e) 825 | { 826 | const XPropertyEvent *ev = &e->xproperty; 827 | XWMHints *wmh; 828 | int c; 829 | char* selection = NULL; 830 | Arg arg; 831 | 832 | if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelectTab]) { 833 | selection = getatom(WMSelectTab); 834 | if (!strncmp(selection, "0x", 2)) { 835 | arg.i = getclient(strtoul(selection, NULL, 0)); 836 | move(&arg); 837 | } else { 838 | cmd[cmd_append_pos] = selection; 839 | arg.v = cmd; 840 | spawn(&arg); 841 | } 842 | } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HINTS && 843 | (c = getclient(ev->window)) > -1 && 844 | (wmh = XGetWMHints(dpy, clients[c]->win))) { 845 | if (wmh->flags & XUrgencyHint) { 846 | XFree(wmh); 847 | wmh = XGetWMHints(dpy, win); 848 | if (c != sel) { 849 | if (urgentswitch && wmh && 850 | !(wmh->flags & XUrgencyHint)) { 851 | /* only switch, if tabbed was focused 852 | * since last urgency hint if WMHints 853 | * could not be received, 854 | * default to no switch */ 855 | focus(c); 856 | } else { 857 | /* if no switch should be performed, 858 | * mark tab as urgent */ 859 | clients[c]->urgent = True; 860 | drawbar(); 861 | } 862 | } 863 | if (wmh && !(wmh->flags & XUrgencyHint)) { 864 | /* update tabbed urgency hint 865 | * if not set already */ 866 | wmh->flags |= XUrgencyHint; 867 | XSetWMHints(dpy, win, wmh); 868 | } 869 | } 870 | XFree(wmh); 871 | } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME && 872 | (c = getclient(ev->window)) > -1) { 873 | updatetitle(c); 874 | } 875 | } 876 | 877 | void 878 | resize(int c, int w, int h) 879 | { 880 | XConfigureEvent ce; 881 | XWindowChanges wc; 882 | 883 | ce.x = 0; 884 | ce.y = wc.y = bh; 885 | ce.width = wc.width = w; 886 | ce.height = wc.height = h; 887 | ce.type = ConfigureNotify; 888 | ce.display = dpy; 889 | ce.event = clients[c]->win; 890 | ce.window = clients[c]->win; 891 | ce.above = None; 892 | ce.override_redirect = False; 893 | ce.border_width = 0; 894 | 895 | XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight, &wc); 896 | XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask, 897 | (XEvent *)&ce); 898 | } 899 | 900 | void 901 | rotate(const Arg *arg) 902 | { 903 | int nsel = -1; 904 | 905 | if (sel < 0) 906 | return; 907 | 908 | if (arg->i == 0) { 909 | if (lastsel > -1) 910 | focus(lastsel); 911 | } else if (sel > -1) { 912 | /* Rotating in an arg->i step around the clients. */ 913 | nsel = sel + arg->i; 914 | while (nsel >= nclients) 915 | nsel -= nclients; 916 | while (nsel < 0) 917 | nsel += nclients; 918 | focus(nsel); 919 | } 920 | } 921 | 922 | void 923 | run(void) 924 | { 925 | XEvent ev; 926 | 927 | /* main event loop */ 928 | XSync(dpy, False); 929 | drawbar(); 930 | if (doinitspawn == True) 931 | spawn(NULL); 932 | 933 | while (running) { 934 | XNextEvent(dpy, &ev); 935 | if (handler[ev.type]) 936 | (handler[ev.type])(&ev); /* call handler */ 937 | } 938 | } 939 | 940 | void 941 | sendxembed(int c, long msg, long detail, long d1, long d2) 942 | { 943 | XEvent e = { 0 }; 944 | 945 | e.xclient.window = clients[c]->win; 946 | e.xclient.type = ClientMessage; 947 | e.xclient.message_type = wmatom[XEmbed]; 948 | e.xclient.format = 32; 949 | e.xclient.data.l[0] = CurrentTime; 950 | e.xclient.data.l[1] = msg; 951 | e.xclient.data.l[2] = detail; 952 | e.xclient.data.l[3] = d1; 953 | e.xclient.data.l[4] = d2; 954 | XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e); 955 | } 956 | 957 | void 958 | setcmd(int argc, char *argv[], int replace) 959 | { 960 | int i; 961 | 962 | cmd = ecalloc(argc + 3, sizeof(*cmd)); 963 | if (argc == 0) 964 | return; 965 | for (i = 0; i < argc; i++) 966 | cmd[i] = argv[i]; 967 | cmd[replace > 0 ? replace : argc] = winid; 968 | cmd_append_pos = argc + !replace; 969 | cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL; 970 | } 971 | 972 | void 973 | setup(void) 974 | { 975 | int bitm, tx, ty, tw, th, dh, dw, isfixed; 976 | XWMHints *wmh; 977 | XClassHint class_hint; 978 | XSizeHints *size_hint; 979 | struct sigaction sa; 980 | 981 | /* do not transform children into zombies when they terminate */ 982 | sigemptyset(&sa.sa_mask); 983 | sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 984 | sa.sa_handler = SIG_IGN; 985 | sigaction(SIGCHLD, &sa, NULL); 986 | 987 | /* clean up any zombies that might have been inherited */ 988 | while (waitpid(-1, NULL, WNOHANG) > 0); 989 | 990 | /* init screen */ 991 | screen = DefaultScreen(dpy); 992 | root = RootWindow(dpy, screen); 993 | initfont(font); 994 | bh = dc.h = barheight; 995 | 996 | /* init atoms */ 997 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 998 | wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 999 | False); 1000 | wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1001 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1002 | wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); 1003 | wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1004 | wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); 1005 | 1006 | /* init appearance */ 1007 | wx = 0; 1008 | wy = 0; 1009 | ww = 800; 1010 | wh = 600; 1011 | isfixed = 0; 1012 | 1013 | if (geometry) { 1014 | tx = ty = tw = th = 0; 1015 | bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&tw, 1016 | (unsigned *)&th); 1017 | if (bitm & XValue) 1018 | wx = tx; 1019 | if (bitm & YValue) 1020 | wy = ty; 1021 | if (bitm & WidthValue) 1022 | ww = tw; 1023 | if (bitm & HeightValue) 1024 | wh = th; 1025 | if (bitm & XNegative && wx == 0) 1026 | wx = -1; 1027 | if (bitm & YNegative && wy == 0) 1028 | wy = -1; 1029 | if (bitm & (HeightValue | WidthValue)) 1030 | isfixed = 1; 1031 | 1032 | dw = DisplayWidth(dpy, screen); 1033 | dh = DisplayHeight(dpy, screen); 1034 | if (wx < 0) 1035 | wx = dw + wx - ww - 1; 1036 | if (wy < 0) 1037 | wy = dh + wy - wh - 1; 1038 | } 1039 | 1040 | dc.norm[ColBG] = getcolor(normbgcolor); 1041 | dc.norm[ColFG] = getcolor(normfgcolor); 1042 | dc.sel[ColBG] = getcolor(selbgcolor); 1043 | dc.sel[ColFG] = getcolor(selfgcolor); 1044 | dc.urg[ColBG] = getcolor(urgbgcolor); 1045 | dc.urg[ColFG] = getcolor(urgfgcolor); 1046 | dc.drawable = XCreatePixmap(dpy, root, ww, wh, 1047 | DefaultDepth(dpy, screen)); 1048 | dc.gc = XCreateGC(dpy, root, 0, 0); 1049 | 1050 | win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, 1051 | dc.norm[ColFG].pixel, dc.norm[ColBG].pixel); 1052 | XMapRaised(dpy, win); 1053 | XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | 1054 | ButtonPressMask | ExposureMask | KeyPressMask | 1055 | PropertyChangeMask | StructureNotifyMask | 1056 | SubstructureRedirectMask); 1057 | xerrorxlib = XSetErrorHandler(xerror); 1058 | 1059 | class_hint.res_name = wmname; 1060 | class_hint.res_class = "tabbed"; 1061 | XSetClassHint(dpy, win, &class_hint); 1062 | 1063 | size_hint = XAllocSizeHints(); 1064 | if (!isfixed) { 1065 | size_hint->flags = PSize | PMinSize; 1066 | size_hint->height = wh; 1067 | size_hint->width = ww; 1068 | size_hint->min_height = bh + 1; 1069 | } else { 1070 | size_hint->flags = PMaxSize | PMinSize; 1071 | size_hint->min_width = size_hint->max_width = ww; 1072 | size_hint->min_height = size_hint->max_height = wh; 1073 | } 1074 | wmh = XAllocWMHints(); 1075 | XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, NULL); 1076 | XFree(size_hint); 1077 | XFree(wmh); 1078 | 1079 | XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); 1080 | 1081 | snprintf(winid, sizeof(winid), "%lu", win); 1082 | setenv("XEMBED", winid, 1); 1083 | 1084 | nextfocus = foreground; 1085 | focus(-1); 1086 | } 1087 | 1088 | void 1089 | spawn(const Arg *arg) 1090 | { 1091 | struct sigaction sa; 1092 | 1093 | if (fork() == 0) { 1094 | if(dpy) 1095 | close(ConnectionNumber(dpy)); 1096 | 1097 | setsid(); 1098 | 1099 | sigemptyset(&sa.sa_mask); 1100 | sa.sa_flags = 0; 1101 | sa.sa_handler = SIG_DFL; 1102 | sigaction(SIGCHLD, &sa, NULL); 1103 | 1104 | if (arg && arg->v) { 1105 | execvp(((char **)arg->v)[0], (char **)arg->v); 1106 | fprintf(stderr, "%s: execvp %s", argv0, 1107 | ((char **)arg->v)[0]); 1108 | } else { 1109 | cmd[cmd_append_pos] = NULL; 1110 | execvp(cmd[0], cmd); 1111 | fprintf(stderr, "%s: execvp %s", argv0, cmd[0]); 1112 | } 1113 | perror(" failed"); 1114 | exit(0); 1115 | } 1116 | } 1117 | 1118 | int 1119 | textnw(const char *text, unsigned int len) 1120 | { 1121 | XGlyphInfo ext; 1122 | XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &ext); 1123 | return ext.xOff; 1124 | } 1125 | 1126 | void 1127 | toggle(const Arg *arg) 1128 | { 1129 | *(Bool*) arg->v = !*(Bool*) arg->v; 1130 | } 1131 | 1132 | void 1133 | unmanage(int c) 1134 | { 1135 | if (c < 0 || c >= nclients) { 1136 | drawbar(); 1137 | XSync(dpy, False); 1138 | return; 1139 | } 1140 | 1141 | if (!nclients) 1142 | return; 1143 | 1144 | if (c == 0) { 1145 | /* First client. */ 1146 | nclients--; 1147 | free(clients[0]); 1148 | memmove(&clients[0], &clients[1], sizeof(Client *) * nclients); 1149 | } else if (c == nclients - 1) { 1150 | /* Last client. */ 1151 | nclients--; 1152 | free(clients[c]); 1153 | clients = erealloc(clients, sizeof(Client *) * nclients); 1154 | } else { 1155 | /* Somewhere inbetween. */ 1156 | free(clients[c]); 1157 | memmove(&clients[c], &clients[c+1], 1158 | sizeof(Client *) * (nclients - (c + 1))); 1159 | nclients--; 1160 | } 1161 | 1162 | if (nclients <= 0) { 1163 | lastsel = sel = -1; 1164 | 1165 | if (closelastclient) 1166 | running = False; 1167 | else if (fillagain && running) 1168 | spawn(NULL); 1169 | } else { 1170 | if (lastsel >= nclients) 1171 | lastsel = nclients - 1; 1172 | else if (lastsel > c) 1173 | lastsel--; 1174 | 1175 | if (c == sel && lastsel >= 0) { 1176 | focus(lastsel); 1177 | } else { 1178 | if (sel > c) 1179 | sel--; 1180 | if (sel >= nclients) 1181 | sel = nclients - 1; 1182 | 1183 | focus(sel); 1184 | } 1185 | } 1186 | 1187 | drawbar(); 1188 | XSync(dpy, False); 1189 | } 1190 | 1191 | void 1192 | unmapnotify(const XEvent *e) 1193 | { 1194 | const XUnmapEvent *ev = &e->xunmap; 1195 | int c; 1196 | 1197 | if ((c = getclient(ev->window)) > -1) 1198 | unmanage(c); 1199 | } 1200 | 1201 | void 1202 | updatenumlockmask(void) 1203 | { 1204 | unsigned int i, j; 1205 | XModifierKeymap *modmap; 1206 | 1207 | numlockmask = 0; 1208 | modmap = XGetModifierMapping(dpy); 1209 | for (i = 0; i < 8; i++) { 1210 | for (j = 0; j < modmap->max_keypermod; j++) { 1211 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 1212 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 1213 | numlockmask = (1 << i); 1214 | } 1215 | } 1216 | XFreeModifiermap(modmap); 1217 | } 1218 | 1219 | void 1220 | updatetitle(int c) 1221 | { 1222 | if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->name, 1223 | sizeof(clients[c]->name))) 1224 | gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->name, 1225 | sizeof(clients[c]->name)); 1226 | if (sel == c) 1227 | xsettitle(win, clients[c]->name); 1228 | drawbar(); 1229 | } 1230 | 1231 | /* There's no way to check accesses to destroyed windows, thus those cases are 1232 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 1233 | * default error handler, which may call exit. */ 1234 | int 1235 | xerror(Display *dpy, XErrorEvent *ee) 1236 | { 1237 | if (ee->error_code == BadWindow 1238 | || (ee->request_code == X_SetInputFocus && 1239 | ee->error_code == BadMatch) 1240 | || (ee->request_code == X_PolyText8 && 1241 | ee->error_code == BadDrawable) 1242 | || (ee->request_code == X_PolyFillRectangle && 1243 | ee->error_code == BadDrawable) 1244 | || (ee->request_code == X_PolySegment && 1245 | ee->error_code == BadDrawable) 1246 | || (ee->request_code == X_ConfigureWindow && 1247 | ee->error_code == BadMatch) 1248 | || (ee->request_code == X_GrabButton && 1249 | ee->error_code == BadAccess) 1250 | || (ee->request_code == X_GrabKey && 1251 | ee->error_code == BadAccess) 1252 | || (ee->request_code == X_CopyArea && 1253 | ee->error_code == BadDrawable)) 1254 | return 0; 1255 | 1256 | fprintf(stderr, "%s: fatal error: request code=%d, error code=%d\n", 1257 | argv0, ee->request_code, ee->error_code); 1258 | return xerrorxlib(dpy, ee); /* may call exit */ 1259 | } 1260 | 1261 | void 1262 | xsettitle(Window w, const char *str) 1263 | { 1264 | XTextProperty xtp; 1265 | 1266 | if (XmbTextListToTextProperty(dpy, (char **)&str, 1, 1267 | XCompoundTextStyle, &xtp) == Success) { 1268 | XSetTextProperty(dpy, w, &xtp, wmatom[WMName]); 1269 | XSetTextProperty(dpy, w, &xtp, XA_WM_NAME); 1270 | XFree(xtp.value); 1271 | } 1272 | } 1273 | 1274 | void 1275 | usage(void) 1276 | { 1277 | die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" 1278 | " [-r narg] [-o color] [-O color] [-t color] [-T color]\n" 1279 | " [-u color] [-U color] command...\n", argv0); 1280 | } 1281 | 1282 | int 1283 | main(int argc, char *argv[]) 1284 | { 1285 | Bool detach = False; 1286 | int replace = 0; 1287 | char *pstr; 1288 | 1289 | ARGBEGIN { 1290 | case 'c': 1291 | closelastclient = True; 1292 | fillagain = False; 1293 | break; 1294 | case 'd': 1295 | detach = True; 1296 | break; 1297 | case 'f': 1298 | fillagain = True; 1299 | break; 1300 | case 'g': 1301 | geometry = EARGF(usage()); 1302 | break; 1303 | case 'k': 1304 | killclientsfirst = True; 1305 | break; 1306 | case 'n': 1307 | wmname = EARGF(usage()); 1308 | break; 1309 | case 'O': 1310 | normfgcolor = EARGF(usage()); 1311 | break; 1312 | case 'o': 1313 | normbgcolor = EARGF(usage()); 1314 | break; 1315 | case 'p': 1316 | pstr = EARGF(usage()); 1317 | if (pstr[0] == 's') { 1318 | npisrelative = True; 1319 | newposition = atoi(&pstr[1]); 1320 | } else { 1321 | newposition = atoi(pstr); 1322 | } 1323 | break; 1324 | case 'r': 1325 | replace = atoi(EARGF(usage())); 1326 | break; 1327 | case 's': 1328 | doinitspawn = False; 1329 | break; 1330 | case 'T': 1331 | selfgcolor = EARGF(usage()); 1332 | break; 1333 | case 't': 1334 | selbgcolor = EARGF(usage()); 1335 | break; 1336 | case 'U': 1337 | urgfgcolor = EARGF(usage()); 1338 | break; 1339 | case 'u': 1340 | urgbgcolor = EARGF(usage()); 1341 | break; 1342 | case 'v': 1343 | die("tabbed-"VERSION", © 2009-2016 tabbed engineers, " 1344 | "see LICENSE for details.\n"); 1345 | break; 1346 | default: 1347 | usage(); 1348 | break; 1349 | } ARGEND; 1350 | 1351 | if (argc < 1) { 1352 | doinitspawn = False; 1353 | fillagain = False; 1354 | } 1355 | 1356 | setcmd(argc, argv, replace); 1357 | 1358 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 1359 | fprintf(stderr, "%s: no locale support\n", argv0); 1360 | if (!(dpy = XOpenDisplay(NULL))) 1361 | die("%s: cannot open display\n", argv0); 1362 | 1363 | setup(); 1364 | printf("0x%lx\n", win); 1365 | fflush(NULL); 1366 | 1367 | if (detach) { 1368 | if (fork() == 0) { 1369 | fclose(stdout); 1370 | } else { 1371 | if (dpy) 1372 | close(ConnectionNumber(dpy)); 1373 | return EXIT_SUCCESS; 1374 | } 1375 | } 1376 | 1377 | run(); 1378 | cleanup(); 1379 | XCloseDisplay(dpy); 1380 | 1381 | return EXIT_SUCCESS; 1382 | } 1383 | -------------------------------------------------------------------------------- /tabbed/tabbed.c.rej: -------------------------------------------------------------------------------- 1 | --- tabbed.c 2 | +++ tabbed.c 3 | @@ -993,7 +1002,7 @@ setup(void) 4 | screen = DefaultScreen(dpy); 5 | root = RootWindow(dpy, screen); 6 | initfont(font); 7 | - bh = dc.h = dc.font.height + 2; 8 | + vbh = dc.h = dc.font.height + 2; 9 | 10 | /* init atoms */ 11 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 12 | -------------------------------------------------------------------------------- /tabbed/xembed.1: -------------------------------------------------------------------------------- 1 | .TH XEMBED 1 tabbed\-VERSION 2 | .SH NAME 3 | xembed \- XEmbed foreground process 4 | .SH SYNOPSIS 5 | .B xembed 6 | .I flag command 7 | .RI [ "argument ..." ] 8 | .SH DESCRIPTION 9 | If the environment variable XEMBED is set, and 10 | .B xembed 11 | is in the foreground of its controlling tty, it will execute 12 | .IP 13 | command flag $XEMBED [argument ...] 14 | .LP 15 | Otherwise it will execute 16 | .IP 17 | command [argument ...] 18 | .LP 19 | .SH EXAMPLE 20 | In a terminal emulator within a 21 | .B tabbed 22 | session, the shell alias 23 | .IP 24 | $ alias surf='xembed -e surf' 25 | .LP 26 | will cause `surf' to open in a new tab, unless it is run in the background, 27 | i.e. `surf &', in which case it will instead open in a new window. 28 | .SH AUTHORS 29 | See the LICENSE file for the authors. 30 | .SH LICENSE 31 | See the LICENSE file for the terms of redistribution. 32 | .SH SEE ALSO 33 | .BR tabbed (1) 34 | .SH BUGS 35 | Please report them. 36 | -------------------------------------------------------------------------------- /tabbed/xembed.c: -------------------------------------------------------------------------------- 1 | /* 2 | * See LICENSE file for copyright and license details. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int 11 | main(int argc, char *argv[]) 12 | { 13 | char *xembed; 14 | int tty; 15 | pid_t pgrp, tcpgrp; 16 | 17 | if (argc < 3) { 18 | fprintf(stderr, "usage: %s flag cmd ...\n", argv[0]); 19 | return 2; 20 | } 21 | 22 | if (!(xembed = getenv("XEMBED"))) 23 | goto noembed; 24 | 25 | if ((tty = open("/dev/tty", O_RDONLY)) < 0) 26 | goto noembed; 27 | 28 | pgrp = getpgrp(); 29 | tcpgrp = tcgetpgrp(tty); 30 | 31 | close(tty); 32 | 33 | if (pgrp == tcpgrp) { /* in foreground of tty */ 34 | argv[0] = argv[2]; 35 | argv[2] = xembed; 36 | } else { 37 | noembed: 38 | argv += 2; 39 | } 40 | 41 | execvp(argv[0], argv); 42 | 43 | perror(argv[0]); /* failed to execute */ 44 | return 1; 45 | } 46 | --------------------------------------------------------------------------------