Skip to content

Instantly share code, notes, and snippets.

@altercation
Last active August 29, 2015 14:16
Show Gist options
  • Save altercation/95eca890de27e8d1c983 to your computer and use it in GitHub Desktop.
Save altercation/95eca890de27e8d1c983 to your computer and use it in GitHub Desktop.
Contextual workspace-resolution aware browser launcher for i3
#!/usr/bin/env zsh
{ eval "$(<${$(readlink -f $0):h}/functions/frontmatter)" } || :
xenvinit
# directly called modes
#
# external - external only
# internal - internal only
# scaled - internal scaled-mirror of external (presentation with scaled mirror for easy tablet use)
# span ext - span internal on left, external primary
# span int - span internal on bottom, internal primary (presentation w/separate internal display)
# span - use preferred span mode (span-int)
# mode selection functions
#
# cycle - Cycle sequentially
# auto - Pick based on current environment and heuristics
# menu - Dmenu based picker
# misc functions
#
# envinit - used by mode setting functions to detect environment
# sysinit - initialize script by creating udev rule & systemd suspend hook link
# TODO:
# identify if we are in xhost or xauth mode
# http://stackoverflow.com/questions/20395027/scripts-launched-from-udev-do-not-have-display-access-anymore
# ---------------------------------------------------------------------
# script values
# ---------------------------------------------------------------------
INTERNAL_DISPLAY=eDP1 # see note below
# ---------------------------------------------------------------------
# initializers
# ---------------------------------------------------------------------
scriptinit () {
runonce
eval 'typeset -ag ENV_ACTUAL; ENV_ACTUAL=("${(@f)$(xrandr --display '$DISPLAY' --query)}")'
ENV_ACTUAL=(${(s:OUTPUT :)${(j:MODELINE:)${${ENV_ACTUAL//(#m)(#s)[^[:blank:]]##/OUTPUT $MATCH}//(#s)[[:blank:]]/MODELINE}}})
typeset -ag ALL_DISPS; ALL_DISPS=(${${(M)ENV_ACTUAL:#*connected*}%% *})
typeset -ag ACT_DISPS; ACT_DISPS=(${${(M)ENV_ACTUAL:#*\**}%% *})
typeset -ag CON_DISPS; CON_DISPS=(${${(M)ENV_ACTUAL:#* connected*}%% *})
typeset -g INT_DISP; INT_DISP=$ALL_DISPS[1]
# I'm not convinced that xrandr always outputs the internal dispaly first in the list, so will keep this here for now as a safety check
[[ $INT_DISP != $INTERNAL_DISPLAY ]] && { notify-send "Internal display is unexpectedly $INT_DISP!"; INT_DISP=$INTERNAL_DISPLAY; }
typeset -ag EXT_DISPS; EXT_DISPS=(${CON_DISPS:#$INT_DISP})
typeset -ag EXC_DISPS; EXC_DISPS=(${(M)ALL_DISPS:#VIRTUAL*}) # exclusion of virtual displays
typeset -g EXT_DISP=${EXT_DISPS[1]:-}
typeset -g INT_RES=${(SM)${(M)ENV_ACTUAL:#(#s)$INT_DISP *}##[0-9]##x[0-9]##}
typeset -g EXT_RES=${(SM)${(M)ENV_ACTUAL:#(#s)$EXT_DISP *}##[0-9]##x[0-9]##}
eval 'typeset -g INT_PIX=$(( '${INT_RES//x/*}' ))'
eval 'typeset -g EXT_PIX=$(( '${EXT_RES//x/*}' ))'
typeset -g BIG_DISP; (( EXT_PIX > INT_PIX )) && BIG_DISP=$EXT_DISP || BIG_DISP=$INT_DISP
}
install () {
[[ $USER == root ]] || { print "run with sudo or as root when calling $SCRIPTNAME $0"; exit 1 }
rulepath=/etc/udev/rules.d/99-monitor-hotplug.rules
rule='ACTION=="change", SUBSYSTEM=="drm", ENV{HOTPLUG}=="1", RUN+="'$SCRIPTPATH' udev"'
linktarget=/usr/lib/systemd/system-sleep/$SCRIPTNAME
[[ -f $rulepath ]] && rm -f $rulepath
print $rule > $rulepath || { print "Failed to initialize udev rule '$rulepath'"; exit 1 }
[[ -h $linktarget ]] && rm -f $linktarget
ln -s $SCRIPTPATH $linktarget || { print "Failed to create link from $SCRIPTPATH to $linkname"; exit 1 }
print "Successfully initialized udev rule $rulepath and systemd hook link $linktarget"
udevadm control --reload
}
# ---------------------------------------------------------------------
# modes
# ---------------------------------------------------------------------
internal () {
SKIP_DISPS=($INT_DISP $EXC_DISPS)
xrandr --output $INT_DISP --auto --primary ${=${:-'--output '${^ALL_DISPS:|SKIP_DISPS}' --off'}}
save $0
$SCRIPTROOT/audiomode auto
}
external () {
[[ -n $EXT_DISP ]] || { notify-send "No external display connected."; exit 1 }
SKIP_DISPS=($EXT_DISP $EXC_DISPS)
xrandr --output $EXT_DISP --auto --primary ${=${:-'--output '${^ALL_DISPS:|SKIP_DISPS}' --off'}}
save $0
$SCRIPTROOT/audiomode auto
}
span () {
# span int - span internal on bottom, internal primary (presentation mode w/separate internal display)
# span ext - span internal on left, external primary (default span mode)
if [[ ${1:-} =~ ^int ]]
then
xrandr --output $INT_DISP --auto --primary --output $EXT_DISP --auto --above $INT_DISP
else
xrandr --output $INT_DISP --auto --output $EXT_DISP --auto --primary --right-of $INT_DISP
fi
save $0${1:-}
}
hdmi () {
# for ultrawide monitor via hdmi, not dp, we need to run at half refresh rate
xrandr --output eDP1 --off --output HDMI1 --mode 3440x1440 --rate 29.99 --primary
}
# ---------------------------------------------------------------------
# mode selectors
# ---------------------------------------------------------------------
reset () {
if [[ -n ${EXT_DISP} ]]
then
print "There is a connected external display: $EXT_DISP"
xrandr --output $INT_DISP --auto --output $EXT_DISP --auto
else
xrandr --output $INT_DISP --auto
fi
}
auto () {
[[ -n ${EXT_DISP:-} && $BIG_DISP == ${EXT_DISP:-} ]] && { external; exit 0 }
[[ -z ${EXT_DISP:-} ]] && { internal; exit 0 } # no external, just use internal
span # all other cases
}
menu () {
[[ -z $EXT_DISP ]] && { notify-send "no external display connected"; exit }
menu_modes=(auto external internal scaled span unchanged)
mode=$(print ${(F)menu_modes} | dmenu -p "Display mode")
[[ $mode == unchanged ]] && exit || $mode
}
cycle () {
cycle_modes=(external internal span)
[[ -n ${1} ]] && cycle_modes=(${(Oa)cycle_modes}) # eg cycle reverse
$cycle_modes[$((((${cycle_modes[(i)$(last)]})%(${#cycle_modes})+1)))]
}
# ---------------------------------------------------------------------
# read
# ---------------------------------------------------------------------
scaling () {
# take the x resolution of the primary display, get the x physical dimensions in
# milimeters, divide and divide by a factor that will result in a reasonable scaling
# factor for use in other applications.
# (e.g. with chrome, I use this with the --force-device-scale-factor to start chrome
# with a scaling factor appropriate for the primary display... not perfect but makes
# up for chrome's poor hidpi support)
typeset -F scaling_factor=4.6
typeset -a pri_disp; pri_disp=(${${(M)ENV_ACTUAL:#*primary*}%% *})
typeset pri_xres=${${(SM)${(M)ENV_ACTUAL:#(#s)$pri_disp *}##[0-9]##x}%x}
typeset -F pri_size=${${(SM)${(M)ENV_ACTUAL:#(#s)$pri_disp *}%% [0-9]##mm x}//[^0-9]/}
print ${(M)$((pri_xres/pri_size/scaling_factor))#*.?}
}
# ---------------------------------------------------------------------
# state
# ---------------------------------------------------------------------
save () {
# save mode used with current external display
print $1 > $SCRIPTCONF/last
[[ -n $EXT_DISP ]] && print $1 > $SCRIPTCONF/$EXT_DISP > $SCRIPTCONF/$EXT_RES
}
match () {
# read previous mode used with an external display matching values (right now just resolution)
[[ -f $SCRIPTCONF/$EXT_RES ]] && { < $SCRIPTCONF/$EXT_RES; return 0 }
[[ -f $SCRIPTCONF/$EXT_DISP ]] && { < $SCRIPTCONF/$EXT_DISP; return 0 }
}
last () {
[[ -f $SCRIPTCONF/last ]] && < $SCRIPTCONF/last || return 0
}
# ---------------------------------------------------------------------
# hooks
# ---------------------------------------------------------------------
systemd () {
# systemd will call this with two arguments:
# arg1: pre/post
# arg2: suspend/hibernate,hybrid-sleep
#print "$SCRIPTNAME SYSTEMD INCOMING EVENT: $* ($(date))" > /home/es/tmp/systemd
[[ $1 == post ]] && auto
}
udev () {
# reset
auto
}
# ---------------------------------------------------------------------
# main execution
# ---------------------------------------------------------------------
[[ ${1:-} =~ ^ext ]] && set -- external
[[ ${1:-} =~ ^int ]] && set -- internal
[[ ${1:-} == (pre|post) ]] && set -- systemd $*
[[ -z ${1:-} ]] && set -- auto
[[ ${1:-} == install ]] || scriptinit
$*
#!/usr/bin/env zsh
setopt EXTENDED_GLOB NO_UNSET
unsetopt NO_MATCH
# launch different browser profiles automatically on any named i3 workspace
# plain numbered workspaces (or those listed in DEFAULT_WS) launch the normal
# browser profile
#
# additionally, detect high-dpi mode and append high-dpi flag to command line for chrome
# list of names for google chrome executable or symlink. searches system for first found.
browsers=(google-chrome-unstable google-chrome chromium)
# command line defaults
# (currently just setting a reasonable hidpi scaling factor, auto generated)
CLI_OPTS="--force-device-scale-factor=$(~/bin/dispmode scaling)"
# first checks existing environment variable $BROWSER value to see if it matches an expected chromium or google-chrome
# otherwise checks for valid browser executable in $PATH
# checks for known google-chrome/chromium variations
if [[ -z ${(M)browsers:#${BROWSER:-}} ]]
then
for browser in $browsers; do [[ -n ${(M)${~:-=$browser}#=} ]] || { BROWSER=$browser; break }; done
fi
# workspace names that are "disregarded" and treated like normal, numbered
# workspaces (and thus use the default browser profile, whate're that may be)
DEFAULT_WS=(es coms hak av)
# command line options to apply on default workspaces
DEFAULT_OPTS=()
# always use incognito profile on the following spaces
INCOGNITO_WS=(test dev)
# get current name of workspace, lowercased and stripped of non alpha chars
WS=${${(SL)$(i3-msg -t get_workspaces)//(#b)(#s)*name\":\"([[:alnum:]:]##)*focused\":true*(#e)/$match}//[^[:alpha:]]/}
# filter out named spaces that we want to use the default profile on (no --user-data-dir option will be used for these workspaces)
WS_FILTERED=${WS:|DEFAULT_WS}
# browser profile config data directory root (XDG config directory or defaults to $HOME/.config
CONFIG_DIR=${XDG_CONFIG_HOME:-${HOME}/.config}
# browser command
$BROWSER $CLI_OPTS ${*} ${${${WS:*INCOGNITO_WS}:+--incognito}:-${${WS_FILTERED:+--user-data-dir=${CONFIG_DIR}/${BROWSER}_$WS_FILTERED}:-${DEFAULT_OPTS:-}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment