Last active
August 29, 2015 14:16
-
-
Save altercation/95eca890de27e8d1c983 to your computer and use it in GitHub Desktop.
Contextual workspace-resolution aware browser launcher for i3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
$* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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