Thursday, September 11, 2014

Selectively Invert workspaces for readability

UPDATE October 2019: The script has been updated for multirow operation, better window identification, and reduced execution time.

In my recent experiences dealing with the rapid progression of cataracts and vision loss, I had to make a lot of adjustments to my computing environment to accommodate for the diffusion.  In my sight, bright regions in the field of view bleed over dark areas and obscure them extremely effectively.  The diffusion averages the brightness of the entire visual field, nearly eliminating local contrast.  This pretty much means that the thin black lines of text on a white background disappear.  White characters on a dark background bleed as well, but the impact on readability is much smaller.

OH GOD THE BRIGHTNESS

I do understand that other individuals with cataracts claim different experiences regarding contrast preference; perhaps it is the differences in the types of cataract structure that explains those things.  Regardless, my overall goal is to control bright areas of my workspace and enforce a relatively high contrast light-on-dark regime. 

This new quest covered the normal bases:
  • GTK 2/3 theme
  • QT4 color settings
  • Desktop wallpaper
  • Custom user styles for Firefox/Stylish
... but still, there are things that can't be properly themed.  Wine applications, as well as certain things like virtual machines and Matlab/Simulink all appear as horrible fuzzy bright rectangles of pain.  What the hell does one do about those?

If i could invert an individual window's colors, that would be sufficient.  The only method i can recall to do this is with Compiz, and that's not going to happen for various reasons.  Under Mint 14/XFCE, the only thing i could think to do is invert the entire X display.  Since i normally run these offending applications maximized, the amount of remaining bright area is mostly restricted to the xfce4-panel area at the top.  Inverting the entire display can be done with xcalib, but unless one wants to get blinded every time the workspace is switched, the application of the screen inversion should be automated.

#!/bin/bash

# This is an ad-hoc replacement for wm-specific workspace switching (ctrl-alt-left, ctrl-alt-right, ctrl-alt-number)
# Script conditionally inverts X display on workspaces containing offending windows
# Helps to enforce dark, hi-contrast UI despite unthemeable windows (virtual machines, wine apps, etc)
# Works best when applications are run full-screen
# directional usage: switchworkspace left|right|up|down
# explicit usage: switchworkspace workspacenumber

# this version is about 5% faster than the old 1-D version
# and does not invoke gamma enforcement and inversion checks at endpoints

# specify the workspace/pager layout
nwsx=3 # number of columns
nwsy=3 # number of rows


current=$(wmctrl -d | sed -n 's/^\([0-9]\+\) *\*.*/\1/p')

if [ $1 ]; then
 ex='^[0-9]+$'
 if ! [[ $1 =~ $ex ]]; then
  # argument is not a number
  if [ $1 == "right" ] || [ $1 == "left" ]; then
   if [ $1 == "right" ]; then
    if [ $(($current % $nwsx)) == $(($nwsx-1)) ]; then
     exit
    else
     target=$(($current+1))
    fi
   elif [ $1 == "left" ]; then
    if [ $(($current % $nwsx)) == 0 ]; then
     exit
    else
     target=$(($current-1))
    fi
   fi
  elif [ $1 == "up" ] || [ $1 == "down" ]; then
   if [ $1 == "down" ]; then
    if [ $(($current / $nwsx)) == $(($nwsy-1)) ]; then
     exit
    else
     target=$(($current+$nwsx))
    fi
   elif [ $1 == "up" ]; then
    if [ $(($current / $nwsx)) == 0 ]; then
     exit
    else
     target=$(($current-$nwsx))
    fi
   fi
  else
   echo "unknown direction"
   exit
  fi
 else
  # argument is numeric
  total=$(wmctrl -d | wc -l)
  if [ $1 -gt $total ]; then
   target=$(($total-1))
  elif [ $1 -lt 1 ]; then
   target=0
  else
   target=$(($1-1))
  fi
  
  if [ $current == $target ]; then exit; fi
 fi 
else
 echo "must specify a workspace number or direction (right/left/up/down)"
 exit
fi


# add other applications by window title keyword or window class
# if multiple windows match, all corresponding desktops will be affected
winlist=$(wmctrl -lx)
inv[0]=$(echo "$winlist" | grep "XFramePeer.com-mathworks-util-PostVMInit" | cut -d \  -f 3)
inv[1]=$(echo "$winlist" | grep "XFramePeer.MATLAB" | cut -d \  -f 3)
inv[2]=$(echo "$winlist" | grep "eagle.exe." | cut -d \  -f 3)
inv[3]=$(echo "$winlist" | grep "scad3.exe." | cut -d \  -f 3)
inv[4]=$(echo "$winlist" | grep "femm.exe." | cut -d \  -f 3)


function contains {
 case "${inv[@]}" in  *"$1"*) 
  echo 1 
  return 1 ;; 
 esac
 echo 0
}

wmctrl -s $target

A=$(contains $target)
B=$(contains $current)

#echo $current $target $A $B

if [ $A == 1 ] && [ $B != 1 ]; then

 xcalib -i -a
elif  [ $A == 1 ] && [ $B == 1 ]; then
 return
else
 xcalib -clear

 # i have gamma presets for each of my two monitors
 # issuing 'xcalib -clear' will reset the gamma to 1.00
 # so i'll need to reassert my preference depending on the active display
 # you might not need this
 # when running this from a terminal, $DISPLAY may be ":0" depending on what's happened during its session
 thisdisp=$(echo $DISPLAY)
 #echo $thisdisp
 if [ "$thisdisp" == ":0.0" ]; then
  xgamma -quiet -gamma 0.87
 else
  xgamma -quiet -gamma 1.2
 fi
fi


The script is relatively simple and a bit of effort did go into making it quick.  An array of workspace numbers is created by searching for specific WM_CLASS strings in the window list.  When switching workspaces with this script, the inversion state of the display is altered to correspond to the workspace contents.

Two workspaces: Terminator (normal), Matlab/Simulink (inverted)

Just add appropriate window title keywords and set your preferred wrapping behavior in the script.  Reassign the appropriate keybindings (Ctrl-Alt-Left, Ctrl-Alt-Right) so they execute this script instead of the inbuilt window manager functions.  Script accepts one parameter, either "prev" or "next".

No comments:

Post a Comment