Compare commits
43 Commits
6e7e845a6c
...
v1.9
Author | SHA1 | Date | |
---|---|---|---|
19d201ecf4 | |||
cb7bfb36e8 | |||
9c16367299 | |||
68620696fc | |||
43067c0efe | |||
4773bdae87 | |||
ea7450bb8b | |||
50f7bd809a | |||
1de8cde4cf | |||
7ee783edeb | |||
9c1ac5f7f3 | |||
dca451c996 | |||
61cdfffc0d | |||
5851aec8b3 | |||
8b30abc8b6 | |||
9efa0b110d | |||
c1278a81ee | |||
fa0c32297c | |||
c6f97648c3 | |||
ec8aa3c00a | |||
de95c4284a | |||
390fc4c114 | |||
71cd45f781 | |||
6702dc357b | |||
a7f22597f1 | |||
530c9e5073 | |||
73d16bde78 | |||
ccbb89e372 | |||
8ce2e7eb32 | |||
0e7125d2fc | |||
5671b31715 | |||
a28cbc59ff | |||
ce5f632bfc | |||
2953ecd157 | |||
64cffc7fc7 | |||
e0ac69b66e | |||
689b997dae | |||
1a0daf36a8 | |||
29e941e3d5 | |||
9109f7660c | |||
e1043e982d | |||
ac1464d965 | |||
40104086fe |
23
README.md
23
README.md
@ -6,6 +6,7 @@ Accessibility setup script for the i3 window manager.
|
||||
Released under the terms of the WTFPL License: http://www.wtfpl.net
|
||||
This is a Stormux project: https://stormux.org
|
||||
|
||||
|
||||
## Why the name?
|
||||
|
||||
An uppercase I looks like a 1, 3 from i3, and 8 because the song [We Are 138](https://www.youtube.com/watch?v=-n2Mkdw4q44) rocks!
|
||||
@ -13,11 +14,13 @@ An uppercase I looks like a 1, 3 from i3, and 8 because the song [We Are 138](ht
|
||||
|
||||
## Requirements
|
||||
|
||||
- i3-wm: The i3 window manager.
|
||||
- acpi: [optional] for battery status. It will still work even without this package, but uses it if it is installed.
|
||||
- dex: [optional] Alternative method for auto starting applications.
|
||||
- clipster: clipboard manager
|
||||
- grun: Run application dialog
|
||||
- jq: for getting the current workspace
|
||||
- libnotify: For sending notifications
|
||||
- lxsession: For policykit authentication agent.
|
||||
- notification-daemon: To handle notifications
|
||||
- ocrdesktop: For getting contents of the current window with OCR.
|
||||
- pamixer: for the mute-unmute script
|
||||
@ -26,11 +29,15 @@ An uppercase I looks like a 1, 3 from i3, and 8 because the song [We Are 138](ht
|
||||
- sgtk-menu: for applications menu
|
||||
- sox: for sounds.
|
||||
- transfersh: [optional] for file sharing GUI
|
||||
- udiskie: [optional] for automatically mounting removable storage
|
||||
- xclip: Clipboard support
|
||||
- xbacklight: [optional] for screen brightness adjustment
|
||||
- xorg-setxkbmap: [optional] for multiple keyboard layouts
|
||||
- yad: For screen reader accessible dialogs
|
||||
|
||||
I38 will try to detect your browser, file manager, and web browser and present you with a list of options to bind to their launch keys. It will also create bindings for pidgin and mumble if they are installed. To use the bindings, press your ratpoison mode key which is set when you run the i38.sh script. next, press the binding for the application you want, w for web browser, e for text editor, f for file manager, m for mumble, etc. To learn all the bindings, find and read the mode ratpoison section of ~/.config/i3/config.
|
||||
|
||||
|
||||
## Usage:
|
||||
|
||||
- With no arguments, create the i3 configuration.
|
||||
@ -38,3 +45,17 @@ I38 will try to detect your browser, file manager, and web browser and present y
|
||||
- -u: Copy over the latest version of scripts.
|
||||
- -x: Generate ~/.xinitrc and ~/.xprofile.
|
||||
- -X: Generate ~/.xprofile only.
|
||||
|
||||
|
||||
## Ratpoison Mode
|
||||
|
||||
I38 is an adaptation of the old Strychnine project which was based on the Ratpoison window manager. Ratpoison is a screen like window manager, and the important concept from that, which applies to I38, is adding keyboard shortcuts without conflicting application shortcuts. This is done with an "escape key".
|
||||
|
||||
When creating I38, I really wanted to port that functionality over, because it is very powerful and allows for lots and lots of shortcuts while minimizing collisions between shortcuts. So, for example, if you have chosen brave as your web browser, and selected alt+escape as your ratpoison mode key, you can quickly launch brave by pressing alt+escape followed by the letter w.
|
||||
|
||||
|
||||
## I38 Help
|
||||
|
||||
To get help for I38, you can press the top level keybinding alt+shift+F1. It is also available by pressing the ratpoison mode key followed by question mark. A limitation of yad, which is used to display the help text means that the cursor starts at the bottom of the text. Please press control+home to jump to the top. You can navigate with the arrow keys, and use control+f to find text within the document.
|
||||
|
||||
The help text is a modified version of the configuration file itself that is intended to be easier to read. I have tried to add in comments that should also serve to make things more clear.
|
||||
|
328
i38.sh
328
i38.sh
@ -5,11 +5,13 @@
|
||||
# Released under the terms of the WTFPL http://www.wtfpl.net
|
||||
|
||||
i3Path="${XDG_CONFIG_HOME:-$HOME/.config}/i3"
|
||||
i3msg="i3-msg"
|
||||
sensibleTerminal="i3-sensible-terminal"
|
||||
# Dialog accessibility
|
||||
export DIALOGOPTS='--no-lines --visit-items'
|
||||
|
||||
# Check to make sure minimum requirements are installed.
|
||||
for i in dialog grun jq sgtk-menu yad ; do
|
||||
for i in dialog jq sgtk-menu yad ; do
|
||||
if ! command -v "$i" &> /dev/null ; then
|
||||
missing+=("$i")
|
||||
fi
|
||||
@ -23,6 +25,105 @@ if [[ -n "${missing}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
keyboard_menu() {
|
||||
keyboardMenu=("us" "USA"
|
||||
"ad" "Andorra"
|
||||
"af" "Afghanistan"
|
||||
"ara" "Arabic"
|
||||
"al" "Albania"
|
||||
"am" "Armenia"
|
||||
"az" "Azerbaijan"
|
||||
"by" "Belarus"
|
||||
"be" "Belgium"
|
||||
"bd" "Bangladesh"
|
||||
"in" "India"
|
||||
"ba" "Bosnia and Herzegovina"
|
||||
"br" "Brazil"
|
||||
"bg" "Bulgaria"
|
||||
"ma" "Morocco"
|
||||
"mm" "Myanmar"
|
||||
"ca" "Canada"
|
||||
"cd" "Congo, Democratic Republic of the"
|
||||
"cn" "China"
|
||||
"hr" "Croatia"
|
||||
"cz" "Czechia"
|
||||
"dk" "Denmark"
|
||||
"nl" "Netherlands"
|
||||
"bt" "Bhutan"
|
||||
"ee" "Estonia"
|
||||
"ir" "Iran"
|
||||
"iq" "Iraq"
|
||||
"fo" "Faroe Islands"
|
||||
"fi" "Finland"
|
||||
"fr" "France"
|
||||
"gh" "Ghana"
|
||||
"gn" "Guinea"
|
||||
"ge" "Georgia"
|
||||
"de" "Germany"
|
||||
"gr" "Greece"
|
||||
"hu" "Hungary"
|
||||
"is" "Iceland"
|
||||
"il" "Israel"
|
||||
"it" "Italy"
|
||||
"jp" "Japan"
|
||||
"kg" "Kyrgyzstan"
|
||||
"kh" "Cambodia"
|
||||
"kz" "Kazakhstan"
|
||||
"la" "Laos"
|
||||
"latam" "Latin American"
|
||||
"lt" "Lithuania"
|
||||
"lv" "Latvia"
|
||||
"mao" "Maori"
|
||||
"me" "Montenegro"
|
||||
"mk" "Macedonia"
|
||||
"mt" "Malta"
|
||||
"mn" "Mongolia"
|
||||
"no" "Norway"
|
||||
"pl" "Poland"
|
||||
"pt" "Portugal"
|
||||
"ro" "Romania"
|
||||
"ru" "Russia"
|
||||
"rs" "Serbia"
|
||||
"si" "Slovenia"
|
||||
"sk" "Slovakia"
|
||||
"es" "Spain"
|
||||
"se" "Sweden"
|
||||
"ch" "Switzerland"
|
||||
"sy" "Syria"
|
||||
"tj" "Tajikistan"
|
||||
"lk" "Sri Lanka"
|
||||
"th" "Thailand"
|
||||
"tr" "Turkey"
|
||||
"tw" "Taiwan"
|
||||
"ua" "Ukraine"
|
||||
"gb" "United Kingdom"
|
||||
"uz" "Uzbekistan"
|
||||
"vn" "Vietnam"
|
||||
"kr" "Korea, Republic of"
|
||||
"nec_vndr/jp" "Japan (PC-98xx Series)"
|
||||
"ie" "Ireland"
|
||||
"pk" "Pakistan"
|
||||
"mv" "Maldives"
|
||||
"za" "South Africa"
|
||||
"epo" "Esperanto"
|
||||
"np" "Nepal"
|
||||
"ng" "Nigeria"
|
||||
"et" "Ethiopia"
|
||||
"sn" "Senegal"
|
||||
"brai" "Braille"
|
||||
"tm" "Turkmenistan"
|
||||
"ml" "Mali"
|
||||
"tz" "Tanzania"
|
||||
)
|
||||
dialog --title "I38" \
|
||||
--backtitle "Use the arrow keys to find the option you want, and enter to select it. When you are finished selecting layouts, use right arrow to find \"Done\" and press enter." \
|
||||
--clear \
|
||||
--cancel-label "Done" \
|
||||
--no-tags \
|
||||
--menu "Select Keyboard Layout" 0 0 0 "${keyboardMenu[@]}" --stdout
|
||||
return $?
|
||||
}
|
||||
|
||||
menulist() {
|
||||
# Args: List of items for menu.
|
||||
# returns: selected tag
|
||||
@ -36,7 +137,7 @@ menulist() {
|
||||
--backtitle "Use the arrow keys to find the option you want, and enter to select it." \
|
||||
--clear \
|
||||
--no-tags \
|
||||
--menu "$menuText" 0 0 0 ${menuList[@]} --stdout
|
||||
--menu "$menuText" 0 0 0 "${menuList[@]}" --stdout
|
||||
return $?
|
||||
}
|
||||
|
||||
@ -131,6 +232,7 @@ update_scripts() {
|
||||
# Array of command line arguments
|
||||
declare -A command=(
|
||||
[h]="This help screen."
|
||||
[s]="Create sway configuration instead of i3."
|
||||
[u]="Copy over the latest version of scripts."
|
||||
[x]="Generate ~/.xinitrc and ~/.xprofile."
|
||||
[X]="Generate ~/.xprofile only."
|
||||
@ -142,6 +244,11 @@ args="${args//[[:space:]]/}"
|
||||
while getopts "${args}" i ; do
|
||||
case "$i" in
|
||||
h) help;;
|
||||
s)
|
||||
i3msg="swaymsg"
|
||||
i3Path="${XDG_CONFIG_HOME:-$HOME/.config}/sway"
|
||||
sensibleTerminal="sway --sensible-terminal"
|
||||
;;
|
||||
u) update_scripts;;
|
||||
x) write_xinitrc ;&
|
||||
X) write_xprofile ;;
|
||||
@ -149,15 +256,14 @@ while getopts "${args}" i ; do
|
||||
done
|
||||
|
||||
# Configuration questions
|
||||
export i3Mode=$(yesno "Would you like to use ratpoison mode? This behaves more like strychnine, with an escape key followed by keybindings.")
|
||||
if [[ $i3Mode -eq 1 ]]; then
|
||||
mod="Mod4"
|
||||
fi
|
||||
export i3Mode=$(yesno "Would you like to use ratpoison mode? This behaves more like strychnine, with an escape key followed by keybindings. (Recommended)")
|
||||
# Prevent setting ratpoison mode key to the same as default mode key
|
||||
while [[ "$escapeKey" == "$mod" ]]; do
|
||||
escapeKey="$(menulist "Ratpoison mode key:" Control+t Control+z Control+Escape Alt+Escape Control+Space Super)"
|
||||
escapeKey="${escapeKey//Alt/Mod1}"
|
||||
escapeKey="${escapeKey//Super/Mod4}"
|
||||
if [[ $i3Mode -eq 0 ]]; then
|
||||
escapeKey="$(menulist "Ratpoison mode key:" Control+t Control+z Control+Escape Alt+Escape Control+Space Super)"
|
||||
escapeKey="${escapeKey//Alt/Mod1}"
|
||||
escapeKey="${escapeKey//Super/Mod4}"
|
||||
fi
|
||||
mod="$(menulist "I3 mod key, for top level bindings:" Alt Control Super)"
|
||||
mod="${mod//Alt/Mod1}"
|
||||
mod="${mod//Super/Mod4}"
|
||||
@ -165,11 +271,35 @@ while [[ "$escapeKey" == "$mod" ]]; do
|
||||
dialog --title "I38" --msgbox "Ratpoison and mod key cannot be the same key." -1 -1
|
||||
fi
|
||||
done
|
||||
# Multiple keyboard layouts
|
||||
if [[ $(yesno "Do you want to use multiple keyboard layouts?") -eq 0 ]]; then
|
||||
unset kbd
|
||||
while : ; do
|
||||
kbd+=("$(keyboard_menu)") || break
|
||||
done
|
||||
fi
|
||||
# Volume jump
|
||||
volumeJump=$(rangebox "How much should pressing the volume keys change the volume?" 1 15 5)
|
||||
# Email client
|
||||
unset programList
|
||||
for i in betterbird evolution thunderbird ; do
|
||||
if command -v ${i/#-/} &> /dev/null ; then
|
||||
if [ -n "$programList" ]; then
|
||||
programList="$programList $i"
|
||||
else
|
||||
programList="$i"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "$programList" != "${programList// /}" ]; then
|
||||
emailClient="$(menulist "Email client:" $programList)"
|
||||
else
|
||||
emailClient="${programList/#-/}"
|
||||
fi
|
||||
emailClient="$(command -v $emailClient)"
|
||||
# Web browser
|
||||
unset programList
|
||||
for i in brave chromium epiphany firefox google-chrome-stable midori seamonkey ; do
|
||||
for i in brave chromium epiphany firefox google-chrome-stable microsoft-edge microsoft-edge-beta microsoft-edge-dev midori seamonkey ; do
|
||||
if command -v ${i/#-/} &> /dev/null ; then
|
||||
if [ -n "$programList" ]; then
|
||||
programList="$programList $i"
|
||||
@ -186,7 +316,7 @@ fi
|
||||
webBrowser="$(command -v $webBrowser)"
|
||||
# Text editor
|
||||
unset programList
|
||||
for i in geany gedit kate kwrite l3afpad leafpad libreoffice mousepad pluma ; do
|
||||
for i in emacs geany gedit kate kwrite l3afpad leafpad libreoffice mousepad pluma ; do
|
||||
if hash ${i/#-/} &> /dev/null ; then
|
||||
if [ -n "$programList" ]; then
|
||||
programList="$programList $i"
|
||||
@ -219,6 +349,11 @@ else
|
||||
fileBrowser="${programList/#-/}"
|
||||
fi
|
||||
fileBrowser="$(command -v $fileBrowser)"
|
||||
# Auto mount removable media
|
||||
udiskie=1
|
||||
if command -v udiskie &> /dev/null ; then
|
||||
export udiskie=$(yesno "Would you like removable drives to automatically mount when plugged in?")
|
||||
fi
|
||||
# Auto start with dex
|
||||
dex=1
|
||||
if command -v dex &> /dev/null ; then
|
||||
@ -253,25 +388,26 @@ cat << EOF > ${i3Path}/config
|
||||
#
|
||||
# This config file uses keycodes (bindsym) and was written for the QWERTY
|
||||
# layout.
|
||||
#
|
||||
# To get a config file with the same key positions, but for your current
|
||||
# layout, use the i3-config-wizard
|
||||
#
|
||||
#set mod key
|
||||
|
||||
# set mod key
|
||||
set \$mod $mod
|
||||
|
||||
#set workspace layout to tapped so apps use most of the screen
|
||||
# set workspace layout to tabbed so apps use most of the screen
|
||||
workspace_layout tabbed
|
||||
|
||||
#set the mouse so it is trapped in focused window
|
||||
# set the mouse so it is trapped in focused window
|
||||
# this fixes some issues in some games that require focus and pause when focus is moved via mouse accidentally
|
||||
focus_follows_mouse no
|
||||
|
||||
# Font for window titles. Will also be used by the bar unless a different font
|
||||
# is used in the bar {} block below.
|
||||
font pango:monospace 8
|
||||
|
||||
# I38 help
|
||||
bindsym \$mod+Shift+F1 exec ${i3Path}/scripts/i38-help.sh
|
||||
|
||||
# Run dialog
|
||||
bindsym \$mod+F2 exec grun
|
||||
bindsym \$mod+F2 exec ${i3Path}/scripts/run_dialog.sh
|
||||
|
||||
# Clipboard manager
|
||||
bindsym \$mod+Control+c exec clipster -s
|
||||
@ -280,23 +416,23 @@ bindsym \$mod+Control+c exec clipster -s
|
||||
bindsym \$mod+Control+Delete exec --no-startup-id sgtk-bar
|
||||
|
||||
# Use pactl to adjust volume in PulseAudio.
|
||||
bindsym \$mod+XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}% & play -qnG synth 0.05 sin 440
|
||||
bindsym \$mod+XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${volumeJump}% & play -qnG synth 0.05 sin 440
|
||||
bindsym \$mod+XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}% & play -qnG synth 0.03 sin 440
|
||||
bindsym \$mod+XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${volumeJump}% & play -qnG synth 0.03 sin 440
|
||||
bindsym \$mod+XF86AudioMute exec --no-startup-id ${i3Path}/scripts/mute-unmute.sh
|
||||
|
||||
# Music player controls
|
||||
# Requires playerctl.
|
||||
bindsym XF86AudioRaiseVolume exec --no-startup-id ${i3Path}/scripts/music_controler.sh incvol $volumeJump
|
||||
bindsym XF86AudioLowerVolume exec --no-startup-id ${i3Path}/scripts/music_controler.sh decvol $volumeJump
|
||||
bindsym XF86AudioPrev exec --no-startup-id ${i3Path}/scripts/music_controler.sh prev
|
||||
bindsym XF86AudioMute exec --no-startup-id ${i3Path}/scripts/music_controler.sh pause
|
||||
bindsym XF86AudioPlay exec --no-startup-id ${i3Path}/scripts/music_controler.sh play
|
||||
bindsym \$mod+XF86AudioPlay exec --no-startup-id ${i3Path}/scripts/music_controler.sh info
|
||||
bindsym XF86AudioStop exec --no-startup-id ${i3Path}/scripts/music_controler.sh stop
|
||||
bindsym XF86AudioNext exec --no-startup-id ${i3Path}/scripts/music_controler.sh next
|
||||
bindsym XF86AudioPrev exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh prev
|
||||
bindsym XF86AudioMute exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh pause
|
||||
bindsym XF86AudioPlay exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh play
|
||||
bindsym \$mod+XF86AudioPlay exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh info
|
||||
bindsym XF86AudioStop exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh stop
|
||||
bindsym XF86AudioNext exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh next
|
||||
|
||||
# start a terminal
|
||||
bindsym \$mod+Return exec i3-sensible-terminal
|
||||
bindsym \$mod+Return exec $sensibleTerminal
|
||||
|
||||
# kill focused window
|
||||
bindsym \$mod+F4 kill
|
||||
@ -308,18 +444,13 @@ bindsym \$mod+F1 exec --no-startup-id sgtk-menu -f
|
||||
bindsym \$mod+Control+d exec --no-startup-id yad --icons --compact --no-buttons --title="Desktop" --close-on-unfocus --read-dir=${HOME}/Desktop
|
||||
|
||||
# change focus
|
||||
# alt+tab and shift tab
|
||||
# alt+tab and alt+shift+tab
|
||||
bindsym Mod1+Shift+Tab focus left
|
||||
bindsym Mod1+Tab focus right
|
||||
|
||||
# enter fullscreen mode for the focused container
|
||||
bindsym \$mod+BackSpace fullscreen toggle
|
||||
|
||||
# focus the parent container
|
||||
#bindsym \$mod+a focus parent
|
||||
|
||||
# focus the child container
|
||||
#bindsym \$mod+d focus child
|
||||
|
||||
# move the currently focused window to the scratchpad
|
||||
bindsym \$mod+Shift+minus move scratchpad
|
||||
@ -366,114 +497,101 @@ bindsym Control+Shift+F9 move container to workspace number \$ws9, exec spd-say
|
||||
bindsym Control+Shift+F10 move container to workspace number \$ws10, exec spd-say -P important -Cw "moved to workspace 10"
|
||||
|
||||
|
||||
# A mode that will pass all keys except $mod+shift+backspace to the current application.
|
||||
# Use $mod+shift+backspace to exit the mode.
|
||||
bindsym $mod+shift+BackSpace mode "bypass"
|
||||
mode "bypass" {
|
||||
# Exit bypass mode.
|
||||
bindsym $mod+Shift+BackSpace mode "default"
|
||||
}
|
||||
|
||||
|
||||
EOF
|
||||
|
||||
# Multiple keyboard layouts if requested.
|
||||
if [[ ${#kbd[@]} -gt 1 ]]; then
|
||||
echo "bindsym Mod4+space exec ${i3Path}/scripts/keyboard.sh cycle ${kbd[@]}" >> ${i3Path}/config
|
||||
fi
|
||||
|
||||
# Create ratpoison mode if requested.
|
||||
if [[ -n "${escapeKey}" ]]; then
|
||||
cat << EOF >> ${i3Path}/config
|
||||
bindsym $escapeKey mode "ratpoison"
|
||||
mode "ratpoison" {
|
||||
# I38 help bound to ?
|
||||
bindsym Shift+slash exec ${i3Path}/scripts/i38-help.sh, mode "default"
|
||||
# Terminal emulator bound to c
|
||||
bindsym c exec $sensibleTerminal, mode "default"
|
||||
# Text editor bound to e
|
||||
bindsym e exec $textEditor, mode "default"
|
||||
# File browser bound to f
|
||||
bindsym f exec $fileBrowser, mode "default"
|
||||
# Email client bound to \$mod+e
|
||||
bindsym \$mod+e exec $emailClient, mode "default"
|
||||
# Web browser bound to w
|
||||
bindsym w exec $webBrowser, mode "default"
|
||||
bindsym g exec ${i3Path}/scripts/game_controler.sh -s, mode "default"
|
||||
# Kill window bound to k
|
||||
bindsym k kill, mode "default"
|
||||
$(if command -v mumble &> /dev/null ; then
|
||||
echo "# Mumble bound to m"
|
||||
echo "bindsym m exec $(command -v mumble), mode \"default\""
|
||||
fi)
|
||||
$(if command -v ocrdesktop &> /dev/null ; then
|
||||
echo "bindsym Print exec $(command -v ocrdesktop), mode \"default\""
|
||||
echo "# OCR desktop bound to print screen alternative \$mod+r"
|
||||
echo "bindsym Print exec $(command -v ocrdesktop) -b, mode \"default\""
|
||||
echo "bindsym \$mod+r exec $(command -v ocrdesktop) -b, mode \"default\""
|
||||
fi)
|
||||
$(if command -v pidgin &> /dev/null ; then
|
||||
echo "# p bound to pidgin"
|
||||
echo "bindsym p exec $(command -v pidgin), mode \"default\""
|
||||
fi)
|
||||
$(if command -v xrandr &> /dev/null ; then
|
||||
echo "# alt+s bound to brightness control"
|
||||
echo "bindsym \$mod+s exec --no-startup-id ${i3Path}/scripts/screen_controller.sh, mode \"default\""
|
||||
fi)
|
||||
$(if command -v transfersh &> /dev/null ; then
|
||||
echo "# t bound to share file with transfer.sh"
|
||||
echo 'bindsym t exec bash -c '"'"'fileName="$(yad --title "I38 Upload File" --file)" && url="$(transfersh "${fileName}" | tee >(yad --title "I38 - Uploading ${fileName##*/} ..." --progress --pulsate --auto-close))" && echo "${url#*saved at: }" | tee >(yad --title "I38 - Upload URL" --show-cursor --show-uri --button yad-close --sticky --text-info) >(xclip -selection clipboard)'"', mode \"default\""
|
||||
fi)
|
||||
bindsym Mod1+Shift+0 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}%
|
||||
bindsym Mod1+Shift+9 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${voluemJump}%
|
||||
|
||||
#Keyboard based volume Controls with pulseaudio
|
||||
bindsym Mod1+Shift+0 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}% & play -qnG synth 0.03 sin 440
|
||||
bindsym Mod1+Shift+9 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${volumeJump}% & play -qnG synth 0.03 sin 440
|
||||
# Music player controls
|
||||
# Requires playerctl.
|
||||
bindsym Mod1+Shift+equal exec --no-startup-id ${i3Path}/scripts/music_controler.sh incvol $volumeJump, mode "default"
|
||||
bindsym Mod1+Shift+minus exec --no-startup-id ${i3Path}/scripts/music_controler.sh decvol $volumeJump, mode "default"
|
||||
bindsym Mod1+Shift+z exec --no-startup-id ${i3Path}/scripts/music_controler.sh prev, mode "default"
|
||||
bindsym Mod1+Shift+c exec --no-startup-id ${i3Path}/scripts/music_controler.sh pause, mode "default"
|
||||
bindsym Mod1+Shift+x exec --no-startup-id ${i3Path}/scripts/music_controler.sh play, mode "default"
|
||||
bindsym Mod1+Shitf+v exec --no-startup-id ${i3Path}/scripts/music_controler.sh stop, mode "default"
|
||||
bindsym Mod1+Shift+b exec --no-startup-id ${i3Path}/scripts/music_controler.sh next, mode "default"
|
||||
bindsym Mod1+Shift+u exec --no-startup-id ${i3Path}/scripts/music_controler.sh info, mode "default"
|
||||
bindsym Mod1+Shift+minus exec --no-startup-id ${i3Path}/scripts/music_controler.sh decvol $volumeJump, mode "default"
|
||||
bindsym Mod1+Shift+z exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh prev, mode "default"
|
||||
bindsym Mod1+Shift+c exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh pause, mode "default"
|
||||
bindsym Mod1+Shift+x exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh play, mode "default"
|
||||
bindsym Mod1+Shift+v exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh stop, mode "default"
|
||||
bindsym Mod1+Shift+b exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh next, mode "default"
|
||||
bindsym Mod1+Shift+u exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh info, mode "default"
|
||||
#Check battery status
|
||||
bindsym Mod1+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh, mode "default"
|
||||
#Check controller battery status
|
||||
bindsym g exec ${i3Path}/scripts/game_controler.sh -s, mode "default"
|
||||
# Get a list of windows in the current workspace
|
||||
bindsym apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh, mode "default"
|
||||
# Restart orca
|
||||
bindsym Shift+o exec $(command -v orca) --replace, mode "default"
|
||||
# reload the configuration file
|
||||
bindsym Control+semicolon exec bash -c 'i3-msg -t run_command reload && spd-say -P important -Cw "I38 Configuration reloaded."', mode "default"
|
||||
bindsym Control+semicolon exec bash -c '$i3msg -t run_command reload && spd-say -P important -Cw "I38 Configuration reloaded."', mode "default"
|
||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
bindsym Control+Shift+semicolon exec bash -c 'i3-msg -t run_command restart && spd-say -P important -Cw "I3 restarted."', mode "default"
|
||||
bindsym Control+Shift+semicolon exec bash -c '$i3msg -t run_command restart && spd-say -P important -Cw "I3 restarted."', mode "default"
|
||||
# Run dialog with exclamation
|
||||
bindsym Shift+exclam exec ${i3Path}/scripts/run_dialog.sh, mode "default"
|
||||
# exit i3 (logs you out of your X session)
|
||||
bindsym Control+q exec bash -c 'yad --image "dialog-question" --title "I38" --button=yes:0 --button=no:1 --text "You pressed the exit shortcut. Do you really want to exit i3? This will end your X session." && i3-msg -t run_command exit'
|
||||
bindsym Control+q exec bash -c 'yad --image "dialog-question" --title "I38" --button=yes:0 --button=no:1 --text "You pressed the exit shortcut. Do you really want to exit i3? This will end your X session." && $i3msg -t run_command exit'
|
||||
# Exit ratpoison mode without any action escape or Control+g
|
||||
bindsym Escape mode "default"
|
||||
bindsym Control+g mode "default"
|
||||
}
|
||||
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# For those who do not want ratpoison mode.
|
||||
if [[ -z "${escapeKey}" ]]; then
|
||||
cat << EOF >> ${i3Path}/config
|
||||
# Text editor bound to $mod+e
|
||||
bindsym \$mod+e exec $textEditor
|
||||
# File browser bound to $mod+f
|
||||
bindsym \$mod+f exec $fileBrowser
|
||||
# Web browser bound to $mod+w
|
||||
bindsym \$mod+w exec $webBrowser
|
||||
bindsym \$mod+g exec ${i3Path}/scripts/game_controler.sh -s, mode "default"
|
||||
$(if command -v mumble &> /dev/null ; then
|
||||
echo "bindsym \$mod+m exec $(command -v mumble)"
|
||||
fi)
|
||||
$(if command -v ocrdesktop &> /dev/null ; then
|
||||
echo "bindsym Print exec $(command -v ocrdesktop)"
|
||||
fi)
|
||||
$(if command -v pidgin &> /dev/null ; then
|
||||
echo "bindsym \$mod+p exec $(command -v pidgin)"
|
||||
fi)
|
||||
$(if command -v transfersh &> /dev/null ; then
|
||||
echo 'bindsym \$mod+t exec bash -c '"'"'fileName="$(yad --title "I38 Upload File" --file)" && url="$(transfersh "${fileName}" | tee >(yad --title "I38 - Uploading ${fileName##*/} ..." --progress --pulsate --auto-close))" && echo "${url#*saved at: }" | tee >(yad --title "I38 - Upload URL" --show-cursor --show-uri --button yad-close --sticky --text-info) >(xclip -selection clipboard)'"', mode \"default\""
|
||||
fi)
|
||||
bindsym \$mod+Shift+0 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}%
|
||||
bindsym \$mod+Shift+9 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${voluemJump}%
|
||||
# Music player controls
|
||||
# Requires playerctl.
|
||||
bindsym \$mod+Shift+equal exec --no-startup-id ${i3Path}/scripts/music_controler.sh incvol $volumeJump
|
||||
bindsym \$mod+Shift+minus exec --no-startup-id ${i3Path}/scripts/music_controler.sh decvol $volumeJump
|
||||
bindsym \$mod+Shift+z exec --no-startup-id ${i3Path}/scripts/music_controler.sh prev
|
||||
bindsym \$mod+Shift+c exec --no-startup-id ${i3Path}/scripts/music_controler.sh pause
|
||||
bindsym \$mod+Shift+x exec --no-startup-id ${i3Path}/scripts/music_controler.sh play
|
||||
bindsym \$mod+Shitf+v exec --no-startup-id ${i3Path}/scripts/music_controler.sh stop
|
||||
bindsym \$mod+Shift+b exec --no-startup-id ${i3Path}/scripts/music_controler.sh next
|
||||
bindsym \$mod+Shift+u exec --no-startup-id ${i3Path}/scripts/music_controler.sh info
|
||||
#Check battery status
|
||||
bindsym \$mod+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh
|
||||
# Get a list of windows in the current workspace
|
||||
bindsym \$mod+apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh
|
||||
# Restart orca
|
||||
bindsym \$mod+Shift+o exec $(command -v orca) --replace
|
||||
# reload the configuration file
|
||||
bindsym \$mod+Control+semicolon exec bash -c 'i3-msg -t run_command reload && spd-say -P important -Cw "I38 Configuration reloaded."'
|
||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
bindsym \$mod+Control+Shift+semicolon exec bash -c 'i3-msg -t run_command restart && spd-say -P important -Cw "I3 restarted."'
|
||||
# exit i3 (logs you out of your X session)
|
||||
bindsym \$mod+Control+q exec bash -c 'yad --image "dialog-question" --title "I38" --button=yes:0 --button=no:1 --text "You pressed the exit shortcut. Do you really want to exit i3? This will end your X session." && i3-msg -t run_command exit'
|
||||
}
|
||||
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat << EOF >> ${i3Path}/config
|
||||
# Auto start section
|
||||
@ -483,16 +601,26 @@ fi
|
||||
if [[ $brlapi -eq 0 ]]; then
|
||||
echo 'exec --no-startup-id xbrlapi --quiet'
|
||||
fi
|
||||
if [[ $udiskie -eq 0 ]]; then
|
||||
echo 'exec --no-startup-id udiskie'
|
||||
fi
|
||||
if command -v lxpolkit &> /dev/null ; then
|
||||
echo "exec_always --no-startup-id $(command -v lxpolkit)"
|
||||
fi
|
||||
if [[ -x "/usr/lib/notification-daemon-1.0/notification-daemon" ]]; then
|
||||
echo 'exec_always --no-startup-id /usr/lib/notification-daemon-1.0/notification-daemon -r'
|
||||
fi
|
||||
# Work around for weird Void Linux stuff.
|
||||
if [[ -x "/usr/libexec/notification-daemon" ]]; then
|
||||
echo 'exec_always --no-startup-id /usr/libexec/notification-daemon -r'
|
||||
fi
|
||||
if [[ $dex -eq 0 ]]; then
|
||||
echo '# Start XDG autostart .desktop files using dex. See also'
|
||||
echo '# https://wiki.archlinux.org/index.php/XDG_Autostart'
|
||||
echo 'exec --no-startup-id dex --autostart --environment i3'
|
||||
else
|
||||
echo '# Startup applications'
|
||||
echo 'exec clipster -d'
|
||||
echo 'exec --no-startup-id clipster -d'
|
||||
echo 'exec orca'
|
||||
fi)
|
||||
|
||||
|
@ -1,8 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
workSpace="$(i3-msg -t get_workspaces \
|
||||
| jq '.[] | select(.focused==true).name' \
|
||||
| cut -d"\"" -f2)"
|
||||
path="$(readlink -f $0)"
|
||||
path="${path%/*/*}"
|
||||
path="${path##*/}"
|
||||
if [[ "$path" == "i3" ]]; then
|
||||
workSpace="$(i3-msg -t get_workspaces \
|
||||
| jq '.[] | select(.focused==true).name' \
|
||||
| cut -d"\"" -f2)"
|
||||
else
|
||||
workSpace="$(swaymsg -t get_workspaces \
|
||||
| jq '.[] | select(.focused==true).name' \
|
||||
| cut -d"\"" -f2)"
|
||||
fi
|
||||
left=9
|
||||
right=0
|
||||
msg="Workspace ${workSpace}"
|
||||
|
32
scripts/i38-help.sh
Executable file
32
scripts/i38-help.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
configPath="$(readlink -f $0)"
|
||||
configPath="${configPath%/*/*}"
|
||||
|
||||
if [[ -f "${configPath}/config" ]]; then
|
||||
mod="$(grep 'set $mod ' "${configPath}/config" | cut -d ' ' -f3)"
|
||||
mod="${mod//Mod1/Alt}"
|
||||
mod="${mod//Mod4/Super}"
|
||||
mapfile helpText < <(sed -e '/set \($mod\|mod\)/d' \
|
||||
-e '/set $ws/d' \
|
||||
-e 's/bindsym/Key:/g' \
|
||||
-e 's/Mod1/Alt/g' \
|
||||
-e 's/, mode "default"//g' \
|
||||
-e 's/--no-startup-id //g' \
|
||||
-e 's/$ws\([0-9]\)/\1/g' \
|
||||
-e 's/play \(.*\)& //g' \
|
||||
-e "s/\$mod/$mod/g" "${configPath}/config")
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
for i in "${!helpText[@]}" ; do
|
||||
helpText[$i]="${helpText[$i]//${configPath}\/scripts\//}"
|
||||
helpText[$i]="${helpText[$i]/.sh/}"
|
||||
helpText[$i]="${helpText[$i]/, exec announce*/$'\n'}"
|
||||
helpText[$i]="${helpText[$i]/, exec spd-say*/$'\n'}"
|
||||
done
|
||||
helpText+=("End of text. Please press Control+Home to jump to the beginning of this document.")
|
||||
echo "${helpText[@]}" | yad --text-info --show-cursor --title "I38 help" --button "Close:0" --listen
|
||||
|
||||
exit 0
|
48
scripts/keyboard.sh
Executable file
48
scripts/keyboard.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
# This script is a modified version of i3-keyboard-layout.
|
||||
# Originally Copyright (c) 2018 Sergio Gil.
|
||||
# https://github.com/porras/i3-keyboard-layout
|
||||
# This modified script, like the rest of I38, is released under the WTFPL license.
|
||||
# The original work was released under the MIT license.
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
get_kbdlayout() {
|
||||
layout=$(setxkbmap -query | grep -oP 'layout:\s*\K([\w,]+)')
|
||||
variant=$(setxkbmap -query | grep -oP 'variant:\s*\K(\w+)')
|
||||
echo "$layout" "$variant"
|
||||
}
|
||||
|
||||
set_kbdlayout() {
|
||||
eval "array=($1)"
|
||||
setxkbmap "${array[@]}" &&
|
||||
spd-say -P important -Cw "${array[@]}"
|
||||
}
|
||||
|
||||
cycle() {
|
||||
current_layout=$(get_kbdlayout | xargs)
|
||||
layouts=("$@" "$1") # add the first one at the end so that it cycles
|
||||
index=0
|
||||
while [ "${layouts[$index]}" != "$current_layout" ] && [ $index -lt "${#layouts[@]}" ]; do index=$[index +1]; done
|
||||
next_index=$[index +1]
|
||||
next_layout=${layouts[$next_index]}
|
||||
set_kbdlayout "$next_layout"
|
||||
}
|
||||
|
||||
|
||||
subcommand="$1"
|
||||
shift || exit 1
|
||||
|
||||
case $subcommand in
|
||||
"get")
|
||||
echo -n $(get_kbdlayout)
|
||||
;;
|
||||
"cycle")
|
||||
cycle "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
45
scripts/run_dialog.sh
Executable file
45
scripts/run_dialog.sh
Executable file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
write_history(){
|
||||
oldHistory="$(grep -v "$txt" "${historyPath}/.history" | head -n 49)"
|
||||
echo -e "$txt\n$oldHistory" | sed 's/^$//g' > "${historyPath}/.history"
|
||||
}
|
||||
|
||||
|
||||
historyPath="$(readlink -f $0)"
|
||||
historyPath="${historyPath%/*}"
|
||||
if ! [[ -d "$historyPath" ]]; then
|
||||
mkdir -p "$historyPath"
|
||||
fi
|
||||
|
||||
if [[ -f "${historyPath}/.history" ]]; then
|
||||
txt="$(yad --entry --editable --title "I38" --text "Execute program or enter file" --button "Open:0" --separator "\n" --rest "${historyPath}/.history")"
|
||||
else
|
||||
txt="$(yad --entry --title "I38" --text "Execute program or enter file" --button "Open:0")"
|
||||
fi
|
||||
if [[ -z "$txt" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$txt" =~ ^ftp://|^http://|^https://|^www.* ]]; then
|
||||
xdg-open $txt
|
||||
write_history
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$txt" =~ ^mailto://.* ]]; then
|
||||
xdg-email $txt
|
||||
write_history
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$txt" =~ ^man://.* ]]; then
|
||||
eval "${txt/:\/\// }" | yad --text-info --show-cursor --button "Close:0" --title "I38" -
|
||||
write_history
|
||||
exit 0
|
||||
fi
|
||||
if command -v "$(echo "$txt" | cut -d " " -f1)" &> /dev/null ; then
|
||||
eval $txt&
|
||||
else
|
||||
xdg-open $txt&
|
||||
fi
|
||||
write_history
|
||||
exit 0
|
24
scripts/screen_controller.sh
Executable file
24
scripts/screen_controller.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Not for multiple screens.
|
||||
# Get the name of the screen.
|
||||
screenName="$(xrandr --query | grep "connected" | cut -d ' ' -f1 | head -n 1)"
|
||||
|
||||
menuOptions=(
|
||||
"1.0" "Maximum Brightness"
|
||||
"0.75" "75 percent"
|
||||
"0.5" "50 percent"
|
||||
"0.25" "25 percent"
|
||||
"0" "Screen Curtain"
|
||||
)
|
||||
|
||||
brightness="$(yad --list --title "I38" --text "Set Screen Brightness" --columns 2 --hide-column 1 --column "" --column "Select" "${menuOptions[@]}")"
|
||||
|
||||
if [[ ${#brightness} -lt 1 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
xrandr --output ${screenName} --brightness ${brightness%%|*} &&
|
||||
spd-say -P important -Cw "Screen set to ${brightness#*|}."
|
||||
|
||||
exit 0
|
@ -14,10 +14,14 @@ def on_new_window(self,i3):
|
||||
|
||||
def on_mode(self,event):
|
||||
mode= event.change
|
||||
if mode == 'default':
|
||||
if mode == 'ratpoison':
|
||||
system('play -qV0 "|sox -np synth .07 sq 400" "|sox -np synth .5 sq 800" fade h 0 .5 .5 norm -20 &')
|
||||
elif mode == 'bypass':
|
||||
system('play -nqV0 synth .1 saw 700 saw 1200 delay 0 .04 remix - norm -6')
|
||||
elif mode == 'default':
|
||||
system('play -qV0 "|sox -np synth .07 sq 400" "|sox -np synth .5 sq 800" fade h 0 .5 .5 norm -20 reverse &')
|
||||
else:
|
||||
system('play -qV0 "|sox -np synth .07 sq 400" "|sox -np synth .5 sq 800" fade h 0 .5 .5 norm -20 &')
|
||||
system('play -n synth 0.05 pluck F3 norm -8 : synth 0.05 pluck C4 norm -8 : synth 0.05 pluck F4 norm -8 : synth 0.05 pluck C5 norm -8')
|
||||
|
||||
def on_workspace_focus(self,i3):
|
||||
#system('play -qnV0 synth pi fade 0 .25 .15 pad 0 1 reverb overdrive riaa norm -8 speed 1 &')
|
||||
|
@ -1,5 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
path="$(readlink -f $0)"
|
||||
path="${path%/*/*}"
|
||||
path="${path##*/}"
|
||||
if [[ "$path" == "i3" ]]; then
|
||||
mapfile -t windowList < <(python3 -c '
|
||||
import i3ipc
|
||||
|
||||
@ -13,4 +17,28 @@ id="$(yad --title "I38" --list --separator "" --column "id" --column "Select Win
|
||||
if [[ -z "${id}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
i3-msg \[id="${id}"\] focus
|
||||
i3-msg \[id="${id}"\] focus
|
||||
else
|
||||
mapfile -t windowList < <(python3 -c '
|
||||
import i3ipc
|
||||
|
||||
i3 = i3ipc.Connection()
|
||||
|
||||
for con in i3.get_tree():
|
||||
if con.window or con.type == "con":
|
||||
if con.name:
|
||||
print(con.window)
|
||||
print(con.name)')
|
||||
|
||||
# Remove the first entry if it is "none"
|
||||
if [[ "${windowList[0]}" == "none" ]]; then
|
||||
unset "windowList[0]"
|
||||
fi
|
||||
|
||||
id="$(yad --title "I38" --list --separator "" --column "id" --column "Select Window" --hide-column 1 --print-column 1 "${windowList[@]}")"
|
||||
|
||||
if [[ -z "${id}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
swaymsg \[id="${id}"\] focus
|
||||
fi
|
||||
|
Reference in New Issue
Block a user