#!/bin/bash
# Compiz manager
# Copyright (c) 2007 Kristian Lyngstøl <kristian@bohemians.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
#
# Much of this code is based on Beryl code, also licensed under the GPL.
# This script will detect what options we need to pass to compiz to get it
# started, read a simple configuration file, and start a default
# plugin and possibly window decorator.
# All of this should be possible to override in a configuration file.
# Todo:
# - GUI, possibly in a second script.
# - Testing on Xgl
# - Configuration file sanity tests
#########################################################################
# You should NOT edit this, edit the configuration file instead. #
# This is left for completness and if you need to modify the script. #
# The generated configuration file should be equally documented. #
#########################################################################
# Defaults
# Configuration file is based on XDG base dir. Set this to override the
# defaults. Leave empty if in doubt. Use -v to see exactly what
# configuration files compiz-manager looks for on your system.
#CONFIG="./compiz-managerrc"
# Set to yes to enable verbose (-v) by default.
VERBOSE="no"
# Default arguments. Others are added to this, and the configuration can
# override ALL arguments.
ARGS="--sm-disable --replace"
# Ditto for enviromental variables.
ENV=""
# Default plugins. Should probably be ini, gconf or ccp.
PLUGINS="ccp"
# Defines the decorator and arguments.
# Set it to empty to not use a decorator.
DECORATOR="emerald"
DECORATORARGS="--replace"
# Delay in seconds before we bring up the decorator(s)
# This avoids starting the decorator before the WM is up,
# even if it shouldn't be a problem.
DELAY="5"
# Set to "no" to pipe all decorator error messages to /dev/null
DECOERRORS="no"
# Internal, used to process options.
TASK="normal"
# No indirect by default
INDIRECT=1
# Echos the arguments if verbose
function verbose
{
if [ "x$VERBOSE" = "xyes" ]; then
echo -ne "$*"
fi
}
### System checks
# These are used for checking what hardware and software we're dealing with,
# so we can decide what options to pass to compiz, if it's even possible to
# start compiz.
# Check wether the composite extension is present
function check_composite
{
verbose "Checking for Composite extension: "
if xdpyinfo -queryExtensions | grep -q Composite ; then
verbose "present. \n";
return 0;
else
verbose "not present. \n";
return 1;
fi
}
function check_xdamage
{
verbose "Checking for XDamage extension: "
if xdpyinfo -queryExtensions | grep -q DAMAGE ; then
verbose "present. \n";
return 0;
else
verbose "not present. \n";
return 1;
fi
}
# Check for existence if NV-GLX
function check_nvidia
{
verbose "Checking for nVidia: "
if xdpyinfo | grep -q NV-GLX ; then
verbose "present. \n"
return 0;
else
verbose "not present. \n"
return 1;
fi
}
# Detects if Xgl is running
function check_xgl
{
verbose "Checking for Xgl: "
if xvinfo | grep -q Xgl ; then
verbose "present. \n"
return 0;
else
verbose "not present. \n"
return 1;
fi
}
# Check for presence of FBConfig
function check_fbconfig
{
verbose "Checking for FBConfig: "
if glxinfo 2> /dev/null | grep -q GLX_SGIX_fbconfig ; then
verbose "present. \n"
return 0;
else
verbose "not present. \n"
return 1;
fi
}
# Check for TFP
function check_tfp
{
verbose "Checking for texture_from_pixmap: "
if [ `glxinfo 2>/dev/null | grep GLX_EXT_texture_from_pixmap -c` -gt 2 ] ; then
verbose "present. \n"
return 0;
else
verbose "not present. \n"
if [ "$INDIRECT" -eq 0 ]; then
unset LIBGL_ALWAYS_INDIRECT
INDIRECT=1
return 1;
else
verbose "Trying again with indirect rendering:\n";
INDIRECT=0
export LIBGL_ALWAYS_INDIRECT=1
check_tfp;
return $?
fi
fi
}
# Check for non power of two texture support
function check_npot_texture
{
verbose "Checking for non power of two support: "
if glxinfo | egrep -q '(GL_ARB_texture_non_power_of_two|GL_NV_texture_rectangle|GL_EXT_texture_rectangle|GL_ARB_texture_rectangle)' ; then
verbose "present. \n";
return 0;
else
verbose "Not present. \n"
return 1;
fi
}
function check_xsync
{
verbose "Checking for XSync extension: ";
if xdpyinfo -queryExtensions | grep -q SYNC ; then
verbose "present. \n";
return 0;
else
verbose "not present. \n" ;
fi
}
# Counts how many screens we have, and the base value for DISPLAY=
# so we can easily start one decorator per screen
function check_multiscreen
{
SCREENS=$(xdpyinfo | grep "screen #" | wc -l)
verbose "Detected $SCREENS screen(s)\n";
if [ "$SCREENS" == "1" ]; then return 0; fi;
verbose "Multiscreen enviromental detection: \n"
DISPLAYBASE=$(xdpyinfo | grep name\ of\ display | sed 's/.* display: *//' | sed 's/\..*//')
verbose "\tDetected $DISPLAYBASE as the base of the DISPLAY variable\n";
SCREENNUMBERS=$(xdpyinfo | grep "screen #" | sed -r 's/screen #(.):/\1/')
for a in $SCREENNUMBERS ; do
MULTIDISPLAY[$a]=${DISPLAYBASE}.$a
verbose "\tMULTIDISPLAY[$a] set to: ${MULTIDISPLAY[$a]}\n";
done
}
function possible_check
{
if [ ! "$1" ]; then
echo "Fatal: Failed test: $2";
return 1;
fi
return 0;
}
# Returns true if we think it's actually possible to start compiz
function check_possible
{
POSSIBLE="1"
if ! possible_check "$TFP" "texture_from_pixmap support"; then return 1; fi
if ! possible_check "$NPOT" "non-power-of-two texture support"; then return 1; fi
if ! possible_check "$FBCONFIG" "FBConfig"; then return 1; fi
if ! possible_check "$COMPOSITE" "Composite extension"; then return 1; fi
if ! possible_check "$XDAMAGE" "XDamage extension"; then return 1; fi
if ! possible_check "$XSYNC" "XSync extension"; then return 1; fi
POSSIBLE="0";
return 0;
}
### Work functions
# Builds a new-line seperated string of enviromental variables we might want
function build_env
{
if [ $NVIDIA -eq 0 ]; then
ENV="__GL_YIELD=NOTHING "
fi
if [ $INDIRECT -eq 0 ]; then
ENV="$ENV LIBGL_ALWAYS_INDIRECT=1 "
fi
}
# Builds the argument list
function build_args
{
if [ $NVIDIA -eq 0 -a $XGL -ne 0 -a $INDIRECT -ne 0 ]; then
ARGS="--loose-binding "$ARGS
fi
if [ $INDIRECT -eq 0 ]; then
ARGS="--indirect-rendering "$ARGS
fi
}
# Prints usage
function usage
{
echo "Usage: $0 [-r <env|args>] [-v] [-h] [-i] [-f] [-d] [-w]"
echo -e "-r\toutputs recommended values for either "
echo -e " \tenviromental variables, or arguments."
echo -e "-v\tVerbose: Output the result of each individual test"
echo -e "-h\tDisplay this message";
echo -e "-i\tIgnore config file(s)";
echo -e "-f\tForce; This overwrites your existing config file."
echo -e "-d\tDry run: Do everything, but don't start."
echo -e "-w\tOnly start window decorator(s). One per screen.";
echo -e "Configuration"
echo -e "$0 automatically stores configuration the first time you run it.";
echo -e "You can use that to override checks, or pass custom arguments."
echo -e "To re-write the configuration, you can either use -f, to get one"
echo -e "based on your own settings, or -fi to create a fresh config."
}
# Parses options
function parse_options
{
while getopts "r:vhifdw" ARG
do
if [ "x$ARG" = "xr" ]; then
TASK="RECOMMEND";
if [ "x$OPTARG" = "xenv" ]; then
REC="env";
elif [ "x$OPTARG" = "xargs" ]; then
REC="args"
elif [ "x$OPTARG" = "xboth" ]; then
REC="both"
else
echo "Invalid recommend argument"
usage
exit 1
fi
elif [ "x$ARG" = "xv" ]; then
VERBOSE="yes"
elif [ "x$ARG" = "xi" ]; then
no_config
IGNORECONFIG="yes"
elif [ "x$ARG" = "xd" ]; then
DRY="yes"
elif [ "x$ARG" = "xf" ]; then
FORCE="yes"
FORCECONFIG="yes"
elif [ "x$ARG" = "xw" ]; then
TASK="WINDOWDECORATOR";
else
usage
exit 0
fi
done
}
# Store configuration
function store_config
{
if [ -n "$IGNORECONFIG" ]; then
if [ "x$FORCECONFIG" != "xyes" ]; then return; fi;
fi
if [ -z $CONFIG ]; then return ; fi
if [ -f $CONFIG ]; then
if [ "x$FORCECONFIG" != "xyes" ]; then
verbose "Not writing config; allready exists.\n";
return 1;
fi;
fi;
echo Writing configuration to: $CONFIG
echo "# Autogenerated configuration" > $CONFIG
echo "# Generated:" `date` >> $CONFIG
echo "# On $HOSTNAME by $USER" >> $CONFIG
echo >> $CONFIG
echo "# Behavior references: (yes/no)" >> $CONFIG
echo "# Set this to \"yes\" to get the same result as if you ran compiz-manager with -v" >> $CONFIG
echo "#VERBOSE=$VERBOSE" >> $CONFIG
echo >> $CONFIG
echo "# Plugins" >> $CONFIG
echo "PLUGINS=\"$PLUGINS\"" >> $CONFIG
echo "# Or, to append: " >> $CONFIG
echo "# PLUGINS=\"\$PLUGINS <... >\"" >> $CONFIG
echo >> $CONFIG
echo "# Arguments, same as plugins to append" >> $CONFIG
echo "# ARGS=\"\$ARGS <... >\"" >> $CONFIG
echo "#ARGS=\"$ARGS\"" >> $CONFIG
echo >> $CONFIG
echo "# Screen detection: " >> $CONFIG
echo "SCREENS=$SCREENS" >> $CONFIG
if [ -n "$SCREENNUMBERS" ]; then
echo "SCREENNUMBERS=\"$SCREENNUMBERS\"" >> $CONFIG
for a in ${SCREENNUMBERS}; do
echo "MULTIDISPLAY[$a]=${MULTIDISPLAY[$a]}" >> $CONFIG
done
fi
echo >> $CONFIG
echo "# Decorator" >> $CONFIG
echo "# Use \"unset DECORATOR\" or set DECORATOR=\"\" to not use one." >> $CONFIG
echo "DECORATOR=\"$DECORATOR\"" >> $CONFIG
echo "DECORATORARGS=\"$DECORATORARGS\"" >> $CONFIG
echo "# Delay in seconds before the decorator is started." >> $CONFIG
echo "DELAY=\"$DELAY\"" >> $CONFIG
echo "# Set this to \"no\" to send all decorator errors to /dev/null" >> $CONFIG
echo "DECOERRORS=\"$DECOERRORS\"" >> $CONFIG
echo >> $CONFIG
echo "# Values of 0 mean \"true\" (present), values of 1 means \"false\" (not present)" >> $CONFIG
echo "# Checks: " >> $CONFIG
echo NVIDIA=$NVIDIA >> $CONFIG
echo FBCONFIG=$FBCONFIG >> $CONFIG
echo XGL=$XGL >> $CONFIG
echo TFP=$TFP >> $CONFIG
echo NPOT=$NPOT >> $CONFIG
echo COMPOSITE=$COMPOSITE >> $CONFIG
echo XDAMAGE=$XDAMAGE >> $CONFIG
echo POSSIBLE=$POSSIBLE >> $CONFIG
echo XSYNC=$XSYNC >> $CONFIG
echo INDIRECT=$INDIRECT >> $CONFIG
}
####
# Execute checks, if necesarry.
function check_everything
{
if [ -z "$NVIDIA" ]; then
check_nvidia
NVIDIA=$?
else
verbose "Skipping nVidia check, using stored value.\n"
fi
if [ -z "$XGL" ]; then
check_xgl
XGL=$?
else
verbose "Skipping Xgl check, using stored value.\n"
fi
if [ -z "$FBCONFIG" ]; then
check_fbconfig
FBCONFIG=$?
else
verbose "Skipping FBConfig check, using stored value.\n"
fi
if [ -z "$TFP" ]; then
check_tfp
TFP=$?
else
verbose "Skipping texture_from_pixmap check, using stored value.\n"
fi
if [ -z "$NPOT" ]; then
check_npot_texture
NPOT=$?
else
verbose "Skipping non-power-of-two texture check, using stored value.\n"
fi
if [ -z "$COMPOSITE" ]; then
check_composite
COMPOSITE=$?
else
verbose "Skipping Composite extension check, using stored value.\n"
fi
if [ -z "$XDAMAGE" ]; then
check_xdamage
XDAMAGE=$?
else
verbose "Skipping Damage extension check, using stored value.\n"
fi
if [ -z "$XSYNC" ]; then
check_xsync
XSYNC=$?
else
verbose "Skipping XSync extension check, using stored value.\n";
fi
if [ -z "$SCREENS" ]; then
check_multiscreen
else
verbose "Skipping screen detection check, using stored value.\n";
fi
}
###
# Check if a directory exists; creates it if it doesn't, returns false if the
# path isn't a directory.
function require_dir
{
if ! [ -a "$1" ]; then
verbose "Creating directory $1\n";
mkdir $1;
fi
if [ ! -d $1 ]; then
echo "Warning: $1 exists but isn't a directory.";
return 1;
fi
return 0;
}
####
# Configuration handeling
# We attempt to follow the XDG basedir spec here;
# We can read both a global config, and a local one.
# The configuration file is extremly simple, as it's just a bash script.
# It might be a good idea to improve that a bit, specially with security
# in mind, an general errors.
# No config, so unset and possibly warn. (Might do more later)
function no_config
{
if [ -n "$1" ]; then
echo "$1";
fi
unset CONFIG
}
function get_config_name
{
if [ -n "$CONFIG" ]; then
return 0;
fi
if [ -z "$XDG_CONFIG_HOME" ]; then
if ! require_dir "$HOME/.config" ; then
no_config "Don't know how to treat config files. Ignoring them."
else
CONFIG="$HOME/.config/compiz-managerrc"
fi
else
if ! require_dir "$XDG_CONFIG_HOME" ; then
no_config "Don't know how to treat config files. Ignoring them."
else
CONFIG="$XDG_CONFIG_HOME/compiz-managerrc"
fi
fi
}
function parse_config
{
if [ -z "$XDG_CONFIG_DIRS" ]; then
XDG_CONFIG_DIRS="/etc/xdg/";
fi
verbose "Looking for configuration file(s): \n"
oldIFS=$IFS
IFS=":";
for a in $XDG_CONFIG_DIRS; do
if [ -f "$a/compiz-managerrc" ]; then
verbose "\t Loading ${a}/compiz-managerrc\n";
. $a/compiz-managerrc;
else
verbose "\t Not found: ${a}/compiz-managerrc\n";
fi
done
IFS=$oldIFS
if [ -n "$CONFIG" ]; then
if [ -f "$CONFIG" ]; then
verbose "\t Loading $CONFIG\n"
. $CONFIG ;
else
verbose "\t Not found: \"$CONFIG\"\n";
fi
fi
}
###
# Let's get this show started!
function start_compiz
{
###
# No need to continue if we've determined it's not possible to start anyway
if [ $POSSIBLE != "0" ]; then
echo "Checks indicate that it's impossible to start compiz on your system."
exit 1;
else
verbose "Checks indicate compiz should work on your system\n"
fi;
verbose "Exporting: $ENV \n"
export $ENV
verbose Executing: compiz $ARGS $PLUGINS "\n"
if [ "x$DRY" = "xyes" ]; then exit 0; fi
compiz $ARGS $PLUGINS
}
####
# Starts one decorator per screen
function start_decorators
{
if [ -z "$DECORATOR" ]; then return 1; fi
if [ "$SCREENS" == "1" ]; then
verbose "Starting delayed decorator in the background: "
verbose "sleep $DELAY && $DECORATOR $DECORATORARGS &\n"
if [ "x$DRY" = "xyes" ]; then return 0; fi
if [ "$DECOERRORS" = "no" ]; then
sleep $DELAY && $DECORATOR $DECORATORARGS 2>/dev/null &
else
sleep $DELAY && $DECORATOR $DECORATORARGS &
fi
return 0;
fi
verbose "Starting decorators for all screens: \n"
for a in $SCREENNUMBERS; do
verbose "\t Screen $a: "
verbose "sleep $DELAY && DISPLAY=${MULTIDISPLAY[$a]} $DECORATOR $DECORATORARGS\n"
if [ "x$DRY" != "xyes" ]; then
if [ "$DECOERRORS" = "no" ]; then
sleep $DELAY && DISPLAY=${MULTIDISPLAY[$a]} $DECORATOR $DECORATORARGS 2>/dev/null &
else
sleep $DELAY && DISPLAY=${MULTIDISPLAY[$a]} $DECORATOR $DECORATORARGS &
fi
fi
done
}
####################
# Execution begins here.
# First get options, check for configuration
# Check everything if necesarry, build the enviroment and arguments
# and eventually select a task.
parse_options $*
# We need this even when ignoring, or we won't know where to store force
# configuration files
get_config_name
if [ -z "$IGNORECONFIG" ]; then parse_config
else verbose "Ignoring configuration files as you requested\n"; fi
if [ -z "$NOCHECKS" ]; then check_everything; fi
###
# This is the master-test, it has to be done last.
if [ -z "$POSSIBLE" ]; then check_possible
else verbose "Skipping \"possible\" test, using stored value.\n"; fi
####
# Builds the enviromental variables list and argument list based
# on the result of the checks
build_env
build_args
case "$TASK" in
RECOMMEND)
if [ "x$REC" = "xenv" ]; then
echo -e $ENV;
elif [ "x$REC" = "xargs" ]; then
echo -e $ARGS
elif [ "x$REC" = "xboth" ]; then
echo -e $ARGS $PLUGINS
echo -e $ENV
fi
if [ $POSSIBLE != "0" ]; then return 1; fi
;;
WINDOWDECORATOR)
echo "start window decorator here..."
start_decorators
;;
*)
store_config
start_decorators
start_compiz
;;
esac