#!/bin/bash # Compiz manager # Copyright (c) 2007 Kristian Lyngstøl # # 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 ] [-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