Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 pdf-presenter-console (3.1.1-6) unstable; urgency=medium
 .
   * oops add build dependency on libgstreamer-plugins-base0.10-dev
Author: Barak A. Pearlmutter <bap@debian.org>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: <YYYY-MM-DD>

--- pdf-presenter-console-3.1.1.orig/CMakeLists.txt
+++ pdf-presenter-console-3.1.1/CMakeLists.txt
@@ -1,24 +1,16 @@
 project("pdfpc" C)
 cmake_minimum_required(VERSION 2.6)
 
-SET(CMAKE_BUILD_TYPE "Release")
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Vala_CMake/vala)
 
-set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc" CACHE FILEPATH "sysconfdir")
+find_package(Vala "0.16" REQUIRED)
+include(${VALA_USE_FILE})
 
-# Uncomment this section if you want to compile from vala sources
-#list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Vala_CMake/vala)
-#include(ValaPrecompile)
-#include(ValaVersion)
-#
-#find_package(Vala)
-#
-#ensure_vala_version("0.11.0" MINIMUM)
-#
-#add_subdirectory(src)
+set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc" CACHE FILEPATH "sysconfdir")
 
-# ...and comment the following line
-add_subdirectory(c-src)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
+add_subdirectory(src)
 add_subdirectory(icons)
 add_subdirectory(man)
 add_subdirectory(rc)
--- pdf-presenter-console-3.1.1.orig/README.rst
+++ pdf-presenter-console-3.1.1/README.rst
@@ -15,7 +15,7 @@ documents, which can be created using ne
 software.
 
 More information, including screenshots and a demo presentation, can be found
-at http://davvil.github.com/pdfpc/
+at http://pdfpc.github.com/pdfpc/
 
 Requirements
 ============
@@ -72,7 +72,7 @@ unstable development happens in the *dev
 When installing from git you will need two additional dependencies:
 
 - git
-- Vala Compiler Version >=0.11.0
+- Vala Compiler Version >=0.16.0
 
 The pdfpc source can be retrieved from github::
 
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/README.rst
@@ -0,0 +1,182 @@
+==========
+Vala CMake
+==========
+:Author: 
+    Jakob Westhoff
+:Version:
+    Draft
+
+
+Overview
+========
+
+Vala CMake is a collection of macros for the CMake_ build system to allow the
+creation and management of projects developed using the Vala_ programming
+language or its "Genie" flavor (less tested).
+
+
+Installation
+============
+
+To use the Vala macros in your own project you need to copy the macro files to
+an arbitrary folder in your projects directory and reference them in your
+``CMakeLists.txt`` file.
+
+Assuming the macros are stored under ``cmake/vala`` in your projects folder you
+need to add the following information to your base ``CMakeLists.txt``::
+
+    list(APPEND CMAKE_MODULE_PATH 
+        ${CMAKE_SOURCE_DIR}/cmake/vala
+    )
+
+After the new module path as been added you can simply include the provided
+modules or use the provided find routines.
+
+
+Finding Vala
+============
+
+The find module for vala works like any other Find module in CMake.
+You can use it by simply calling the usual ``find_package`` function. Default
+parameters like ``REQUIRED`` and ``QUIETLY`` are supported.
+
+::
+
+    find_package(Vala REQUIRED)
+
+After a successful call to the find_package function the following variables 
+will be set:
+
+VALA_FOUND
+    Whether the vala compiler has been found or not
+
+VALA_EXECUTABLE
+    Full path to the valac executable if it has been found
+
+VALA_VERSION
+    Version number of the available valac
+
+
+Precompiling Vala sources
+=========================
+
+CMake is mainly supposed to handle c or c++ based projects. Luckily every vala
+program is translated into plain c code using the vala compiler, followed by
+normal compilation of the generated c program using gcc.
+
+The macro ``vala_precompile`` uses that fact to create c files from your .vala
+sources for further CMake processing. 
+
+The first parameter provided is a variable, which will be filled with a list of
+c files outputted by the vala compiler. This list can than be used in
+conjunction with functions like ``add_executable`` or others to create the
+necessary compile rules with CMake.
+
+The initial variable is followed by a list of .vala files to be compiled.
+Please take care to add every vala file belonging to the currently compiled
+project or library as Vala will otherwise not be able to resolve all
+dependencies.
+
+The following sections may be specified afterwards to provide certain options
+to the vala compiler:
+
+PACKAGES  
+    A list of vala packages/libraries to be used during the compile cycle. The
+    package names are exactly the same, as they would be passed to the valac
+    "--pkg=" option.
+
+OPTIONS
+    A list of optional options to be passed to the valac executable. This can be
+    used to pass "--thread" for example to enable multi-threading support.
+
+DIRECTORY
+    Specify the directory where the output source files will be stored. If 
+    ommitted, the source files will be stored in CMAKE_CURRENT_BINARY_DIR.
+
+CUSTOM_VAPIS
+    A list of custom vapi files to be included for compilation. This can be
+    useful to include freshly created vala libraries without having to install
+    them in the system.
+
+GENERATE_VAPI
+    Pass all the needed flags to the compiler to create an internal vapi for
+    the compiled library. The provided name will be used for this and a
+    <provided_name>.vapi file will be created.
+
+GENERATE_HEADER
+    Let the compiler generate a header file for the compiled code. There will
+    be a header file as well as an internal header file being generated called
+    <provided_name>.h and <provided_name>_internal.h
+
+The following call is a simple example to the vala_precompile macro showing an
+example to every of the optional sections::
+
+    vala_precompile(VALA_C
+        source1.vala
+        source2.vala
+        source3.vala
+    PACKAGES
+        gtk+-2.0
+        gio-1.0
+        posix
+    OPTIONS
+        --thread
+    CUSTOM_VAPIS
+        some_vapi.vapi
+    GENERATE_VAPI
+        myvapi
+    GENERATE_HEADER
+        myheader
+    )
+
+Most important is the variable VALA_C which will contain all the generated c
+file names after the call. The easiest way to use this information is to tell
+CMake to create an executable out of it.
+
+::
+
+    add_executable(myexecutable ${VALA_C})
+
+Building a debug build
+======================
+In the section OPTIONS add the '-g' option. This will instruct the Vala compiler
+to include the Vala source code line information in the compiled binary. Add the
+'--save-temps' option to keep the temporary C source files.
+
+When running cmake add '-DCMAKE_BUILD_TYPE=Debug' option. After building the
+program, it can then be debugged with ``gdb`` or ``nemiver``.
+
+
+Further reading
+===============
+
+The `Pdf Presenter Console`__ , which is a vala based project of mine, makes
+heavy usage of the here described macros.  To look at a real world example of
+these macros the mentioned project is the right place to take a look. The svn
+trunk of it can be found at::
+
+	svn://pureenergy.cc/pdf_presenter_console/trunk
+
+
+__ http://westhoffswelt.de/projects/pdf_presenter_console.html
+
+
+Acknowledgments
+===============
+
+Thanks go out to Florian Sowade, a fellow local PHP-Usergroupie, who helped me
+a lot with the initial version of this macros and always answered my mostly
+dumb CMake questions.
+
+.. _CMake: http://cmake.org
+.. _Vala: http://live.gnome.org/Vala
+.. _Genie: http://live.gnome.org/Genie
+
+
+
+..
+   Local Variables:
+   mode: rst
+   fill-column: 79
+   End: 
+   vim: et syn=rst tw=79
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/vala/FindVala.cmake
@@ -0,0 +1,70 @@
+##
+# Find module for the Vala compiler (valac)
+#
+# This module determines wheter a Vala compiler is installed on the current
+# system and where its executable is.
+#
+# Call the module using "find_package(Vala) from within your CMakeLists.txt.
+#
+# The following variables will be set after an invocation:
+#
+#  VALA_FOUND       Whether the vala compiler has been found or not
+#  VALA_EXECUTABLE  Full path to the valac executable if it has been found
+#  VALA_VERSION     Version number of the available valac
+#  VALA_USE_FILE    Include this file to define the vala_precompile function
+##
+
+##
+# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
+# Copyright 2010-2011 Daniel Pfeifer
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+# 
+#    2. Redistributions in binary form must reproduce the above copyright notice,
+#       this list of conditions and the following disclaimer in the documentation
+#       and/or other materials provided with the distribution.
+# 
+# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# The views and conclusions contained in the software and documentation are those
+# of the authors and should not be interpreted as representing official policies,
+# either expressed or implied, of Jakob Westhoff
+##
+
+# Search for the valac executable in the usual system paths
+# Some distributions rename the valac to contain the major.minor in the binary name
+find_program(VALA_EXECUTABLE NAMES valac valac-0.20 valac-0.18 valac-0.16 valac-0.14 valac-0.12 valac-0.10)
+mark_as_advanced(VALA_EXECUTABLE)
+
+# Determine the valac version
+if(VALA_EXECUTABLE)
+    execute_process(COMMAND ${VALA_EXECUTABLE} "--version" 
+                    OUTPUT_VARIABLE VALA_VERSION
+                    OUTPUT_STRIP_TRAILING_WHITESPACE)
+    string(REPLACE "Vala " "" VALA_VERSION "${VALA_VERSION}")
+endif(VALA_EXECUTABLE)
+
+# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call.
+# Furthermore set VALA_FOUND to TRUE if Vala has been found (aka.
+# VALA_EXECUTABLE is set)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Vala
+    REQUIRED_VARS VALA_EXECUTABLE
+    VERSION_VAR VALA_VERSION)
+
+set(VALA_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseVala.cmake")
+
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/vala/ParseArguments.cmake
@@ -0,0 +1,36 @@
+##
+# This is a helper Macro to parse optional arguments in Macros/Functions
+# It has been taken from the public CMake wiki.  
+# See http://www.cmake.org/Wiki/CMakeMacroParseArguments for documentation and
+# licensing.
+##
+macro(parse_arguments prefix arg_names option_names)
+  set(DEFAULT_ARGS)
+  foreach(arg_name ${arg_names})
+    set(${prefix}_${arg_name})
+  endforeach(arg_name)
+  foreach(option ${option_names})
+    set(${prefix}_${option} FALSE)
+  endforeach(option)
+
+  set(current_arg_name DEFAULT_ARGS)
+  set(current_arg_list)
+  foreach(arg ${ARGN})
+    set(larg_names ${arg_names})
+    list(FIND larg_names "${arg}" is_arg_name)
+    if(is_arg_name GREATER -1)
+      set(${prefix}_${current_arg_name} ${current_arg_list})
+      set(current_arg_name ${arg})
+      set(current_arg_list)
+    else(is_arg_name GREATER -1)
+      set(loption_names ${option_names})
+      list(FIND loption_names "${arg}" is_option)
+      if(is_option GREATER -1)
+	    set(${prefix}_${arg} TRUE)
+      else(is_option GREATER -1)
+	    set(current_arg_list ${current_arg_list} ${arg})
+      endif(is_option GREATER -1)
+    endif(is_arg_name GREATER -1)
+  endforeach(arg)
+  set(${prefix}_${current_arg_name} ${current_arg_list})
+endmacro(parse_arguments)
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/vala/UseVala.cmake
@@ -0,0 +1,192 @@
+##
+# Compile vala files to their c equivalents for further processing. 
+#
+# The "vala_precompile" function takes care of calling the valac executable on
+# the given source to produce c files which can then be processed further using
+# default cmake functions.
+#
+# The first parameter provided is a variable, which will be filled with a list
+# of c files outputted by the vala compiler. This list can than be used in
+# conjuction with functions like "add_executable" or others to create the
+# neccessary compile rules with CMake.
+#
+# The following sections may be specified afterwards to provide certain options
+# to the vala compiler:
+#
+# SOURCES
+#   A list of .vala files to be compiled. Please take care to add every vala
+#   file belonging to the currently compiled project or library as Vala will
+#   otherwise not be able to resolve all dependencies.
+#
+# PACKAGES
+#   A list of vala packages/libraries to be used during the compile cycle. The
+#   package names are exactly the same, as they would be passed to the valac
+#   "--pkg=" option.
+#
+# OPTIONS
+#   A list of optional options to be passed to the valac executable. This can be
+#   used to pass "--thread" for example to enable multi-threading support.
+#
+# DEFINITIONS
+#   A list of symbols to be used for conditional compilation. They are the same
+#   as they would be passed using the valac "--define=" option.
+#
+# CUSTOM_VAPIS
+#   A list of custom vapi files to be included for compilation. This can be
+#   useful to include freshly created vala libraries without having to install
+#   them in the system.
+#
+# GENERATE_VAPI
+#   Pass all the needed flags to the compiler to create a vapi for
+#   the compiled library. The provided name will be used for this and a
+#   <provided_name>.vapi file will be created.
+#
+# GENERATE_HEADER
+#   Let the compiler generate a header file for the compiled code. There will
+#   be a header file as well as an internal header file being generated called
+#   <provided_name>.h and <provided_name>_internal.h
+#
+# The following call is a simple example to the vala_precompile macro showing
+# an example to every of the optional sections:
+#
+#   find_package(Vala "0.12" REQUIRED)
+#   include(${VALA_USE_FILE})
+#
+#   vala_precompile(VALA_C
+#     SOURCES
+#       source1.vala
+#       source2.vala
+#       source3.vala
+#     PACKAGES
+#       gtk+-2.0
+#       gio-1.0
+#       posix
+#     DIRECTORY
+#       gen
+#     OPTIONS
+#       --thread
+#     CUSTOM_VAPIS
+#       some_vapi.vapi
+#     GENERATE_VAPI
+#       myvapi
+#     GENERATE_HEADER
+#       myheader
+#     )
+#
+# Most important is the variable VALA_C which will contain all the generated c
+# file names after the call.
+##
+
+##
+# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
+# Copyright 2010-2011 Daniel Pfeifer
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+# 
+#    2. Redistributions in binary form must reproduce the above copyright notice,
+#       this list of conditions and the following disclaimer in the documentation
+#       and/or other materials provided with the distribution.
+# 
+# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# The views and conclusions contained in the software and documentation are those
+# of the authors and should not be interpreted as representing official policies,
+# either expressed or implied, of Jakob Westhoff
+##
+
+include(CMakeParseArguments)
+
+function(vala_precompile output)
+    cmake_parse_arguments(ARGS "" "DIRECTORY;GENERATE_HEADER;GENERATE_VAPI"
+        "SOURCES;PACKAGES;OPTIONS;DEFINITIONS;CUSTOM_VAPIS" ${ARGN})
+
+    if(ARGS_DIRECTORY)
+		get_filename_component(DIRECTORY ${ARGS_DIRECTORY} ABSOLUTE)
+    else(ARGS_DIRECTORY)
+        set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+    endif(ARGS_DIRECTORY)
+    include_directories(${DIRECTORY})
+
+    set(vala_pkg_opts "")
+    foreach(pkg ${ARGS_PACKAGES})
+        list(APPEND vala_pkg_opts "--pkg=${pkg}")
+    endforeach(pkg ${ARGS_PACKAGES})
+
+    set(vala_define_opts "")
+    foreach(def ${ARGS_DEFINTIONS})
+        list(APPEND vala_define_opts "--define=${def}")
+    endforeach(def ${ARGS_DEFINTIONS})
+
+    set(in_files "")
+    set(out_files "")
+    foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
+        list(APPEND in_files "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
+        string(REPLACE ".vala" ".c" src ${src})
+        string(REPLACE ".gs" ".c" src ${src})
+        set(out_file "${DIRECTORY}/${src}")
+        list(APPEND out_files "${DIRECTORY}/${src}")
+    endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
+
+    set(custom_vapi_arguments "")
+    if(ARGS_CUSTOM_VAPIS)
+        foreach(vapi ${ARGS_CUSTOM_VAPIS})
+            if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+                list(APPEND custom_vapi_arguments ${vapi})
+            else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+                list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
+            endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+        endforeach(vapi ${ARGS_CUSTOM_VAPIS})
+    endif(ARGS_CUSTOM_VAPIS)
+
+    set(vapi_arguments "")
+    if(ARGS_GENERATE_VAPI)
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI}.vapi")
+        set(vapi_arguments "--vapi=${ARGS_GENERATE_VAPI}.vapi")
+
+        # Header and internal header is needed to generate internal vapi
+        if (NOT ARGS_GENERATE_HEADER)
+            set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI})
+        endif(NOT ARGS_GENERATE_HEADER)
+    endif(ARGS_GENERATE_VAPI)
+
+    set(header_arguments "")
+    if(ARGS_GENERATE_HEADER)
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}.h")
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h")
+        list(APPEND header_arguments "--header=${DIRECTORY}/${ARGS_GENERATE_HEADER}.h")
+        list(APPEND header_arguments "--internal-header=${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h")
+    endif(ARGS_GENERATE_HEADER)
+
+    add_custom_command(OUTPUT ${out_files} 
+    COMMAND 
+        ${VALA_EXECUTABLE} 
+    ARGS 
+        "-C" 
+        ${header_arguments} 
+        ${vapi_arguments}
+        "-b" ${CMAKE_CURRENT_SOURCE_DIR} 
+        "-d" ${DIRECTORY} 
+        ${vala_pkg_opts} 
+        ${vala_define_opts}
+        ${ARGS_OPTIONS} 
+        ${in_files} 
+        ${custom_vapi_arguments}
+    DEPENDS 
+        ${in_files} 
+        ${ARGS_CUSTOM_VAPIS}
+    )
+    set(${output} ${out_files} PARENT_SCOPE)
+endfunction(vala_precompile)
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/vala/ValaPrecompile.cmake
@@ -0,0 +1,187 @@
+##
+# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+# 
+#    2. Redistributions in binary form must reproduce the above copyright notice,
+#       this list of conditions and the following disclaimer in the documentation
+#       and/or other materials provided with the distribution.
+# 
+# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# The views and conclusions contained in the software and documentation are those
+# of the authors and should not be interpreted as representing official policies,
+# either expressed or implied, of Jakob Westhoff
+##
+
+include(ParseArguments)
+find_package(Vala REQUIRED)
+
+##
+# Compile vala files to their c equivalents for further processing. 
+#
+# The "vala_precompile" macro takes care of calling the valac executable on the
+# given source to produce c files which can then be processed further using
+# default cmake functions.
+# 
+# The first parameter provided is a variable, which will be filled with a list
+# of c files outputted by the vala compiler. This list can than be used in
+# conjuction with functions like "add_executable" or others to create the
+# neccessary compile rules with CMake.
+# 
+# The initial variable is followed by a list of .vala files to be compiled.
+# Please take care to add every vala file belonging to the currently compiled
+# project or library as Vala will otherwise not be able to resolve all
+# dependencies.
+# 
+# The following sections may be specified afterwards to provide certain options
+# to the vala compiler:
+# 
+# PACKAGES
+#   A list of vala packages/libraries to be used during the compile cycle. The
+#   package names are exactly the same, as they would be passed to the valac
+#   "--pkg=" option.
+# 
+# OPTIONS
+#   A list of optional options to be passed to the valac executable. This can be
+#   used to pass "--thread" for example to enable multi-threading support.
+#
+# CUSTOM_VAPIS
+#   A list of custom vapi files to be included for compilation. This can be
+#   useful to include freshly created vala libraries without having to install
+#   them in the system.
+#
+# GENERATE_VAPI
+#   Pass all the needed flags to the compiler to create an internal vapi for
+#   the compiled library. The provided name will be used for this and a
+#   <provided_name>.vapi file will be created.
+# 
+# GENERATE_HEADER
+#   Let the compiler generate a header file for the compiled code. There will
+#   be a header file as well as an internal header file being generated called
+#   <provided_name>.h and <provided_name>_internal.h
+#
+# The following call is a simple example to the vala_precompile macro showing
+# an example to every of the optional sections:
+#
+#   vala_precompile(VALA_C
+#       source1.vala
+#       source2.vala
+#       source3.vala
+#   PACKAGES
+#       gtk+-2.0
+#       gio-1.0
+#       posix
+#   DIRECTORY
+#       gen
+#   OPTIONS
+#       --thread
+#   CUSTOM_VAPIS
+#       some_vapi.vapi
+#   GENERATE_VAPI
+#       myvapi
+#   GENERATE_HEADER
+#       myheader
+#   )
+#
+# Most important is the variable VALA_C which will contain all the generated c
+# file names after the call.
+##
+
+macro(vala_precompile output)
+    parse_arguments(ARGS "PACKAGES;OPTIONS;DIRECTORY;GENERATE_HEADER;GENERATE_VAPI;CUSTOM_VAPIS" "" ${ARGN})
+    if(ARGS_DIRECTORY)
+        set(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DIRECTORY})
+    else(ARGS_DIRECTORY)
+        set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+    endif(ARGS_DIRECTORY)
+    include_directories(${DIRECTORY})
+    set(vala_pkg_opts "")
+    foreach(pkg ${ARGS_PACKAGES})
+        list(APPEND vala_pkg_opts "--pkg=${pkg}")
+    endforeach(pkg ${ARGS_PACKAGES})
+    set(in_files "")
+    set(out_files "")
+    set(${output} "")
+    foreach(src ${ARGS_DEFAULT_ARGS})
+        string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR}/ "" src ${src})
+        string(REGEX MATCH "^/" IS_MATCHED ${src})
+        if(${IS_MATCHED} MATCHES "/")
+            list(APPEND in_files "${src}")
+        else()
+            list(APPEND in_files "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
+        endif()
+        string(REPLACE ".vala" ".c" src ${src})
+        string(REPLACE ".gs" ".c" src ${src})
+        if(${IS_MATCHED} MATCHES "/")
+            get_filename_component(VALA_FILE_NAME ${src} NAME)
+            set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
+            list(APPEND out_files "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
+        else()
+            set(out_file "${DIRECTORY}/${src}")
+            list(APPEND out_files "${DIRECTORY}/${src}")
+        endif()
+        list(APPEND ${output} ${out_file})
+    endforeach(src ${ARGS_DEFAULT_ARGS})
+
+    set(custom_vapi_arguments "")
+    if(ARGS_CUSTOM_VAPIS)
+        foreach(vapi ${ARGS_CUSTOM_VAPIS})
+            if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+                list(APPEND custom_vapi_arguments ${vapi})
+            else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+                list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
+            endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
+        endforeach(vapi ${ARGS_CUSTOM_VAPIS})
+    endif(ARGS_CUSTOM_VAPIS)
+
+    set(vapi_arguments "")
+    if(ARGS_GENERATE_VAPI)
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI}.vapi")
+        set(vapi_arguments "--internal-vapi=${ARGS_GENERATE_VAPI}.vapi")
+
+        # Header and internal header is needed to generate internal vapi
+        if (NOT ARGS_GENERATE_HEADER)
+            set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI})
+        endif(NOT ARGS_GENERATE_HEADER)
+    endif(ARGS_GENERATE_VAPI)
+
+    set(header_arguments "")
+    if(ARGS_GENERATE_HEADER)
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}.h")
+        list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h")
+        list(APPEND header_arguments "--header=${DIRECTORY}/${ARGS_GENERATE_HEADER}.h")
+        list(APPEND header_arguments "--internal-header=${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h")
+    endif(ARGS_GENERATE_HEADER)
+
+    add_custom_command(OUTPUT ${out_files} 
+    COMMAND 
+        ${VALA_EXECUTABLE} 
+    ARGS 
+        "-C" 
+        ${header_arguments} 
+        ${vapi_arguments}
+        "-b" ${CMAKE_CURRENT_SOURCE_DIR} 
+        "-d" ${DIRECTORY} 
+        ${vala_pkg_opts} 
+        ${ARGS_OPTIONS} 
+        ${in_files} 
+        ${custom_vapi_arguments}
+    DEPENDS 
+        ${in_files} 
+        ${ARGS_CUSTOM_VAPIS}
+    )
+endmacro(vala_precompile)
--- /dev/null
+++ pdf-presenter-console-3.1.1/cmake/Vala_CMake/vala/ValaVersion.cmake
@@ -0,0 +1,96 @@
+##
+# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+# 
+#    2. Redistributions in binary form must reproduce the above copyright notice,
+#       this list of conditions and the following disclaimer in the documentation
+#       and/or other materials provided with the distribution.
+# 
+# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# The views and conclusions contained in the software and documentation are those
+# of the authors and should not be interpreted as representing official policies,
+# either expressed or implied, of Jakob Westhoff
+##
+
+include(ParseArguments)
+find_package(Vala REQUIRED)
+
+##
+# Ensure a certain valac version is available
+#
+# The initial argument is the version to check for
+# 
+# It may be followed by a optional parameter to specifiy a version range. The
+# following options are valid:
+# 
+# EXACT
+#   Vala needs to be available in the exact version given
+# 
+# MINIMUM
+#   The provided version is the minimum version. Therefore Vala needs to be
+#   available in the given version or any higher version
+#
+# MAXIMUM
+#   The provided version is the maximum. Therefore Vala needs to be available
+#   in the given version or any version older than this
+#
+# If no option is specified the version will be treated as a minimal version.
+##
+macro(ensure_vala_version version)
+    parse_arguments(ARGS "" "MINIMUM;MAXIMUM;EXACT" ${ARGN})
+	set(compare_message "")
+	set(error_message "")
+	if(ARGS_MINIMUM)
+		set(compare_message "a minimum ")
+		set(error_message "or greater ")
+	elseif(ARGS_MAXIMUM)
+		set(compare_message "a maximum ")
+		set(error_message "or less ")
+	endif(ARGS_MINIMUM)
+	
+	message(STATUS 
+		"checking for ${compare_message}Vala version of ${version}"
+	)
+
+	unset(version_accepted)
+	
+	# MINIMUM is the default if no option is specified
+	if(ARGS_EXACT)
+		if(${VALA_VERSION} VERSION_EQUAL ${version} )
+			set(version_accepted TRUE)	
+		endif(${VALA_VERSION} VERSION_EQUAL ${version})
+	elseif(ARGS_MAXIMUM)
+		if(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
+			set(version_accepted TRUE)	
+		endif(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
+	else(ARGS_MAXIMUM)
+		if(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
+			set(version_accepted TRUE)	
+		endif(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
+	endif(ARGS_EXACT)
+
+	if (NOT version_accepted)
+		message(FATAL_ERROR 
+			"Vala version ${version} ${error_message}is required."
+		)
+	endif(NOT version_accepted)
+
+	message(STATUS
+		"  found Vala, version ${VALA_VERSION}"
+	)
+endmacro(ensure_vala_version)
--- pdf-presenter-console-3.1.1.orig/icons/CMakeLists.txt
+++ pdf-presenter-console-3.1.1/icons/CMakeLists.txt
@@ -1,8 +1,8 @@
 install(FILES
-        blank.svg
-        snow.svg
-        pause.svg
+    blank.svg
+    snow.svg
+    pause.svg
 DESTINATION
-        share/pixmaps/pdfpc
+    share/pixmaps/pdfpc
 )
 
--- pdf-presenter-console-3.1.1.orig/man/CMakeLists.txt
+++ pdf-presenter-console-3.1.1/man/CMakeLists.txt
@@ -1,7 +1,7 @@
 CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/pdfpc.in ${CMAKE_CURRENT_SOURCE_DIR}/pdfpc.1)
 
 install(FILES
-        pdfpc.1
+    pdfpc.1
 DESTINATION
-        share/man/man1
+    share/man/man1
 )
--- pdf-presenter-console-3.1.1.orig/man/pdfpc.in
+++ pdf-presenter-console-3.1.1/man/pdfpc.in
@@ -1,7 +1,11 @@
-.TH  pdfpc "1" "Jun 2012" 
+.TH  pdfpc "1" "May 2013"
+.de URL
+\\$2 \(laURL: \\$1 \(ra\\$3
+..
+.if \n[.g] .mso www.tmac
 
 .SH NAME
-pdfpc \- pdf presentater console with multi-monitor support
+pdfpc \- pdf presenter console with multi-monitor support
 
 .SH SYNOPSIS
 .B pdfpc
@@ -28,7 +32,7 @@ pdfpc file.pdf
 By default the presenter view appears in the primary monitor and the
 presentation view in the second monitor (provided you have two
 monitors).
-If you want to swith displays, start with the \-s option (see below).
+If you want to switch displays, start with the \-s option (see below).
 
 .SH OPTIONS
 .TP
@@ -71,6 +75,15 @@ Force to use only one screen
 .BI "\-L, \-\-list\-actions"
 List actions supported in the config file(s)
 .TP
+.BI "\-w, \-\-windowed"
+Run in windowed mode
+.TP
+.BI "\-Z, \-\-size"
+Size of the presenter console in width:height format (forces windowed mode)
+.TP
+.BI "\-n, \-\-notes"=P
+Position of notes on the pdf page. Position can be either left, right, top or bottom (Default none)
+.TP
 .BI "\-h, \-\-help"
 Shows the help
 
@@ -146,29 +159,29 @@ below if you want to customize the keybi
 
 .SH FEATURES
 
-.SS Caching / Prerendering
+.SS Caching / Pre-rendering
 
 To allow fast changes between the different slides of the presentation the pdf
-pages are prerendered to memory.  The progress bar on the bottom of the
+pages are pre-rendered to memory.  The progress bar on the bottom of the
 presenter screen indicates how many percent of the slides have been
 pre-rendered already.  During the initial rendering phase this will slow down
 slide changes, as most cpu power is used for the rendering process in the
 background.  After the cache is fully primed however the changing of slides
 should be much faster, as with normal pdf viewers.
 
-As the prerendering takes a lot of memory it can be disabled using the
+As the pre-rendering takes a lot of memory it can be disabled using the
 \-\-disable\-cache switch at the cost of speed.
 .SS Cache compression
 
-The prerendered and cached slides can be compressed in memory to save up some
+The pre-rendered and cached slides can be compressed in memory to save up some
 memory.  Without compression a set of about 100 pdf pages can easily grow up to
 about 1.5gb size.  Netbooks with only 1gb of memory would swap themselves to
-death if prerendering is enabled in such a situation.  The compression is
+death if pre-rendering is enabled in such a situation.  The compression is
 enabled by default as it does not harm rendering speed in a noticeable way on
-most systems.  It does however slows down prerendering by about a factor of 2.
+most systems.  It does however slow down prerendering by about a factor of two.
 If you have got enough memory and want to ensure the fastest possible
-prerendering you can disable slide compression by using the \-z switch. But be
-warned using the uncompressed prerendering storage will use about 30 times the
+pre-rendering you can disable slide compression by using the \-z switch. But be
+warned using the uncompressed pre-rendering storage will use about 30 times the
 memory the new compressed storage utilizes (aka the 1.5gb become about 50mb)
 
 .SS Timer
@@ -214,7 +227,7 @@ enumerations where the single items are
 "animations", where parts of a picture change from slide to slide.  Pdf
 Presenter Console includes facilities for dealing with such overlays.
 
-In this description, we will differentiation between slides (i.e.  pages in the
+In this description, we will differentiate between slides (i.e.  pages in the
 pdf document) and "user slides", that are the logical slides.  The standard
 forward movement command (page down, enter, etc.) moves through one slide at a
 time, as expected.  That means that every step in the overlay is traversed.
@@ -326,26 +339,56 @@ bind S+Next    next10
 A list of all possible functions can be obtained
 via the \-L command line option.
 
-Under
-.I http://davvil.github.com/pdfpc#configs 
-you can find some configuration files for commonly used devices
-(wireles presenters, bluetooth headesets, ...). If your device is not
+Some configuration files for commonly used devices
+(wireless presenters, bluetooth headsets, ...)
+.URL http://davvil.github.com/pdfpc#configs "are available" .
+If your device is not
 yet supported and you generated a working config file, please
 contribute it (see contact information below).
 
+.SS Movies
+
+Pdfpc can play back movies included in the PDF file.  Movies may be started
+and stopped by clicking within their area.  For the presenter, a progress
+bar is drawn along the bottom of the movie.  This expands when the mouse
+hovers over it, allowing one to seek by clicking or dragging within the
+progress bar.  Switching slides automatically stops playback, and movies
+will be reset after leaving and returning to a slide.
+
+Movies may be included in PDF files as "screen annotations".  pdfpc does
+not yet support options that modify the playback of these movies.  In LaTeX,
+such movies may be added to a presentation with the "movie15" package.
+Note that the poster, autoplay, and repeat options are not yet supported.
+(Also, run ps2pdf with the \-dNOSAFER flag.)
+
+As a perhaps simpler option, pdfpc will play back movies linked from a
+hyperlink of type "launch".  A query string may be added to the URL of the
+movie to enable the "autostart" and "loop" properties.  (E.g., a link to
+"movie.avi?autostart&loop" will start playing automatically, and loop when
+it reaches the end.)  In LaTeX, such links are created with
+
+.RS
+\\usepackage{hyperref}
+.br
+\\href{run:<movie file>}{<placeholder content>}
+.RE
+
+The movie will playback in the area taken by the placeholder content.  Using
+a frame of the movie will ensure the correct aspect ratio.
+
 .SH BUGS
 
 There may be a small memory leak in the program. I am trying to solve it. It
 should not be too important for up to some hundreds of slides.
 
-Other bugs can be reported at 
-.I https://github.com/davvil/pdfpc/issues
+Other bugs can be reported at
+.URL https://github.com/davvil/pdfpc/issues "the issue tracker" .
 
 .SH CONTACT
 .PP
-Comments and suggestion are welcome. Write an email to 
-.I davvil@gmail.com
+Comments and suggestion are welcome. Write an email to
+.URL mailto:davvil@gmail.com "David Vilar" .
 
 .SH SEE ALSO
-pdfpc is a fork of pdf-presenter console, available at
-.I http://westhoffswelt.de/projects/pdf_presenter_console.html
+pdfpc is a fork of Pdf Presenter Console, available
+.URL http://westhoffswelt.de/projects/pdf_presenter_console.html "online" .
--- pdf-presenter-console-3.1.1.orig/src/CMakeLists.txt
+++ pdf-presenter-console-3.1.1/src/CMakeLists.txt
@@ -1,84 +1,103 @@
 find_package(PkgConfig)
 pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
 pkg_check_modules(GIO REQUIRED gio-2.0)
-pkg_check_modules(GEE REQUIRED gee-1.0)
+pkg_check_modules(GEE REQUIRED gee-0.8)
 pkg_check_modules(POPPLER REQUIRED poppler-glib)
-pkg_check_modules(GTK REQUIRED gtk+-2.0)
+pkg_check_modules(GTK REQUIRED gtk+-3.0)
 pkg_check_modules(GTHREAD REQUIRED gthread-2.0)
-pkg_check_modules(RSVG REQUIRED librsvg-2.0)
+pkg_check_modules(GSTREAMER REQUIRED gstreamer-0.10)
+pkg_check_modules(GSTINTERFACES REQUIRED gstreamer-interfaces-0.10)
+pkg_check_modules(GSTVIDEO REQUIRED gstreamer-video-0.10)
+pkg_check_modules(GDKX11 REQUIRED gdk-x11-3.0)
 
 set(CFLAGS
-	${GOBJECT_CFLAGS} ${GOBJECT_CFLAGS_OTHER}
-	${GIO_CFLAGS} ${GIO_CFLAGS_OTHER}
-        ${GEE_CFLAGS} ${GEE_CFLAGS_OTHER}
-	${POPPLER_CFLAGS} ${POPPLER_CFLAGS_OTHER}
-	${GTK_CFLAGS} ${GTK_CFLAGS_OTHER}
-	${GTHREAD_CFLAGS} ${GTHREAD_CFLAGS_OTHER}
-        ${RSVG_CFLAGS} ${RSVG_CFLAGS_OTHER}
+    ${GOBJECT_CFLAGS} ${GOBJECT_CFLAGS_OTHER}
+    ${GIO_CFLAGS} ${GIO_CFLAGS_OTHER}
+    ${GEE_CFLAGS} ${GEE_CFLAGS_OTHER}
+    ${POPPLER_CFLAGS} ${POPPLER_CFLAGS_OTHER}
+    ${GTK_CFLAGS} ${GTK_CFLAGS_OTHER}
+    ${GTHREAD_CFLAGS} ${GTHREAD_CFLAGS_OTHER}
+    ${GSTREAMER_CFLAGS} ${GSTREAMER_CFLAGS_OTHER}
+    ${GSTINTERFACES_CFLAGS} ${GSTINTERFACES_CFLAGS_OTHER}
+    ${GSTVIDEO_CFLAGS} ${GSTVIDEO_CFLAGS_OTHER}
+    ${GDKX11_CFLAGS} ${GDKX11_CFLAGS_OTHER}
 )
 add_definitions(${CFLAGS})
 
 set(LIBS
-	${GOBJECT_LIBRARIES}
-	${GIO_LIBRARIES}
-        ${GEE_LIBRARIES}
-	${POPPLER_LIBRARIES}
-	${GTK_LIBRARIES}
-	${GTHREAD_LIBRARIES}
-        ${RSVG_LIBRARIES}
+    ${GOBJECT_LIBRARIES}
+    ${GIO_LIBRARIES}
+    ${GEE_LIBRARIES}
+    ${POPPLER_LIBRARIES}
+    ${GTK_LIBRARIES}
+    ${GTHREAD_LIBRARIES}
+    ${GSTREAMER_LIBRARIES}
+    ${GSTINTERFACES_LIBRARIES}
+    ${GSTVIDEO_LIBRARIES}
+    ${GDKX11_LIBRARIES}
 )
 link_libraries(${LIBS})
 
 set(LIB_PATHS
-	${GOBJECT_LIBRARY_DIRS}
-	${GIO_LIBRARY_DIRS}
-        ${GEE_LIBRARY_DIRS}
-	${POPPLER_LIBRARY_DIRS}
-	${GTK_LIBRARY_DIRS}
-	${GTHREAD_LIBRARY_DIRS}
-        ${RSVG_LIBRARY_DIRS}
+    ${GOBJECT_LIBRARY_DIRS}
+    ${GIO_LIBRARY_DIRS}
+    ${GEE_LIBRARY_DIRS}
+    ${POPPLER_LIBRARY_DIRS}
+    ${GTK_LIBRARY_DIRS}
+    ${GTHREAD_LIBRARY_DIRS}
+    ${GSTREAMER_LIBRARY_DIRS}
+    ${GSTINTERFACES_LIBRARY_DIRS}
+    ${GSTVIDEO_LIBRARY_DIRS}
+    ${GDKX11_LIBRARY_DIRS}
 )
 link_directories(${LIB_PATHS})
 
 CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/paths.in ${CMAKE_CURRENT_SOURCE_DIR}/paths.vala)
 
 file (GLOB_RECURSE VALA_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.vala)
+# Vala 0.14 (and presumably lower) doesn't have Poppler.AnnotFileAttachment
+if(${VALA_VERSION} VERSION_LESS "0.16.0")
+    set(VALA_SRC ${VALA_SRC} poppler-annot-file-attachment.vapi)
+endif(${VALA_VERSION} VERSION_LESS "0.16.0")
 
 vala_precompile(VALA_C
-	${VALA_SRC}
-PACKAGES 
-	gio-2.0
-        gee-1.0
-	poppler-glib
-	gtk+-2.0
-	posix
-        librsvg-2.0
-OPTIONS 
-	--thread
-	--debug
+    ${VALA_SRC}
+PACKAGES
+    gio-2.0
+    gee-0.8
+    poppler-glib
+    gtk+-3.0
+    posix
+    gstreamer-0.10
+    gstreamer-interfaces-0.10
+    gstreamer-video-0.10
+    gdk-x11-3.0
+OPTIONS
+    --thread
+    --debug
 GENERATE_HEADER
-	presenter
+    presenter
 GENERATE_VAPI
-	presenter
+    presenter
 )
 
 add_executable(pdfpc
-  	${VALA_C}
+    ${VALA_C}
 )
 
 # explicitly add libraries (needed e.g. for Fedora 13+)
 target_link_libraries(pdfpc -lm)
 
-install(TARGETS 
-	pdfpc
+install(TARGETS
+    pdfpc
 RUNTIME
 DESTINATION
-	bin
+    bin
 )
 
 # The tests need a library version of all the pdf_presenter_stuff
-add_library(pdf_presenter_console_test_library 
-	SHARED
-	EXCLUDE_FROM_ALL
-	${VALA_C} presenter.h presenter.vapi
+add_library(pdf_presenter_console_test_library
+    SHARED
+    EXCLUDE_FROM_ALL
+    ${VALA_C} presenter.h presenter.vapi
 )
--- /dev/null
+++ pdf-presenter-console-3.1.1/src/classes/action/action_mapping.vala
@@ -0,0 +1,118 @@
+/**
+ * Base action mapping, encapsulating link and annotation mappings.
+ *
+ * This file is part of pdfpc.
+ *
+ * Copyright (C) 2012 Robert Schroll <rschroll@gmail.com>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+namespace pdfpc {
+    /**
+     * Base action mapping, encapsulating actions that result from links or annotations.
+     */
+    public abstract class ActionMapping: GLib.Object {
+        /**
+         * The area on the PDF page associated with the action.
+         */
+        public Poppler.Rectangle area;
+
+        /**
+         * The presentation controller, which is probably needed to execute actions.
+         */
+        protected PresentationController controller;
+
+        /**
+         * The PDF document in question.
+         */
+        protected Poppler.Document document;
+
+        /**
+         * Constructors of ActionMapping classes shouldn't actually do much.  These
+         * objects will generally be made with the new_from_... methods, below.
+         * Unlike constructors, these have the option of returning null.  Since I
+         * couldn't figure out how to make class objects in Vala, those are object
+         * methods, and we need blank objects to call them.  Thus, this blank
+         * constructor.
+         */
+        public ActionMapping() {
+            base();
+        }
+
+        /**
+         * Instead of in the constructor, most setup is done in the init method.
+         */
+        public virtual void init(Poppler.Rectangle area, PresentationController controller,
+                Poppler.Document document) {
+            this.area = area;
+            this.controller = controller;
+            this.document = document;
+        }
+
+        /**
+         * Create and return a new ActionMapping object from the LinkMapping, or
+         * return null if this class doesn't handle this type of LinkMapping.  Note
+         * that this is an object method, not a static method, which makes it easier
+         * to figure out if you're in a subclass.
+         */
+        public virtual ActionMapping? new_from_link_mapping(Poppler.LinkMapping mapping,
+                PresentationController controller, Poppler.Document document) {
+            return null;
+        }
+
+        /**
+         * Create and return a new ActionMapping object from the AnnotMapping, or
+         * return null if this class doesn't handle this type of AnnotMapping.
+         */
+        public virtual ActionMapping? new_from_annot_mapping(Poppler.AnnotMapping mapping,
+                PresentationController controller, Poppler.Document document) {
+            return null;
+        }
+
+        /**
+         * Override this method to get notified of the mouse entering the area.
+         */
+        public virtual void on_mouse_enter(Gtk.Widget widget, Gdk.EventMotion event) {
+            // Set the cursor to the X11 theme default link cursor
+            event.window.set_cursor(
+                new Gdk.Cursor.from_name(Gdk.Display.get_default(), "hand2")
+            );
+        }
+
+        /**
+         * Override this method to get notified of the mouse exiting the area.
+         */
+        public virtual void on_mouse_leave(Gtk.Widget widget, Gdk.EventMotion event) {
+            // Restore the cursor to its default state (The parent cursor
+            // configuration is used)
+            event.window.set_cursor(null);
+        }
+
+        /**
+         * Handle mouse press events in the area.  Return true to indicate that
+         * the event was handled (and therefore we should not advance to the next
+         * page.
+         */
+        public abstract bool on_button_press(Gtk.Widget widget, Gdk.EventButton event);
+
+        /**
+         * Called when leaving the page.  Override to clean up after yourself.
+         */
+        public virtual void deactivate() {
+            return;
+        }
+    }
+}
--- /dev/null
+++ pdf-presenter-console-3.1.1/src/classes/action/link_action.vala
@@ -0,0 +1,80 @@
+/**
+ * Action mapping for handling internal links.
+ *
+ * This file is part of pdfpc.
+ *
+ * Copyright (C) 2012 Robert Schroll <rschroll@gmail.com>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+namespace pdfpc {
+    /**
+     * Action for internal links in the PDF file.
+     */
+    public class LinkAction: ActionMapping {
+        /**
+         * The Poppler.Action associated with the link.
+         */
+        public Poppler.Action action;
+
+        /**
+         * Base constructor does nothing.
+         */
+        public LinkAction() {
+            base();
+        }
+
+        /**
+         * Initializer.
+         */
+        public new void init(Poppler.LinkMapping mapping, PresentationController controller,
+                Poppler.Document document) {
+            base.init(mapping.area, controller, document);
+            this.action = mapping.action.copy();
+        }
+
+        /**
+         * Create from the LinkMapping if the link is an internal link to a named
+         * destination inside the PDF file.
+         */
+        public override ActionMapping? new_from_link_mapping(Poppler.LinkMapping mapping,
+                PresentationController controller, Poppler.Document document) {
+            if (mapping.action.type != Poppler.ActionType.GOTO_DEST)
+                return null;
+            if (((Poppler.ActionGotoDest*)mapping.action).dest.type != Poppler.DestType.NAMED)
+                return null;
+
+            var new_obj = new LinkAction();
+            new_obj.init(mapping, controller, document);
+            return new_obj as ActionMapping;
+        }
+
+        /**
+         * Goto the link's destination on left clicks.
+         */
+        public override bool on_button_press(Gtk.Widget widget, Gdk.EventButton event) {
+            if (event.button != 1)
+                return false;
+
+            unowned Poppler.ActionGotoDest* action = (Poppler.ActionGotoDest*)this.action;
+            Poppler.Dest destination;
+            destination = this.document.find_dest(action.dest.named_dest);
+
+            this.controller.page_change_request((int)(destination.page_num - 1));
+            return true;
+        }
+    }
+}
--- /dev/null
+++ pdf-presenter-console-3.1.1/src/classes/action/movie.vala
@@ -0,0 +1,694 @@
+/**
+ * Action mapping for movies.
+ *
+ * This file is part of pdfpc.
+ *
+ * Copyright (C) 2012 Robert Schroll <rschroll@gmail.com>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+namespace pdfpc {
+    /**
+     * An error in constructing a gstreamer pipeline.
+     */
+    public errordomain PipelineError {
+        ElementConstruction,
+        Linking
+    }
+    /**
+     * Make a non-NULL gstreamer element, or raise an error.
+     */
+    public Gst.Element gst_element_make(string factoryname, string? name) throws PipelineError {
+        var element = Gst.ElementFactory.make(factoryname, name);
+        if (element == null)
+            throw new PipelineError.ElementConstruction(@"Could not make element $name of type $factoryname.");
+        return element;
+    }
+
+    /**
+     * A movie with basic controls -- click to start and stop.
+     */
+    public class Movie: ActionMapping {
+        /**
+         * The gstreamer pipeline for playback.
+         */
+        public dynamic Gst.Element pipeline;
+
+        /**
+         * A flag to indicate when we've reached the End Of Stream, so we
+         * know whether to restart on the next click.
+         */
+        protected bool eos = false;
+
+        /**
+         * A flag to indicated whether the movie should be played in a loop.
+         */
+        protected bool loop;
+
+        /**
+         * If the movie was attached to the PDF file, we store it in a temporary
+         * file, whose name we store here.  If not, this will be the blank string.
+         */
+        protected string temp;
+
+        /**
+         * Base constructor does nothing.
+         */
+        public Movie() {
+            base();
+        }
+
+        /**
+         * This initializer is odd -- it's called from an other object from the
+         * one your are initializing.  This is so subclasses can override this
+         * to do custom initialization without having to implement the new_from_...
+         * methods.  This can't be a good way to handle this, but I've yet to figure
+         * out a better one.
+         */
+        public virtual void init_other(ActionMapping other, Poppler.Rectangle area,
+                PresentationController controller, Poppler.Document document,
+                string uri, bool autostart, bool loop, bool temp=false) {
+            other.init(area, controller, document);
+            var movie = other as Movie;
+            movie.loop = loop;
+            movie.temp = temp ? uri.substring(7) : "";
+            GLib.Idle.add( () => {
+                movie.establish_pipeline(uri);
+                if (autostart)
+                    movie.play();
+                return false;
+            } );
+        }
+
+        /**
+         * Create a new Movie from a link mapping, if the link of type "LAUNCH"
+         * and points to a file that looks like a video file.  The video is
+         * played back in the area of the hyperlink, which is probably not
+         * conforming to the PDF spec, but it makes this an easy way to include
+         * movies in presentations.  As a bonus, a query string on the video
+         * filename can activate the autostart and loop properties.  (E.g., link
+         * to movie.avi?autostart&loop to make movie.avi start playing with the
+         * page is entered and loop back to the beginning when it reaches the end.)
+         *
+         * In LaTeX, create such links with
+         *      \usepackage{hyperref}
+         *      \href{run:<movie file>}{<placeholder content>}
+         * Since the video will take the shape of the placeholder content, you
+         * probably want to use a frame from the movie to get the right aspect
+         * ratio.
+         */
+        public override ActionMapping? new_from_link_mapping(Poppler.LinkMapping mapping,
+                PresentationController controller, Poppler.Document document) {
+            if (mapping.action.type != Poppler.ActionType.LAUNCH)
+                return null;
+
+            var file = ((Poppler.ActionLaunch*)mapping.action).file_name;
+            var splitfile = file.split("?", 2);
+            file = splitfile[0];
+            var querystring = "";
+            if (splitfile.length == 2)
+                querystring = splitfile[1];
+            var queryarray = querystring.split("&");
+            var autostart = "autostart" in queryarray;
+            var loop = "loop" in queryarray;
+
+            string uri = filename_to_uri(file, controller.get_pdf_url());
+            bool uncertain;
+            var ctype = GLib.ContentType.guess(uri, null, out uncertain);
+            if (!("video" in ctype))
+                return null;
+
+            var type = Type.from_instance(this);
+            var new_obj = GLib.Object.new(type) as ActionMapping;
+            this.init_other(new_obj, mapping.area, controller, document, uri, autostart, loop);
+            return new_obj;
+        }
+
+        /**
+         * Create a new Movie from an annotation mapping, if the annotation is a
+         * screen annotation with a video file or a movie annotation.  Various
+         * options to modify the behavior of the playback are not yet supported,
+         * since they're missing from poppler.
+         *
+         * In LaTeX, create screen annotations with
+         *      \usepackage{movie15}
+         *      \includemovie[text=<placeholder content>]{}{}{<movie file>}
+         * The movie size is determined by the size of the placeholder content, so
+         * a frame from the movie is a good choice.  Note that the poster, autoplay,
+         * and repeat options are not yet supported.  (Also note that movie15 is
+         * deprecated, but it works as long as you run ps2pdf with the -dNOSAFER flag.)
+         *
+         * In LaTeX, create movie annotations with
+         *      \usepackage{multimedia}
+         *      \movie[<options>]{<placeholder content>}{<movie file>}
+         * The movie size is determined from the size of the placeholder content or
+         * the width and height options.  Note that the autostart, loop/repeat, and
+         * poster options are not yet supported.
+         */
+        public override ActionMapping? new_from_annot_mapping(Poppler.AnnotMapping mapping,
+                PresentationController controller, Poppler.Document document) {
+            var annot = mapping.annot;
+            string uri;
+            bool temp = false;
+            switch (annot.get_annot_type()) {
+            case Poppler.AnnotType.SCREEN:
+                if (!("video" in annot.get_contents()))
+                    return null;
+
+                var action = ((Poppler.AnnotScreen) annot).get_action();
+                var movie = (Poppler.Media) action.movie.movie;
+
+                if (movie.is_embedded()) {
+                    string tmp_fn;
+                    int fh;
+                    try {
+                        fh = FileUtils.open_tmp("pdfpc-XXXXXX", out tmp_fn);
+                    } catch (FileError e) {
+                        warning("Could not create temp file: %s", e.message);
+                        return null;
+                    }
+                    FileUtils.close(fh);
+                    try {
+                        movie.save(tmp_fn);
+                    } catch (Error e) {
+                        warning("Could not save temp file: %s", e.message);
+                        return null;
+                    }
+                    uri = "file://" + tmp_fn;
+                    temp = true;
+                } else {
+                    string file = movie.get_filename();
+                    if (file == null) {
+                        warning("Movie not embedded and has no file name");
+                        return null;
+                    }
+                    uri = filename_to_uri(file, controller.get_pdf_url());
+                    temp = false;
+                }
+                break;
+            case Poppler.AnnotType.MOVIE:
+                var movie = ((Poppler.AnnotMovie) annot).get_movie();
+                if (movie.need_poster())
+                    warning("Movie requests poster.  Not yet supported.");
+                string file = movie.get_filename();
+                if (file == null) {
+                    warning("Movie has no file name");
+                    return null;
+                }
+                uri = filename_to_uri(file, controller.get_pdf_url());
+                temp = false;
+                break;
+            default:
+                return null;
+            }
+
+            var type = Type.from_instance(this);
+            var new_obj = GLib.Object.new(type) as ActionMapping;
+            this.init_other(new_obj, mapping.area, controller, document, uri, false, false, temp);
+            return new_obj;
+        }
+
+        /**
+         * Set up the gstreamer pipeline.
+         */
+        protected void establish_pipeline(string uri) {
+            var bin = new Gst.Bin("bin");
+            var tee = Gst.ElementFactory.make("tee", "tee");
+            bin.add_many(tee);
+            bin.add_pad(new Gst.GhostPad("sink", tee.get_pad("sink")));
+            Gdk.Rectangle rect;
+            int n = 0;
+            uint xid;
+            while (true) {
+                xid = this.controller.overlay_pos(n, this.area, out rect);
+                if (xid == 0)
+                    break;
+                var sink = Gst.ElementFactory.make("xvimagesink", @"sink$n");
+                var queue = Gst.ElementFactory.make("queue", @"queue$n");
+                bin.add_many(queue,sink);
+                tee.link(queue);
+                var ad_element = this.link_additional(n, queue, bin, rect);
+                ad_element.link(sink);
+
+                var xoverlay = sink as Gst.XOverlay;
+                xoverlay.set_window_handle(xid);
+                xoverlay.handle_events(false);
+                xoverlay.set_render_rectangle(rect.x, rect.y, rect.width, rect.height);
+                n++;
+            }
+
+            this.pipeline = Gst.ElementFactory.make("playbin2", "playbin");
+            this.pipeline.uri = uri;
+            this.pipeline.video_sink = bin;
+            var bus = this.pipeline.get_bus();
+            bus.add_signal_watch();
+            bus.message["error"] += this.on_message;
+            bus.message["eos"] += this.on_eos;
+        }
+
+        /**
+         * Provides a place for subclasses to hook additional elements into
+         * the pipeline.  n is which output we're dealing with; 0 is where
+         * specialized controls should appear.  Additional elements should
+         * be added to bin and linked from source.  The last element linked
+         * should be returned, so that the tail end of the pipeline can be
+         * attached.
+         *
+         * This stub does nothing.
+         */
+        protected virtual Gst.Element link_additional(int n, Gst.Element source, Gst.Bin bin,
+                                                      Gdk.Rectangle rect) {
+            return source;
+        }
+
+        /**
+         * Utility function for converting filenames to uris.
+         */
+        public string filename_to_uri(string file, string pdf_url) {
+            Regex uriRE = null;
+            try {
+                uriRE = new Regex("^[a-z]*://");
+            } catch (Error error) {
+                // Won't happen
+            }
+            if (uriRE.match(file))
+                return file;
+            if (GLib.Path.is_absolute(file))
+                return "file://" + file;
+            var dirname = GLib.Path.get_dirname(pdf_url);
+            return GLib.Path.build_filename(dirname, file);
+        }
+
+        /**
+         * Play the movie, rewinding to the beginning if we had reached the
+         * end.
+         */
+        public virtual void play() {
+            if (this.eos) {
+                this.eos = false;
+                this.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0);
+            }
+            this.pipeline.set_state(Gst.State.PLAYING);
+        }
+
+        /**
+         * Pause playback.
+         */
+        public virtual void pause() {
+            this.pipeline.set_state(Gst.State.PAUSED);
+        }
+
+        /**
+         * Stop playback.
+         */
+        public virtual void stop() {
+            this.pipeline.set_state(Gst.State.NULL);
+        }
+
+        /**
+         * Pause if playing, as vice versa.
+         */
+        public virtual void toggle_play() {
+            Gst.State state;
+            Gst.ClockTime time = Gst.util_get_timestamp();
+            this.pipeline.get_state(out state, null, time);
+            if (state == Gst.State.PLAYING)
+                this.pause();
+            else
+                this.play();
+        }
+
+        /**
+         * Basic printout of error messages on the pipeline.
+         */
+        public virtual void on_message(Gst.Bus bus, Gst.Message message) {
+            GLib.Error err;
+            string debug;
+            message.parse_error(out err, out debug);
+            stderr.printf("Gstreamer error %s\n", err.message);
+        }
+
+        /**
+         * Mark reaching the end of stream, and set state to paused.
+         */
+        public virtual void on_eos(Gst.Bus bus, Gst.Message message) {
+            if (this.loop) {
+                this.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0);
+            } else {
+                // Can't seek to beginning w/o updating output, so mark to seek later
+                this.eos = true;
+                this.pause();
+            }
+        }
+
+        /**
+         * Play and pause on mouse clicks.
+         */
+        public override bool on_button_press(Gtk.Widget widget, Gdk.EventButton event) {
+            this.toggle_play();
+            return true;
+        }
+
+        /**
+         * When we leave the page, stop the movie and delete any temporary files.
+         */
+        public override void deactivate() {
+            this.stop();
+            if (this.temp != "")
+                if (FileUtils.unlink(this.temp) != 0)
+                    warning("Problem deleting temp file %s", this.temp);
+        }
+    }
+
+    /**
+     * A Movie with overlaid controls; specifically a draggable progress bar.
+     */
+    public class ControlledMovie: Movie {
+        /**
+         * The screen rectangle associated with the movie.
+         */
+        protected Gdk.Rectangle rect;
+
+        /**
+         * Data on how the movie playback must fit on the page.
+         */
+        protected double scalex;
+        protected double scaley;
+        protected int vheight;
+
+        /**
+         * The length of the movie, in nanoseconds (!).
+         */
+        protected int64 duration;
+
+        /**
+         * Settings for the appearance of the progress bar.
+         */
+        protected double seek_bar_height = 20;
+        protected double seek_bar_padding = 2;
+
+        /**
+         * A timeout signal is used to update the GUI when the movie is paused,
+         * since we won't get new frames.
+         */
+        protected uint refresh_timeout = 0;
+
+        /**
+         * Flags about the current state of mouse interaction.
+         */
+        protected bool in_seek_bar = false;
+        protected bool mouse_drag = false;
+        protected bool drag_was_playing;
+
+        /**
+         * Basic constructor does nothing.
+         */
+        public ControlledMovie() {
+            base();
+        }
+
+        /**
+         * The initialization unique to this class.  See the documentation for
+         * Movie.init_other that attempts to justify this ugliness.
+         */
+        public override void init_other(ActionMapping other, Poppler.Rectangle area,
+                PresentationController controller, Poppler.Document document, string file, bool autostart, bool loop, bool temp=false) {
+            base.init_other(other, area, controller, document, file, autostart, loop, temp);
+            var movie = other as ControlledMovie;
+            controller.main_view.motion_notify_event.connect(movie.on_motion);
+            controller.main_view.button_release_event.connect(movie.on_button_release);
+        }
+
+        /**
+         * Hook up the elements to draw the controls to the first output leg.
+         */
+        protected override Gst.Element link_additional(int n, Gst.Element source, Gst.Bin bin,
+                                                       Gdk.Rectangle rect) {
+            if (n != 0)
+                return source;
+
+            this.rect = rect;
+
+            dynamic Gst.Element overlay;
+            Gst.Element adaptor2;
+            try {
+                var scale = gst_element_make("videoscale", "scale");
+                var rate = gst_element_make("videorate", "rate");
+                var adaptor1 = gst_element_make("ffmpegcolorspace", "adaptor1");
+                adaptor2 = gst_element_make("ffmpegcolorspace", "adaptor2");
+                overlay = gst_element_make("cairooverlay", "overlay");
+                var caps = Gst.Caps.from_string(
+                    "video/x-raw-rgb," + // Same as cairooverlay; hope to minimize transformations
+                    "framerate=[25/1,2147483647/1]," + // At least 25 fps
+                    @"width=$(rect.width),height=$(rect.height)"
+                );
+                dynamic Gst.Element filter = gst_element_make("capsfilter", "filter");
+                filter.caps = caps;
+                bin.add_many(adaptor1, adaptor2, overlay, scale, rate, filter);
+                if (!source.link_many(rate, scale, adaptor1, filter, overlay, adaptor2))
+                    throw new PipelineError.Linking("Could not link pipeline.");
+            } catch (PipelineError err) {
+                warning(@"Error creating control pipeline: $(err.message)");
+                return source;
+            }
+
+            overlay.draw.connect(this.on_draw);
+            overlay.caps_changed.connect(this.on_prepare);
+
+            return adaptor2;
+        }
+
+        /**
+         * When we find out the properties of the movie, we can work out how it
+         * needs to be scaled to fit in the alloted area.  (This is only important
+         * for the view with the controls.)
+         */
+        public void on_prepare(Gst.Element overlay, Gst.Caps caps){
+            int width = -1, height = -1;
+            Gst.VideoFormat format = Gst.VideoFormat.UNKNOWN;
+            Gst.video_format_parse_caps(caps, ref format, ref width, ref height);
+            scalex = 1.0*width / rect.width;
+            scaley = 1.0*height / rect.height;
+            vheight = height;
+
+            var tformat = Gst.Format.TIME;
+            overlay.query_duration(ref tformat, out duration);
+        }
+
+        /**
+         * Everytime we get a new frame, update the progress bar.
+         */
+        public void on_draw(Gst.Element overlay, Cairo.Context cr, uint64 timestamp, uint64 duration) {
+            // Transform to work from bottom left, in screen coordinates
+            cr.translate(0, this.vheight);
+            cr.scale(this.scalex, -this.scaley);
+
+            this.draw_seek_bar(cr, timestamp);
+        }
+
+        private void draw_seek_bar(Cairo.Context cr, uint64 timestamp) {
+            double fraction = 1.0*timestamp / this.duration;
+            if (this.in_seek_bar || this.mouse_drag) {
+                var bar_end = fraction * (rect.width - 2*this.seek_bar_padding);
+                cr.rectangle(0, 0, rect.width, this.seek_bar_height);
+                cr.set_source_rgba(0,0,0,0.8);
+                cr.fill();
+                cr.rectangle(this.seek_bar_padding, this.seek_bar_padding,
+                            bar_end, this.seek_bar_height-4);
+                cr.set_source_rgba(1,1,1,0.8);
+                cr.fill();
+
+                var time_in_sec = (int)(timestamp / Gst.SECOND);
+                var timestring = "%i:%02i".printf(time_in_sec/60, time_in_sec%60);
+                var dur_in_sec = (int)(this.duration / Gst.SECOND);
+                var durstring = "%i:%02i".printf(dur_in_sec/60, dur_in_sec%60);
+                Cairo.TextExtents te;
+                Cairo.FontOptions fo = new Cairo.FontOptions();
+                fo.set_antialias(Cairo.Antialias.GRAY);
+                cr.set_font_options(fo);
+                cr.select_font_face("sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL);
+                cr.set_font_size(this.seek_bar_height - 2*seek_bar_padding);
+
+                cr.text_extents(durstring, out te);
+                if ((bar_end + te.width + 4*this.seek_bar_padding) < rect.width) {
+                    cr.move_to(rect.width - te.width - 2*this.seek_bar_padding,
+                               this.seek_bar_height/2 - te.height/2);
+                    cr.set_source_rgba(0.8,0.8,0.8,1);
+                    cr.save();
+                    cr.scale(1, -1);
+                    cr.show_text(durstring);
+                    cr.restore();
+                }
+
+                cr.text_extents(timestring, out te);
+                if (bar_end > te.width) {
+                    cr.move_to(bar_end - te.width, this.seek_bar_height/2 - te.height/2);
+                    cr.set_source_rgba(0,0,0,1);
+                } else {
+                    cr.move_to(bar_end + 2*this.seek_bar_padding, this.seek_bar_height/2 - te.height/2);
+                    cr.set_source_rgba(0.8,0.8,0.8,1);
+                }
+                cr.save();
+                cr.scale(1,-1);
+                cr.show_text(timestring);
+                cr.restore();
+
+            } else {
+                cr.rectangle(0, 0, rect.width, 4);
+                cr.set_source_rgba(0,0,0,0.8);
+                cr.fill();
+                cr.rectangle(1, 1, fraction * (rect.width - 2), 2);
+                cr.set_source_rgba(1,1,1,0.8);
+                cr.fill();
+            }
+        }
+
+        /**
+         * Set a flag about whether the mouse is currently in the progress bar.
+         */
+        private void set_mouse_in(double mx, double my, out double x, out double y) {
+            x = mx - rect.x;
+            y = rect.y + rect.height - my;
+            this.in_seek_bar = (x > 0 && x < rect.width && y > 0 && y < seek_bar_height);
+        }
+
+        /**
+         * Seek to the time indicated by the mouse's position on the progress bar.
+         */
+        public int64 mouse_seek(double x, double y) {
+            double seek_fraction = (x - this.seek_bar_padding) / (rect.width - 2*this.seek_bar_padding);
+            if (seek_fraction < 0) seek_fraction = 0;
+            if (seek_fraction > 1) seek_fraction = 1;
+            var seek_time = (int64)(seek_fraction * this.duration);
+            this.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, seek_time);
+            return seek_time;
+        }
+
+        /**
+         * Seek if we're dragging the progress bar.
+         */
+        public bool on_motion(Gdk.EventMotion event) {
+            double x, y;
+            this.set_mouse_in(event.x, event.y, out x, out y);
+            if (this.mouse_drag)
+                this.mouse_seek(x, y);
+            return false;
+        }
+
+        /**
+         * If we click outside of the progress bar, toggle the playing state.
+         * Inside the progress bar, pause or stop the timeout, and start the
+         * drag state.
+         */
+        public override bool on_button_press(Gtk.Widget widget, Gdk.EventButton event) {
+            Gst.State state;
+            Gst.ClockTime time = Gst.util_get_timestamp();
+            this.pipeline.get_state(out state, null, time);
+            if (state == Gst.State.NULL || widget != this.controller.main_view) {
+                this.toggle_play();
+                return true;
+            }
+
+            double x, y;
+            this.set_mouse_in(event.x, event.y, out x, out y);
+            if (!this.in_seek_bar)
+                this.toggle_play();
+            else {
+                this.mouse_drag = true;
+                this.drag_was_playing = (this.pipeline.current_state == Gst.State.PLAYING);
+                this.pause();
+                this.mouse_seek(x, y);
+                this.stop_refresh();
+            }
+            return true;
+        }
+
+        /**
+         * Stop the drag state and restart either playback or the timeout,
+         * depending on the previous state.
+         */
+        public bool on_button_release(Gdk.EventButton event) {
+            double x, y;
+            this.set_mouse_in(event.x, event.y, out x, out y);
+            if (this.mouse_drag) {
+                var seek_time = this.mouse_seek(x, y);
+                if (this.drag_was_playing || this.eos) {
+                    this.eos = false;
+                    this.play();
+                } else
+                    // Otherwise, time resets to 0 (don't know why).
+                    this.start_refresh_time(seek_time);
+            }
+            this.mouse_drag = false;
+            return false;
+        }
+
+        /**
+         * Start a timeout event to refresh the GUI every 50ms.  To be used when
+         * the movie is paused, so that the controls can still be updated.
+         */
+        public void start_refresh() {
+            if (this.refresh_timeout != 0)
+                return;
+            int64 curr_time;
+            var tformat = Gst.Format.TIME;
+            this.pipeline.query_position(ref tformat, out curr_time);
+            this.start_refresh_time(curr_time);
+        }
+
+        /**
+         * In the timeout, we seek to the current time, which is enough to force
+         * gstreamer to redraw the current frame.
+         */
+        public void start_refresh_time(int64 curr_time) {
+            if (this.eos)
+                // Seeking to the very end won't refresh the output.
+                curr_time -= 1;
+            if (this.refresh_timeout != 0)
+                Source.remove(this.refresh_timeout);
+            this.refresh_timeout = Timeout.add(50, () => {
+                this.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, curr_time);
+                return true;
+            } );
+        }
+
+        /**
+         * Stop the refresh timeout.
+         */
+        public void stop_refresh() {
+            if (this.refresh_timeout == 0)
+                return;
+            Source.remove(this.refresh_timeout);
+            this.refresh_timeout = 0;
+        }
+
+        /**
+         * Stop the refresh timeout when we start playing.
+         */
+        public override void play() {
+            this.stop_refresh();
+            base.play();
+        }
+
+        /**
+         * Start the refresh timeout when we pause.
+         */
+        public override void pause() {
+            base.pause();
+            this.start_refresh();
+        }
+    }
+}
--- pdf-presenter-console-3.1.1.orig/src/classes/cache_status.vala
+++ pdf-presenter-console-3.1.1/src/classes/cache_status.vala
@@ -4,26 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-using Cairo;
-
 namespace pdfpc {
     /**
      * Interface for showing the fill status of all registered pdf image caches
@@ -41,36 +37,24 @@ namespace pdfpc {
         protected int max_value = 0;
 
         /**
-         * The function for updating the display with the current progress
+         * The signal for updating the display with the current progress
          */
-        public delegate void UpdateFunction(double progress);
-        UpdateFunction? update_function = null;
+        public signal void update_progress(double progress);
 
         /**
-         * The function to notify that we are finished
+         * The signal to notify that we are finished
          */
-        public delegate void UpdateComplete();
-        UpdateComplete? update_complete = null;
+        public signal void update_complete();
 
         /**
-         * Register the functions for updating
-         */
-        public void register_update(UpdateFunction update, UpdateComplete complete) {
-            this.update_function = update;
-            this.update_complete = complete;
-        }
-    
-        /**
          * Draw the current state to the widgets surface
          */
         public void update() {
             // Only draw if the widget is actually added to some parent
             if (this.current_value == this.max_value) {
-                if (update_complete != null)
-                    update_complete();
+                update_complete();
             } else {
-                if (update_function != null)
-                    update_function((double)this.current_value / this.max_value);
+                update_progress((double)this.current_value / this.max_value);
             }
         }
 
@@ -79,7 +63,7 @@ namespace pdfpc {
          */
         public void monitor_view( View.Prerendering view ) {
             view.prerendering_started.connect( (v) => {
-                this.max_value += (int)((View.Base)v).get_renderer().get_metadata().get_slide_count();
+                this.max_value += (int)((View.Base)v).get_renderer().metadata.get_slide_count();
             });
             view.slide_prerendered.connect( () => {
                 ++this.current_value;
--- pdf-presenter-console-3.1.1.orig/src/classes/configFileReader.vala
+++ pdf-presenter-console-3.1.1/src/classes/configFileReader.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -26,7 +26,7 @@ namespace pdfpc {
 
         public ConfigFileReader(PresentationController controller) {
             this.presentation_controller = controller;
-            uint supportedModifiers = Gdk.ModifierType.SHIFT_MASK 
+            uint supportedModifiers = Gdk.ModifierType.SHIFT_MASK
                                       | Gdk.ModifierType.CONTROL_MASK
                                       | Gdk.ModifierType.META_MASK
                                     ;
@@ -77,7 +77,7 @@ namespace pdfpc {
                 this.presentation_controller.bind(keycode, modMask, fields[2]);
             }
         }
-        
+
         private void unbindKey(string wholeLine, string[] fields) {
             if (fields.length != 2) {
                 stderr.printf("Bad unbind specification: %s\n", wholeLine);
@@ -125,10 +125,10 @@ namespace pdfpc {
 
         public void readConfig(string fname) {
             var file = File.new_for_path(fname);
-            var splitRegex = new Regex("\\s\\s*");
-            var commentRegex = new Regex("\\s*#.*$");
             uint8[] raw_datau8;
             try {
+                var splitRegex = new Regex("\\s\\s*");
+                var commentRegex = new Regex("\\s*#.*$");
                 file.load_contents(null, out raw_datau8, null);
                 string[] lines = ((string) raw_datau8).split("\n");
                 for (int i=0; i<lines.length; ++i) {
--- pdf-presenter-console-3.1.1.orig/src/classes/metadata/base.vala
+++ pdf-presenter-console-3.1.1/src/classes/metadata/base.vala
@@ -4,24 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
 namespace pdfpc {
     /**
      * Metadata base class describing the basic metadata needed for every
--- pdf-presenter-console-3.1.1.orig/src/classes/metadata/pdf.vala
+++ pdf-presenter-console-3.1.1/src/classes/metadata/pdf.vala
@@ -4,24 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
 namespace pdfpc.Metadata {
     /**
      * Metadata for Pdf files
@@ -29,7 +27,7 @@ namespace pdfpc.Metadata {
     public class Pdf: Base
     {
         protected string? pdf_fname = null;
-        protected string? pdf_url = null;
+        public string? pdf_url = null;
         protected string? pdfpc_url = null;
 
         /**
@@ -40,12 +38,17 @@ namespace pdfpc.Metadata {
         /**
          * Pdf page width
          */
-        protected double page_width;
+        protected double original_page_width;
 
         /**
          * Pdf page height
          */
-        protected double page_height;
+        protected double original_page_height;
+
+        /**
+         * Indicates if pages contains also notes and there position (e. g. when using latex beamer)
+         */
+        protected NotesPosition notes_position;
 
         /**
          * Number of pages in the pdf document
@@ -177,7 +180,7 @@ namespace pdfpc.Metadata {
             var file = File.new_for_commandline_arg(fname);
 
             if (fname.length < 6 || fname[l-6:l] != ".pdfpc") {
-                this.pdf_fname = file.get_basename(); 
+                this.pdf_fname = file.get_basename();
                 this.pdf_url = file.get_uri();
                 string pdf_basefname = file.get_basename();
                 int extension_index = pdf_basefname.last_index_of(".");
@@ -186,7 +189,16 @@ namespace pdfpc.Metadata {
                 this.pdfpc_url = pdfpc_file.get_uri();
             } else {
                 this.pdfpc_url = file.get_uri();
-            } 
+            }
+        }
+
+        /**
+         * Called on quit
+         */
+        public void quit() {
+            this.save_to_disk();
+            foreach (var mapping in this.action_mapping)
+                mapping.deactivate();
         }
 
         /**
@@ -199,14 +211,10 @@ namespace pdfpc.Metadata {
                               + format_end_user_slide()
                               + format_notes();
             try {
-                if ( contents != "" ) {
+                var pdfpc_file = File.new_for_uri(this.pdfpc_url);
+                if (contents != "" || pdfpc_file.query_exists()) {
                     contents = "[file]\n" + this.pdf_fname + "\n" + contents;
-                    var pdfpc_file = File.new_for_uri(this.pdfpc_url);
-                    FileUtils.set_contents(pdfpc_file.get_path(), contents, contents.length-1);
-                } else { // We do not need to write anything. Delete the file if it exists
-                    var file = File.new_for_uri(this.pdfpc_url);
-                    if (file.query_exists())
-                        file.delete();
+                    pdfpc_file.replace_contents(contents.data, null, false, FileCreateFlags.NONE, null);
                 }
             } catch (Error e) {
                 error("%s", e.message);
@@ -244,7 +252,7 @@ namespace pdfpc.Metadata {
          */
         protected string format_notes() {
             string contents = "";
-            if ( this.notes.has_notes() ) 
+            if ( this.notes.has_notes() )
                 contents += ("[notes]\n" + this.notes.format_to_save());
             return contents;
         }
@@ -257,11 +265,32 @@ namespace pdfpc.Metadata {
         }
 
         /**
+         * Fill the slide notes from pdf text annotations.
+         */
+        private void notes_from_document() {
+            for(int i = 0; i < this.page_count; i++) {
+                var page = this.document.get_page(i);
+                List<Poppler.AnnotMapping> anns = page.get_annot_mapping();
+                foreach(unowned Poppler.AnnotMapping am in anns) {
+                    var a = am.annot;
+                    switch(a.get_annot_type()) {
+                        case Poppler.AnnotType.TEXT:
+                            this.notes.set_note(a.get_contents(), real_slide_to_user_slide(i));
+                            break;
+                    }
+                }
+                //page.free_annot_mapping(anns);
+            }
+        }
+
+        /**
          * Base constructor taking the file url to the pdf file
          */
-        public Pdf( string fname ) {
+        public Pdf( string fname, NotesPosition notes_position ) {
             base( fname );
 
+            this.notes_position = notes_position;
+
             fill_path_info(fname);
             notes = new slides_notes();
             skips_by_user = false;
@@ -269,16 +298,15 @@ namespace pdfpc.Metadata {
             if (File.new_for_uri(this.pdfpc_url).query_exists())
                 parse_pdfpc_file(out skip_line);
             this.document = this.open_pdf_document( this.pdf_url );
-            
+
             // Cache some often used values to minimize thread locking in the
             // future.
-            MutexLocks.poppler.lock();
             this.page_count = this.document.get_n_pages();
-            this.document.get_page( 0 ).get_size( 
-                out this.page_width,
-                out this.page_height
+            this.document.get_page( 0 ).get_size(
+                out this.original_page_width,
+                out this.original_page_height
             );
-    
+
             if (!skips_by_user) {
                 // Auto-detect which pages to skip
                 string previous_label = null;
@@ -294,9 +322,11 @@ namespace pdfpc.Metadata {
             } else {
                 parse_skip_line(skip_line);
             }
-            MutexLocks.poppler.unlock();
+
+            // Prepopulate notes from annotations
+            notes_from_document();
         }
-    
+
         /**
          * Return the number of pages in the pdf document
          */
@@ -398,21 +428,83 @@ namespace pdfpc.Metadata {
         /**
          * Return the width of the first page of the loaded pdf document.
          *
+         * If slides contains also notes, this method returns the width of the content part only
+         *
          * In presentations all pages will have the same size in most cases,
          * therefore this value is assumed to be useful.
          */
         public double get_page_width() {
-            return this.page_width;
+            if (    this.notes_position == NotesPosition.LEFT
+                 || this.notes_position == NotesPosition.RIGHT) {
+                 return this.original_page_width / 2;
+            } else {
+                return this.original_page_width;
+            }
         }
 
         /**
          * Return the height of the first page of the loaded pdf document.
          *
+         * If slides contains also notes, this method returns the height of the content part only
+         *
          * In presentations all pages will have the same size in most cases,
          * therefore this value is assumed to be useful.
          */
         public double get_page_height() {
-            return this.page_height;
+            if (    this.notes_position == NotesPosition.TOP
+                 || this.notes_position == NotesPosition.BOTTOM) {
+                 return this.original_page_height / 2;
+            } else {
+                return this.original_page_height;
+            }
+        }
+
+        /**
+         * Return the horizontal offset of the given area on the page
+         */
+        public double get_horizontal_offset(Area area) {
+            switch (area) {
+                case Area.CONTENT:
+                    switch (this.notes_position) {
+                        case NotesPosition.LEFT:
+                            return this.original_page_width / 2;
+                        default:
+                            return 0;
+                    }
+                case Area.NOTES:
+                    switch (this.notes_position) {
+                        case NotesPosition.RIGHT:
+                            return this.original_page_width / 2;
+                        default:
+                            return 0;
+                    }
+                default:
+                    return 0;
+            }
+        }
+
+        /**
+         * Return the vertical offset of the given area on the page
+         */
+        public double get_vertical_offset(Area area) {
+            switch (area) {
+                case Area.CONTENT:
+                    switch (this.notes_position) {
+                        case NotesPosition.TOP:
+                            return this.original_page_height / 2;
+                        default:
+                            return 0;
+                    }
+                case Area.NOTES:
+                    switch (this.notes_position) {
+                        case NotesPosition.BOTTOM:
+                            return this.original_page_height / 2;
+                        default:
+                            return 0;
+                    }
+                default:
+                    return 0;
+            }
         }
 
         /**
@@ -448,22 +540,113 @@ namespace pdfpc.Metadata {
          */
         protected Poppler.Document open_pdf_document( string url ) {
             var file = File.new_for_uri( url );
-            
+
             Poppler.Document document = null;
 
-            MutexLocks.poppler.lock();
             try {
-                document = new Poppler.Document.from_file( 
+                document = new Poppler.Document.from_file(
                     file.get_uri(),
                     null
                 );
+            } catch( GLib.Error e ) {
+                GLib.printerr( "Unable to open pdf file: %s\n", e.message );
+                Posix.exit(1);
             }
-            catch( GLib.Error e ) {
-                error( "Unable to open pdf file: %s", e.message );
-            }            
-            MutexLocks.poppler.unlock();
 
             return document;
         }
+
+        /**
+         * Variables used to keep track of the action mappings for the current
+         * page.
+         */
+        private int mapping_page_num = -1;
+        private GLib.List<ActionMapping> action_mapping;
+        private ActionMapping[] blanks = {new ControlledMovie(), new LinkAction()};
+        public weak PresentationController controller = null;
+
+        /**
+         * Return the action mappings (link and annotation mappings) for the
+         * specified page.  If that page is different from the previous one,
+         * destroy the existing action mappings and create new mappings for
+         * the new page.
+         */
+        public unowned GLib.List<ActionMapping> get_action_mapping( int page_num ) {
+            if (page_num != this.mapping_page_num) {
+                foreach (var mapping in this.action_mapping)
+                    mapping.deactivate();
+                this.action_mapping = null; //.Is this really the correct way to clear a list?
+
+                GLib.List<Poppler.LinkMapping> link_mappings;
+                link_mappings = this.get_document().get_page(page_num).get_link_mapping();
+                foreach (unowned Poppler.LinkMapping mapping in link_mappings) {
+                    foreach (var blank in blanks) {
+                        var action = blank.new_from_link_mapping(mapping, this.controller, this.document);
+                        if (action != null) {
+                            this.action_mapping.append(action);
+                            break;
+                        }
+                    }
+                }
+                // Free the mapping memory; already in lock
+                //Poppler.Page.free_link_mapping(link_mappings);
+
+                GLib.List<Poppler.AnnotMapping> annot_mappings;
+                annot_mappings = this.get_document().get_page(page_num).get_annot_mapping();
+                foreach (unowned Poppler.AnnotMapping mapping in annot_mappings) {
+                    foreach (var blank in blanks) {
+                        var action = blank.new_from_annot_mapping(mapping, this.controller, this.document);
+                        if (action != null) {
+                            this.action_mapping.append(action);
+                            break;
+                        }
+                    }
+                }
+                // Free the mapping memory; already in lock
+                //Poppler.Page.free_annot_mapping(annot_mappings);
+
+                this.mapping_page_num = page_num;
+            }
+            return this.action_mapping;
+        }
+    }
+
+    /**
+     * Defines an area on a pdf page
+     */
+    public enum Area {
+        FULL,
+        CONTENT,
+        NOTES;
+    }
+
+    /**
+     * Indicates if a pdf has also notes on the pages (and there position)
+     */
+    public enum NotesPosition {
+        NONE,
+        TOP,
+        BOTTOM,
+        RIGHT,
+        LEFT;
+
+        public static NotesPosition from_string(string? position) {
+            if (position == null) {
+                return NONE;
+            }
+
+            switch (position.down()) {
+                case "left":
+                    return LEFT;
+                case "right":
+                    return RIGHT;
+                case "top":
+                    return TOP;
+                case "bottom":
+                    return BOTTOM;
+                default:
+                    return NONE;
+            }
+        }
     }
 }
--- pdf-presenter-console-3.1.1.orig/src/classes/metadata/slides_notes.vala
+++ pdf-presenter-console-3.1.1/src/classes/metadata/slides_notes.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
--- pdf-presenter-console-3.1.1.orig/src/classes/options.vala
+++ pdf-presenter-console-3.1.1/src/classes/options.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -36,12 +36,12 @@ namespace pdfpc {
          * Commandline option to force using only one screen.
          */
         public static bool single_screen = false;
-        
+
         /**
          * Commandline option to run in windowed mode
          */
         public static bool windowed = false;
-        
+
         /**
          * Commandline option which allows the complete disabling of slide caching
          */
@@ -99,5 +99,15 @@ namespace pdfpc {
          * Show the actions supported in the config file(s)
          */
         public static bool list_actions = false;
+
+        /**
+         * Position of notes on slides
+         */
+        public static string? notes_position = null;
+        
+        /**
+         * Size of the presenter window
+         */
+        public static string? size = null;
     }
 }
--- pdf-presenter-console-3.1.1.orig/src/classes/presentation_controller.vala
+++ pdf-presenter-console-3.1.1/src/classes/presentation_controller.vala
@@ -4,67 +4,61 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gee;
-
 namespace pdfpc {
     /**
      * Controller handling all the triggered events/signals
      */
-    public class PresentationController: Object {
+    public class PresentationController : Object {
 
         /**
          * The currently displayed slide
          */
-        protected int current_slide_number;
+        public int current_slide_number { get; protected set; }
 
         /**
          * The current slide in "user indexes"
          */
-        protected int current_user_slide_number;
+        public int current_user_slide_number { get; protected set; }
 
         /**
          * Stores if the view is faded to black
          */
-        protected bool faded_to_black = false;
+        public bool faded_to_black { get; protected set; default = false; }
 
         /**
          * Stores if the view is frozen
          */
-        protected bool frozen = false;
-
-        /**
-         * A flag signaling if we allow for a black slide at the end. Tis is
-         * useful for the next view and (for some presenters) also for the main
-         * view.
-         */
-        protected bool black_on_end;
+        public bool frozen { get; protected set; default = false; }
 
         /**
          * The number of slides in the presentation
          */
-        protected int n_slides;
+        public int n_slides { get; protected set; }
 
         /**
-         * Controllables which are registered with this presentation controller.
+         * The number of user slides
          */
-        protected GLib.List<Controllable> controllables;
+        public int user_n_slides {
+            get {
+                return this.metadata.get_user_slide_count();
+            }
+        }
 
         /**
          * Key modifiers that we support
@@ -74,8 +68,20 @@ namespace pdfpc {
         /**
          * Ignore input events. Useful e.g. for editing notes.
          */
-        protected bool ignore_keyboard_events = false;
-        protected bool ignore_mouse_events = false;
+        public bool ignore_keyboard_events { get; protected set; default = false; }
+        public bool ignore_mouse_events { get; protected set; default = false; }
+
+        /**
+         * A flag signaling if we allow for a black slide at the end. Tis is
+         * useful for the next view and (for some presenters) also for the main
+         * view.
+         */
+        protected bool black_on_end;
+
+        /**
+         * Controllables which are registered with this presentation controller.
+         */
+        protected GLib.List<Controllable> controllables;
 
         /**
          * The metadata of the presentation
@@ -87,7 +93,7 @@ namespace pdfpc {
          * skips
          */
         protected Window.Overview overview;
-        public bool overview_shown = false;
+        protected bool overview_shown = false;
 
         /**
          * Disables processing of multiple Keypresses at the same time (debounce)
@@ -105,22 +111,10 @@ namespace pdfpc {
          */
         protected TimerLabel timer;
 
-        /**
-         * The key bindings as a map from keycodes to actions
-         *
-         * Vala doesn't allow for delegates as the values in a HashMap (yet?). See
-         * http://stackoverflow.com/questions/6145635/gee-hashmap-containing-methods-as-values
-         * for this solution.
-         */
-        protected class KeyAction {
-            public delegate void KeyActionDelegate();
-            public KeyActionDelegate d;
-            public KeyAction(KeyActionDelegate d) {
-                this.d = d;
-            }
-        }
-        protected HashMap<string, KeyAction> actionNames;
-        protected class KeyDef {
+        protected delegate void callback();
+        protected SimpleActionGroup action_group = new SimpleActionGroup();
+
+        protected class KeyDef : GLib.Object, Gee.Hashable<KeyDef> {
             public uint keycode { get; set; }
             public uint modMask { get; set; }
 
@@ -129,26 +123,30 @@ namespace pdfpc {
                 this.modMask = m;
             }
 
-            public static uint hash(void *_a) {
-                KeyDef a = (KeyDef)_a;
-                var uintHashFunc = Functions.get_hash_func_for(Type.from_name("uint"));
-                return uintHashFunc(a.keycode | a.modMask); // | is probable the best combinator, but for this small application it should suffice
-            }
-            
-            public static bool equal(void *_a, void *_b) {
-                KeyDef a = (KeyDef) _a;
-                KeyDef b = (KeyDef) _b;
-                return a.keycode == b.keycode && a.modMask == b.modMask;
+            public uint hash() {
+                var uintHashFunc = Gee.Functions.get_hash_func_for(Type.from_name("uint"));
+                return uintHashFunc(this.keycode | this.modMask); // | is probable the best combinator, but for this small application it should suffice
+            }
+
+            public bool equal_to(KeyDef other) {
+                return this.keycode == other.keycode && this.modMask == other.modMask;
             }
         }
-        protected HashMap<KeyDef, KeyAction> keyBindings;
-        protected HashMap<KeyDef, KeyAction> mouseBindings; // We abuse the KeyDef structure
+        protected Gee.HashMap<KeyDef, Action> keyBindings = new Gee.HashMap<KeyDef, Action>();
+        // We abuse the KeyDef structure
+        protected Gee.HashMap<KeyDef, Action> mouseBindings = new Gee.HashMap<KeyDef, Action>();
+
+        /*
+         * "Main" view of current slide
+         */
+        public View.Pdf main_view = null;
 
         /**
          * Instantiate a new controller
          */
-        public PresentationController( Metadata.Pdf metadata, bool allow_black_on_end ) {
+        public PresentationController(Metadata.Pdf metadata, bool allow_black_on_end) {
             this.metadata = metadata;
+            this.metadata.controller = this;
             this.black_on_end = allow_black_on_end;
 
             this.controllables = new GLib.List<Controllable>();
@@ -156,71 +154,78 @@ namespace pdfpc {
             // Calculate the countdown to display until the presentation has to
             // start
             time_t start_time = 0;
-            if ( Options.start_time != null ) 
-            {
-                start_time = this.parseTime( 
-                    Options.start_time 
-                );
+            if (Options.start_time != null) {
+                start_time = this.parseTime(Options.start_time);
             }
             // The same again for end_time
             time_t end_time = 0;
-            if ( Options.end_time != null ) 
-            {
-                end_time = this.parseTime( 
-                    Options.end_time 
-                );
+            if (Options.end_time != null) {
+                end_time = this.parseTime(Options.end_time);
                 Options.duration = 0;
                 this.metadata.set_duration(0);
             }
-            this.timer = getTimerLabel( (int)this.metadata.get_duration() * 60,
-                                        end_time, Options.last_minutes, start_time );
+            this.timer = getTimerLabel((int) this.metadata.get_duration() * 60,
+                end_time, Options.last_minutes, start_time);
             this.timer.reset();
 
-            this.n_slides = (int)metadata.get_slide_count();
-            
+            this.n_slides = (int) this.metadata.get_slide_count();
+
             this.current_slide_number = 0;
             this.current_user_slide_number = 0;
-            
-            // The standard hash function for classes is to use the pointer, so we have to provide our own
-            this.keyBindings = new HashMap<KeyDef, KeyAction>(KeyDef.hash, KeyDef.equal);
-            this.mouseBindings = new HashMap<KeyDef, KeyAction>(KeyDef.hash, KeyDef.equal);
-            this.fillActionNames();
+
+            this.add_actions();
+        }
+
+        /*
+         * Inform metadata of quit, and then quit.
+         */
+        public void quit() {
+            this.metadata.quit();
+            Gtk.main_quit();
         }
 
         public void set_overview(Window.Overview o) {
             this.overview = o;
         }
 
-        protected void fillActionNames() {
-            this.actionNames = new HashMap<string, KeyAction>();
-            this.actionNames.set("next", new KeyAction(this.next_page));
-            this.actionNames.set("next10", new KeyAction(this.jump10));
-            this.actionNames.set("nextOverlay", new KeyAction(this.next_user_page));
-            this.actionNames.set("prev", new KeyAction(this.previous_page));
-            this.actionNames.set("prev10", new KeyAction(this.back10));
-            this.actionNames.set("prevOverlay", new KeyAction(this.previous_user_page));
-
-            this.actionNames.set("goto", new KeyAction(this.controllables_ask_goto_page));
-            this.actionNames.set("gotoFirst", new KeyAction(this.goto_first));
-            this.actionNames.set("gotoLast", new KeyAction(this.goto_last));
-            this.actionNames.set("overview", new KeyAction(this.toggle_overview));
-            this.actionNames.set("histBack", new KeyAction(this.history_back));
-
-            this.actionNames.set("start", new KeyAction(this.start));
-            this.actionNames.set("pause", new KeyAction(this.toggle_pause));
-            this.actionNames.set("resetTimer", new KeyAction(this.reset_timer));
-            this.actionNames.set("reset", new KeyAction(this.controllables_reset));
-
-            this.actionNames.set("blank", new KeyAction(this.fade_to_black));
-            this.actionNames.set("freeze", new KeyAction(this.toggle_freeze));
-            this.actionNames.set("freezeOn", new KeyAction(() => {if (!this.frozen) this.toggle_freeze();}));
-
-            this.actionNames.set("overlay", new KeyAction(this.toggle_skip));
-            this.actionNames.set("note", new KeyAction(this.controllables_edit_note));
-            this.actionNames.set("endSlide", new KeyAction(this.set_end_user_slide));
+        protected void add_actions() {
+            add_action("next", this.next_page);
+            add_action("next10", this.jump10);
+            add_action("nextOverlay", this.next_user_page);
+            add_action("prev", this.previous_page);
+            add_action("prev10", this.back10);
+            add_action("prevOverlay", this.previous_user_page);
+
+            add_action("goto", this.controllables_ask_goto_page);
+            add_action("gotoFirst", this.goto_first);
+            add_action("gotoLast", this.goto_last);
+            add_action("overview", this.toggle_overview);
+            add_action("histBack", this.history_back);
+
+            add_action("start", this.start);
+            add_action("pause", this.toggle_pause);
+            add_action("resetTimer", this.reset_timer);
+            add_action("reset", this.controllables_reset);
+
+            add_action("blank", this.fade_to_black);
+            add_action("freeze", this.toggle_freeze);
+            add_action("freezeOn", () => {
+                if (!this.frozen)
+                    this.toggle_freeze();
+                });
 
-            this.actionNames.set("exitState", new KeyAction(this.exit_state));
-            this.actionNames.set("quit", new KeyAction(this.quit));
+            add_action("overlay", this.toggle_skip);
+            add_action("note", this.controllables_edit_note);
+            add_action("endSlide", this.set_end_user_slide);
+
+            add_action("exitState", this.exit_state);
+            add_action("quit", this.quit);
+        }
+
+        protected void add_action(string name, callback func) {
+            SimpleAction action = new SimpleAction(name, null);
+            action.activate.connect(() => func());  // Trying to connect func directly causes error.
+            this.action_group.add_action(action);
         }
 
         /**
@@ -231,39 +236,40 @@ namespace pdfpc {
          */
         public static string[] getActionDescriptions() {
             return {"next", "Go to next slide",
-					"next10", "Jump 10 slides forward",
-					"nextOverlay", "Jump forward outside of current overlay",
-					"prev", "Go to previous slide",
-					"prev10", "Jump 10 slides back",
-					"prevOverlay", "Jump back outside of current overlay",
-					"goto", "Ask for a page to jump to",
-					"gotoFirst", "Jump to first slide",
-					"gotoLast", "Jump to last slide",
-					"overview", "Show the overview mode",
-					"histBack", "Go back in history",
-					"start", "Start the timer",
-					"pause", "Pause the timer",
-					"resetTimer", "Reset the timer",
-					"reset", "Reset the presentation",
-					"blank", "Blank presentation screen",
-					"freeze", "Toggle freeze presentation screen",
-					"freezeOn", "Freeze presentation screen if unfrozen",
-					"overlay", "Mark current slide as overlay slide",
-					"note", "Edit note for current slide",
-					"endSlide", "Set current slide as end slide",
-					"exitState", "Exit \"special\" state (pause, freeze, blank)",
-					"quit", "Exit pdfpc"
-			};
+                "next10", "Jump 10 slides forward",
+                "nextOverlay", "Jump forward outside of current overlay",
+                "prev", "Go to previous slide",
+                "prev10", "Jump 10 slides back",
+                "prevOverlay", "Jump back outside of current overlay",
+                "goto", "Ask for a page to jump to",
+                "gotoFirst", "Jump to first slide",
+                "gotoLast", "Jump to last slide",
+                "overview", "Show the overview mode",
+                "histBack", "Go back in history",
+                "start", "Start the timer",
+                "pause", "Pause the timer",
+                "resetTimer", "Reset the timer",
+                "reset", "Reset the presentation",
+                "blank", "Blank presentation screen",
+                "freeze", "Toggle freeze presentation screen",
+                "freezeOn", "Freeze presentation screen if unfrozen",
+                "overlay", "Mark current slide as overlay slide",
+                "note", "Edit note for current slide",
+                "endSlide", "Set current slide as end slide",
+                "exitState", "Exit \"special\" state (pause, freeze, blank)",
+                "quit", "Exit pdfpc"
+            };
         }
 
         /**
          * Bind the (user-defined) keys
          */
-        public void bind(uint keycode, uint modMask, string function) {
-            if (this.actionNames.contains(function)) {
-                this.keyBindings.set(new KeyDef(keycode, modMask), this.actionNames[function]);
-            } else
-                stderr.printf("Warning: Unknown function %s\n", function);
+        public void bind(uint keycode, uint modMask, string action_name) {
+            Action? action = this.action_group.lookup_action(action_name);
+            if (action != null)
+                this.keyBindings.set(new KeyDef(keycode, modMask), action);
+            else
+                warning("Unknown action %s", action_name);
         }
 
         /**
@@ -283,11 +289,12 @@ namespace pdfpc {
         /**
          * Bind the (user-defined) keys
          */
-        public void bindMouse(uint button, uint modMask, string function) {
-            if (this.actionNames.contains(function)) {
-                this.mouseBindings.set(new KeyDef(button, modMask), this.actionNames[function]);
-            } else
-                stderr.printf("Warning: Unknown function %s\n", function);
+        public void bindMouse(uint button, uint modMask, string action_name) {
+            Action? action = this.action_group.lookup_action(action_name);
+            if (action != null)
+                this.mouseBindings.set(new KeyDef(button, modMask), action);
+            else
+                warning("Unknown action %s", action_name);
         }
 
         /**
@@ -311,12 +318,17 @@ namespace pdfpc {
          * window have implications on the behaviour of both of them. Therefore
          * this controller is needed to take care of the needed actions.
          */
-        public bool key_press( Gdk.EventKey key ) {
+        public bool key_press(Gdk.EventKey key) {
             if (key.time != last_key_event && !ignore_keyboard_events ) {
                 last_key_event = key.time;
-                var action = this.keyBindings.get(new KeyDef(key.keyval,key.state & this.accepted_key_mods));
+                if (this.overview_shown && this.overview.key_press_event(key))
+                    return true;
+
+                var action = this.keyBindings.get(new KeyDef(key.keyval,
+                    key.state & this.accepted_key_mods));
+
                 if (action != null)
-                    action.d();
+                    action.activate(null);
                 return true;
             } else {
                 return false;
@@ -326,14 +338,14 @@ namespace pdfpc {
         /**
          * Handle mouse clicks to each of the controllables
          */
-        public bool button_press( Gdk.EventButton button ) {
-            if ( !ignore_mouse_events && button.type ==
-                    Gdk.EventType.BUTTON_PRESS ) {
+        public bool button_press(Gdk.EventButton button) {
+            if (!ignore_mouse_events && button.type == Gdk.EventType.BUTTON_PRESS ) {
                 // Prevent double or triple clicks from triggering additional
                 // click events
-                var action = this.mouseBindings.get(new KeyDef(button.button,button.state & this.accepted_key_mods));
+                var action = this.mouseBindings.get(new KeyDef(button.button,
+                    button.state & this.accepted_key_mods));
                 if (action != null)
-                    action.d();
+                    action.activate(null);
                 return true;
             } else {
                 return false;
@@ -343,46 +355,43 @@ namespace pdfpc {
         /**
          * Notify each of the controllables of mouse scrolling
          */
-        public void scroll( Gdk.EventScroll scroll ) {
-            if ( !this.ignore_mouse_events ) {
-                switch( scroll.direction ) {
-                    case Gdk.ScrollDirection.UP: /* Scroll up */
-                    case Gdk.ScrollDirection.LEFT: /* Scroll left */ 
-                        if ( (scroll.state & Gdk.ModifierType.SHIFT_MASK) != 0 )
+        public bool scroll(Gdk.EventScroll scroll) {
+            if (!this.ignore_mouse_events) {
+                switch (scroll.direction) {
+                    case Gdk.ScrollDirection.UP:
+                    case Gdk.ScrollDirection.LEFT:
+                        if ((scroll.state & Gdk.ModifierType.SHIFT_MASK) != 0)
                             this.back10();
                         else
                             this.previous_page();
                     break;
-                    case Gdk.ScrollDirection.DOWN: /* Scroll down */
-                    case Gdk.ScrollDirection.RIGHT: /* Scroll right */ 
-                        if ( (scroll.state & Gdk.ModifierType.SHIFT_MASK) != 0 )
+
+                    case Gdk.ScrollDirection.DOWN:
+                    case Gdk.ScrollDirection.RIGHT:
+                        if ((scroll.state & Gdk.ModifierType.SHIFT_MASK) != 0)
                             this.jump10();
                         else
                             this.next_page();
                     break;
                 }
+                return true;
             }
-        }
-        
-        /**
-         * Get the current (real) slide number
-         */
-        public int get_current_slide_number() {
-            return current_slide_number;
+            return false;
         }
 
         /**
-         * Get the current (user) slide number
+         * Get the PDF URL
          */
-        public int get_current_user_slide_number() {
-            return current_user_slide_number;
+        public string? get_pdf_url() {
+            return this.metadata.pdf_url;
         }
-    
+
         /**
          * Was the previous slide a skip one?
          */
         public bool skip_previous() {
-            return this.current_slide_number > this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+            return this.current_slide_number > this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number);
         }
 
         /**
@@ -390,24 +399,9 @@ namespace pdfpc {
          */
         public bool skip_next() {
             return (this.current_user_slide_number >= this.metadata.get_user_slide_count() - 1
-                    &&
-                    this.current_slide_number < this.n_slides)
-                   ||
-                   (this.current_slide_number+1 < this.metadata.user_slide_to_real_slide(this.current_user_slide_number+1));
-        }
-
-        /**
-         * Get the real total number of slides
-         */
-        public int get_n_slide() {
-            return this.n_slides;
-        }
-
-        /**
-         * Get the user total number of slides
-         */
-        public int get_user_n_slides() {
-            return this.metadata.get_user_slide_count();;
+                && this.current_slide_number < this.n_slides)
+                || (this.current_slide_number + 1 < this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number + 1));
         }
 
         /**
@@ -416,7 +410,7 @@ namespace pdfpc {
         public int get_end_user_slide() {
             return this.metadata.get_end_user_slide();
         }
-    
+
         /**
          * Set the last slide as defined by the user
          */
@@ -436,18 +430,19 @@ namespace pdfpc {
         /**
          * Register the current slide in the history
          */
-        void slide2history() {
+        void push_history() {
             this.history += this.current_slide_number;
         }
 
         /**
          * A request to change the page has been issued
          */
-        public void page_change_request( int page_number ) {
+        public void page_change_request(int page_number) {
             if (page_number != this.current_slide_number)
-                this.slide2history();
+                this.push_history();
             this.current_slide_number = page_number;
-            this.current_user_slide_number = this.metadata.real_slide_to_user_slide(this.current_slide_number);
+            this.current_user_slide_number = this.metadata.real_slide_to_user_slide(
+                this.current_slide_number);
             this.timer.start();
             this.controllables_update();
         }
@@ -455,15 +450,11 @@ namespace pdfpc {
         /**
          * Set the state of ignote_input_events
          */
-        public void set_ignore_input_events( bool v ) {
+        public void set_ignore_input_events(bool v) {
             this.ignore_keyboard_events = v;
             this.ignore_mouse_events = v;
         }
 
-        public void set_ignore_mouse_events( bool v ) {
-            this.ignore_mouse_events = v;
-        }
-
         /**
          * Get the timer
          */
@@ -472,20 +463,22 @@ namespace pdfpc {
         }
 
         /**
-         * Register a new Controllable instance on this controller. 
+         * Register a new Controllable instance on this controller.
          *
          * On success true is returned, in case the controllable has already been
          * registered false is returned.
          */
-        public bool register_controllable( Controllable controllable ) {
-            if ( this.controllables.find( controllable ) != null ) {
+        public bool register_controllable(Controllable controllable) {
+            if (this.controllables.find(controllable) != null) {
                 // The controllable has already been added.
                 return false;
             }
 
             //controllable.set_controller( this );
-            this.controllables.append( controllable );
-            
+            this.controllables.append(controllable);
+            if (this.main_view == null)
+                this.main_view = controllable.main_view;
+
             return true;
         }
 
@@ -495,15 +488,17 @@ namespace pdfpc {
         public void next_page() {
             if (overview_shown)
                 return;
+
             this.timer.start();
-            if ( this.current_slide_number < this.n_slides - 1 ) {
+            if (this.current_slide_number < this.n_slides - 1) {
                 ++this.current_slide_number;
-                if (this.current_slide_number == this.metadata.user_slide_to_real_slide(this.current_user_slide_number + 1))
+                if (this.current_slide_number == this.metadata.user_slide_to_real_slide(
+                    this.current_user_slide_number + 1))
                     ++this.current_user_slide_number;
                 if (!this.frozen)
                     this.faded_to_black = false;
                 this.controllables_update();
-            } else if (this.black_on_end && !this.is_faded_to_black()) {
+            } else if (this.black_on_end && !this.faded_to_black) {
                 this.fade_to_black();
             }
         }
@@ -514,14 +509,15 @@ namespace pdfpc {
         public void next_user_page() {
             this.timer.start();
             bool needs_update; // Did we change anything?
-            if ( this.current_user_slide_number < this.metadata.get_user_slide_count()-1 ) {
+            if (this.current_user_slide_number < this.metadata.get_user_slide_count()-1) {
                 ++this.current_user_slide_number;
-                this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+                this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                    this.current_user_slide_number);
                 needs_update = true;
             } else {
-                if ( this.current_slide_number == this.n_slides - 1) {
+                if (this.current_slide_number == this.n_slides - 1) {
                     needs_update = false;
-                    if (this.black_on_end && !this.is_faded_to_black())
+                    if (this.black_on_end && !this.faded_to_black)
                         this.fade_to_black();
                 } else {
                     this.current_user_slide_number = this.metadata.get_user_slide_count() - 1;
@@ -541,12 +537,14 @@ namespace pdfpc {
          */
         public void previous_page() {
             this.timer.start();
-            if ( this.current_slide_number > 0) {
-                if (this.current_slide_number != this.metadata.user_slide_to_real_slide(this.current_user_slide_number)) {
+            if (this.current_slide_number > 0) {
+                if (this.current_slide_number != this.metadata.user_slide_to_real_slide(
+                    this.current_user_slide_number)) {
                     --this.current_slide_number;
                 } else {
                     --this.current_user_slide_number;
-                    this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+                    this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                        this.current_user_slide_number);
                 }
                 if (!this.frozen)
                     this.faded_to_black = false;
@@ -559,9 +557,10 @@ namespace pdfpc {
          */
         public void previous_user_page() {
             this.timer.start();
-            if ( this.current_user_slide_number > 0 ) {
+            if (this.current_user_slide_number > 0) {
                 --this.current_user_slide_number;
-                this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+                this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                    this.current_user_slide_number);
             } else {
                 this.current_user_slide_number = 0;
                 this.current_slide_number = 0;
@@ -577,7 +576,7 @@ namespace pdfpc {
         public void goto_first() {
             this.timer.start();
             if (this.current_slide_number != 0)
-                this.slide2history();
+                this.push_history();
             this.current_slide_number = 0;
             this.current_user_slide_number = 0;
             if (!this.frozen)
@@ -591,9 +590,10 @@ namespace pdfpc {
         public void goto_last() {
             this.timer.start();
             if (this.current_user_slide_number != this.metadata.get_end_user_slide() - 1)
-                this.slide2history();
+                this.push_history();
             this.current_user_slide_number = this.metadata.get_end_user_slide() - 1;
-            this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+            this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number);
             if (!this.frozen)
                 this.faded_to_black = false;
             this.controllables_update();
@@ -605,12 +605,14 @@ namespace pdfpc {
         public void jump10() {
             if (this.overview_shown)
                 return;
+
             this.timer.start();
             this.current_user_slide_number += 10;
             int max_user_slide = this.metadata.get_user_slide_count();
-            if ( this.current_user_slide_number >= max_user_slide )
+            if (this.current_user_slide_number >= max_user_slide)
                 this.current_user_slide_number = max_user_slide - 1;
-            this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+            this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number);
             if (!this.frozen)
                 this.faded_to_black = false;
             this.controllables_update();
@@ -622,11 +624,13 @@ namespace pdfpc {
         public void back10() {
             if (this.overview_shown)
                 return;
+
             this.timer.start();
             this.current_user_slide_number -= 10;
-            if ( this.current_user_slide_number < 0 )
+            if (this.current_user_slide_number < 0)
                 this.current_user_slide_number = 0;
-            this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+            this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number);
             if (!this.frozen)
                 this.faded_to_black = false;
             this.controllables_update();
@@ -638,20 +642,21 @@ namespace pdfpc {
         public void goto_user_page(int page_number) {
             this.timer.start();
             if (this.current_user_slide_number != page_number - 1)
-                this.slide2history();
-            
+                this.push_history();
+
             this.controllables_hide_overview();
-            int destination = page_number-1;
+            int destination = page_number - 1;
             int n_user_slides = this.metadata.get_user_slide_count();
             if (page_number < 1)
                 destination = 0;
             else if (page_number >= n_user_slides)
                 destination = n_user_slides - 1;
             this.current_user_slide_number = destination;
-            this.current_slide_number = this.metadata.user_slide_to_real_slide(this.current_user_slide_number);
+            this.current_slide_number = this.metadata.user_slide_to_real_slide(
+                this.current_user_slide_number);
             if (!this.frozen)
                 this.faded_to_black = false;
-            this.set_ignore_input_events( false );
+            this.set_ignore_input_events(false);
             this.controllables_update();
         }
 
@@ -661,12 +666,14 @@ namespace pdfpc {
         public void history_back() {
             if (this.overview_shown)
                 return;
+
             int history_length = this.history.length;
             if (history_length == 0) {
                 this.goto_first();
             } else {
                 this.current_slide_number = this.history[history_length - 1];
-                this.current_user_slide_number = this.metadata.real_slide_to_user_slide(this.current_slide_number);
+                this.current_user_slide_number = this.metadata.real_slide_to_user_slide(
+                    this.current_slide_number);
                 this.history.resize(history_length - 1);
                 if (!this.frozen)
                     this.faded_to_black = false;
@@ -678,7 +685,7 @@ namespace pdfpc {
          * Notify the controllables that they have to update the view
          */
         protected void controllables_update() {
-            foreach( Controllable c in this.controllables )
+            foreach (Controllable c in this.controllables)
                 c.update();
         }
 
@@ -701,7 +708,7 @@ namespace pdfpc {
 
         protected void controllables_show_overview() {
             if (this.overview != null) {
-                this.set_ignore_mouse_events(true);
+                this.ignore_mouse_events = true;
                 foreach( Controllable c in this.controllables )
                     c.show_overview();
                 this.overview_shown = true;
@@ -709,14 +716,14 @@ namespace pdfpc {
         }
 
         protected void controllables_hide_overview() {
-            this.set_ignore_mouse_events(false);
+            this.ignore_mouse_events = false;
             // It may happen that in overview mode, the number of (user) slides
             // has changed due to overlay changes. We may need to correct our
             // position
-            if (this.current_user_slide_number >= this.get_user_n_slides())
+            if (this.current_user_slide_number >= this.user_n_slides)
                 this.goto_last();
             this.overview_shown = false;
-            foreach( Controllable c in this.controllables )
+            foreach (Controllable c in this.controllables)
                 c.hide_overview();
             this.controllables_update();
         }
@@ -730,19 +737,12 @@ namespace pdfpc {
         }
 
         /**
-         * Is the presentation blanked?
-         */
-        public bool is_faded_to_black() {
-            return this.faded_to_black;
-        }
-
-        /**
          * Edit note for current slide.
          */
         protected void controllables_edit_note() {
             if (this.overview_shown)
                 return;
-            foreach( Controllable c in this.controllables ) {
+            foreach (Controllable c in this.controllables) {
                 c.edit_note();
             }
         }
@@ -753,7 +753,7 @@ namespace pdfpc {
         protected void controllables_ask_goto_page() {
             if (this.overview_shown)
                 return;
-            foreach( Controllable c in this.controllables ) {
+            foreach (Controllable c in this.controllables) {
                 c.ask_goto_page();
             }
         }
@@ -769,24 +769,18 @@ namespace pdfpc {
         }
 
         /**
-         * Is the presentation frozen?
-         */
-        public bool is_frozen() {
-            return this.frozen;
-        }
-        
-        /**
          * Toggle skip for current slide
          */
         protected void toggle_skip() {
             if (overview_shown) {
                 int user_selected = this.overview.current_slide;
                 int slide_number = this.metadata.user_slide_to_real_slide(user_selected);
-                if (this.metadata.toggle_skip( slide_number, user_selected ) != 0)
-                    this.overview.remove_current( this.get_user_n_slides() );
+                if (this.metadata.toggle_skip(slide_number, user_selected) != 0)
+                    this.overview.remove_current(this.user_n_slides);
             } else {
-                this.current_user_slide_number += this.metadata.toggle_skip( this.current_slide_number, this.current_user_slide_number);
-                this.overview.set_n_slides(this.get_user_n_slides());
+                this.current_user_slide_number += this.metadata.toggle_skip(
+                    this.current_slide_number, this.current_user_slide_number);
+                this.overview.set_n_slides(this.user_n_slides);
                 this.controllables_update();
             }
         }
@@ -798,7 +792,7 @@ namespace pdfpc {
             this.timer.start();
             this.controllables_update();
         }
-        
+
         /**
          * Pause the timer
          */
@@ -826,19 +820,33 @@ namespace pdfpc {
             }
         }
 
-        protected void quit() {
-            this.metadata.save_to_disk();
-            Gtk.main_quit();              
-        }
-
         /**
          * Parse the given time string to a Time object
          */
-        private time_t parseTime( string t ) 
-        {
-            var tm = Time.local( time_t() );
-            tm.strptime( t + ":00", "%H:%M:%S" );
+        private time_t parseTime(string t) {
+            var tm = Time.local(time_t());
+            tm.strptime(t + ":00", "%H:%M:%S");
             return tm.mktime();
         }
+
+        /**
+         * Give the Gdk.Rectangle corresponding to the Poppler.Rectangle for the nth
+         * controllable's main view.  Also, return the XID for the view's window,
+         * useful for overlays.
+         */
+        public uint overlay_pos(int n, Poppler.Rectangle area, out Gdk.Rectangle rect) {
+            Controllable c = this.controllables.nth_data(n);
+            if (c == null) {
+                rect = Gdk.Rectangle();
+                return 0;
+            }
+            View.Pdf view = c.main_view;
+            if (view == null) {
+                rect = Gdk.Rectangle();
+                return 0;
+            }
+            rect = view.convert_poppler_rectangle_to_gdk_rectangle(area);
+            return (uint) ((Gdk.X11.Window) view.get_window()).get_xid ();
+        }
     }
 }
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/base.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/base.vala
@@ -4,90 +4,66 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
 namespace pdfpc {
     /**
      * Renderer base class needed to be extended by every slide renderer.
      */
-    public abstract class Renderer.Base: Object
-    {
+    public abstract class Renderer.Base : Object {
         /**
          * Metadata object to render slides for
          */
-        protected Metadata.Base metadata;
+        public Metadata.Base metadata { get; protected set; }
 
         /**
          * Width to render to
          */
-        protected int width;
+        public int width { get; protected set; }
 
         /**
          * Height to render to
          */
-        protected int height;
+        public int height { get; protected set; }
 
         /**
          * Base constructor taking a metadata object as well as the desired
          * render width and height as parameters.
          */
-        public Base( Metadata.Base metadata, int width, int height ) {
+        public Base(Metadata.Base metadata, int width, int height) {
             this.metadata = metadata;
             this.width = width;
             this.height = height;
         }
 
         /**
-         * Return the registered metadata object
-         */
-        public Metadata.Base get_metadata() {
-            return this.metadata;
-        }
-
-        /**
-         * Return the desired render width
-         */
-        public int get_width() {
-            return this.width;
-        }
-
-        /**
-         * Return the desired render height
-         */
-        public int get_height() {
-            return this.height;
-        }
-
-        /**
-         * Render the given slide_number to a Gdk.Pixmap and return it.
+         * Render the given slide_number to a Cairo.ImageSurface and return it.
          *
          * If the requested slide is not available an
          * RenderError.SLIDE_DOES_NOT_EXIST error should be thrown.
          */
-        public abstract Gdk.Pixmap render_to_pixmap( int slide_number ) 
+        public abstract Cairo.ImageSurface render_to_surface(int slide_number)
             throws RenderError;
 
         /**
          * Fill the display with black. Useful for last "slide" or for fading
          * to black at certain points in the presentation.
          */
-        public abstract Gdk.Pixmap fade_to_black();
+        public abstract Cairo.ImageSurface fade_to_black();
     }
 
     /**
@@ -97,3 +73,4 @@ namespace pdfpc {
         SLIDE_DOES_NOT_EXIST;
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/cache/base.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/cache/base.vala
@@ -4,31 +4,28 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gdk;
-
-namespace pdfpc {
+namespace pdfpc.Renderer.Cache {
     /**
      * Base Cache store interface which needs to be implemented by every
      * working cache.
      */
-    public abstract class Renderer.Cache.Base: Object {
+    public abstract class Base : Object {
         /**
          * Metadata object to provide caching for
          */
@@ -37,7 +34,7 @@ namespace pdfpc {
         /**
          * Initialize the cache store
          */
-        public Base( Metadata.Base metadata ) {
+        public Base(Metadata.Base metadata) {
             this.metadata = metadata;
         }
 
@@ -54,15 +51,25 @@ namespace pdfpc {
         }
 
         /**
-         * Store a pixmap in the cache using the given index as identifier
+         * Store a surface in the cache using the given index as identifier
          */
-        public abstract void store( uint index, Pixmap pixmap );
+        public abstract void store(uint index, Cairo.ImageSurface surface);
 
         /**
-         * Retrieve a stored pixmap from the cache.
+         * Retrieve a stored surface from the cache.
          *
          * If no item with the given index is available null is returned
          */
-        public abstract Pixmap? retrieve( uint index );
+        public abstract Cairo.ImageSurface? retrieve(uint index);
+    }
+
+    /**
+     * Creates cache engines based on the global commandline options
+     */
+    public Base create(Metadata.Base metadata) {
+        if (!Options.disable_cache_compression)
+            return new PNG.Engine(metadata);
+
+        return new Simple.Engine(metadata);
     }
 }
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/cache/png/engine.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/cache/png/engine.vala
@@ -4,28 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gdk;
-using Cairo;
-
-using pdfpc;
-
 namespace pdfpc.Renderer.Cache {
     /**
      * Cache store which holds all given items in memory as compressed png
@@ -38,79 +32,44 @@ namespace pdfpc.Renderer.Cache {
         protected PNG.Item[] storage = null;
 
         /**
-         * Mutex used to limit access to storage array to one thread at a time.
-         *
-         * Unfortunately the vala lock statement does not work here.
-         */
-        protected Mutex mutex = new Mutex();
-
-        /**
          * Initialize the cache store
          */
         public Engine( Metadata.Base metadata ) {
             base( metadata );
-
-            this.mutex.lock();
             this.storage = new PNG.Item[this.metadata.get_slide_count()];
-            this.mutex.unlock();
         }
 
         /**
-         * Store a pixmap in the cache using the given index as identifier
+         * Store a surface in the cache using the given index as identifier
          */
-        public override void store( uint index, Pixmap pixmap ) {
-            int pixmap_width, pixmap_height;
-            pixmap.get_size( out pixmap_width, out pixmap_height );
-
-            // The pixbuf has to be created before being handed over to the
-            // pixbuf_get_from_drawable, because Vala hightens the refcount of
-            // the return value of this function. If a new pixbuf is created by
-            // the function directly it will have a refcount of 2 afterwards
-            // and thereby will not be freed.
-            var pixbuf = new Pixbuf( 
-                Colorspace.RGB,
-                false,
-                8,
-                pixmap_width,
-                pixmap_height
-            );
-            pixbuf_get_from_drawable( 
-                pixbuf,
-                pixmap,
-                null,
-                0, 0,
-                0, 0,
-                pixmap_width, pixmap_height
-            );
-
+        public override void store( uint index, Cairo.ImageSurface surface ) {
+            Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(),
+                surface.get_height());
             uint8[] buffer;
 
             try {
-                pixbuf.save_to_buffer( out buffer, "png", "compression", "1", null );           
+                pixbuf.save_to_buffer( out buffer, "png", "compression", "1", null );
             }
             catch( Error e ) {
                 error( "Could not generate PNG cache image for slide %u: %s", index, e.message );
             }
 
             var item = new PNG.Item( buffer );
-            
-            this.mutex.lock();
             this.storage[index] = item;
-            this.mutex.unlock();
         }
 
         /**
-         * Retrieve a stored pixmap from the cache.
+         * Retrieve a stored surface from the cache.
          *
          * If no item with the given index is available null is returned
          */
-        public override Pixmap? retrieve( uint index ) {
+        public override Cairo.ImageSurface? retrieve( uint index ) {
             var item = this.storage[index];
             if ( item == null ) {
                 return null;
             }
 
-            var loader = new PixbufLoader();
+            var loader = new Gdk.PixbufLoader();
             try {
                 loader.write( item.get_png_data() );
                 loader.close();
@@ -120,25 +79,15 @@ namespace pdfpc.Renderer.Cache {
             }
 
             var pixbuf = loader.get_pixbuf();
-
-            var pixmap = new Gdk.Pixmap( 
-                null, 
-                pixbuf.get_width(),
-                pixbuf.get_height(),
-                24
-            );
-
-            Context cr = Gdk.cairo_create( pixmap );
-            Gdk.cairo_set_source_pixbuf( cr, pixbuf, 0, 0 );
-            cr.rectangle(
-                0,
-                0,
-                pixbuf.get_width(),
-                pixbuf.get_height()
-            );
+            Cairo.ImageSurface surface = new Cairo.ImageSurface(Cairo.Format.ARGB32,
+                pixbuf.get_width(), pixbuf.get_height());
+            Cairo.Context cr = new Cairo.Context(surface);
+            Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
+            cr.rectangle(0, 0, pixbuf.get_width(), pixbuf.get_height());
             cr.fill();
 
-            return pixmap;
+            return surface;
         }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/cache/png/item.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/cache/png/item.vala
@@ -4,24 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
 namespace pdfpc.Renderer.Cache {
     /**
      * PNG picture data stored by the PNG cache engine.
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/cache/simple/engine.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/cache/simple/engine.vala
@@ -4,62 +4,45 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gdk;
-
-using pdfpc;
-
 namespace pdfpc.Renderer.Cache {
     /**
      * Cache store which simply holds all given items in memory.
      */
     public class Simple.Engine: Renderer.Cache.Base {
         /**
-         * In memory storage for all the given pixmaps
+         * In memory storage for all the given surfaces
          */
-        protected Pixmap[] storage = null;
-
-        /**
-         * Mutex used to limit access to storage array to one thread at a time.
-         *
-         * Unfortunately the vala lock statement does not work here.
-         */
-        protected Mutex mutex = new Mutex();
+        protected Cairo.ImageSurface[] storage = null;
 
         /**
          * Initialize the cache store
          */
         public Engine( Metadata.Base metadata ) {
             base( metadata );
-
-            this.mutex.lock();
-            this.storage = new Pixmap[this.metadata.get_slide_count()];
-            this.mutex.unlock();
+            this.storage = new Cairo.ImageSurface[this.metadata.get_slide_count()];
         }
 
         /**
-         * Store a pixmap in the cache using the given index as identifier
+         * Store a surface in the cache using the given index as identifier
          */
-        public override void store( uint index, Pixmap pixmap ) {
-            this.mutex.lock();
-            this.storage[index] = pixmap;
-            this.mutex.unlock();
+        public override void store( uint index, Cairo.ImageSurface surface ) {
+            this.storage[index] = surface;
         }
 
         /**
@@ -67,7 +50,7 @@ namespace pdfpc.Renderer.Cache {
          *
          * If no item with the given index is available null is returned
          */
-        public override Pixmap? retrieve( uint index ) {
+        public override Cairo.ImageSurface? retrieve( uint index ) {
             return this.storage[index];
         }
     }
--- pdf-presenter-console-3.1.1.orig/src/classes/renderer/pdf.vala
+++ pdf-presenter-console-3.1.1/src/classes/renderer/pdf.vala
@@ -4,41 +4,41 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gdk;
-using Cairo;
-
 namespace pdfpc {
     /**
      * Pdf slide renderer
      */
-    public class Renderer.Pdf: Renderer.Base, Renderer.Caching
-    {
+    public class Renderer.Pdf : Renderer.Base, Renderer.Caching {
         /**
          * The scaling factor needed to render the pdf page to the desired size.
          */
         protected double scaling_factor;
 
         /**
+         * The area of the pdf which shall be displayed
+         */
+        protected Metadata.Area area;
+
+        /**
          * Cache store to be used
          */
-        protected Renderer.Cache.Base cache = null;
+        public Renderer.Cache.Base? cache { get; set; default = null; }
 
         /**
          * Base constructor taking a pdf metadata object as well as the desired
@@ -49,92 +49,80 @@ namespace pdfpc {
          * pdf document the renderspace is filled up completely cutting of a
          * part of the pdf document.
          */
-        public Pdf( Metadata.Pdf metadata, int width, int height ) {
-            base( metadata, width, height );
+        public Pdf(Metadata.Pdf metadata, int width, int height, Metadata.Area area) {
+            base(metadata, width, height);
 
-            // Calculate the scaling factor needed.
-            this.scaling_factor = Math.fmax( 
-                width / metadata.get_page_width(),
-                height / metadata.get_page_height()
-            );
-        }
+            this.area = area;
 
-        /**
-         * Set cache store to use
-         */
-        public void set_cache( Renderer.Cache.Base cache ) {
-            this.cache = cache;
-        }
-
-        /**
-         * Retrieve the currently used cache engine
-         */
-        public Renderer.Cache.Base get_cache() {
-            return this.cache;
+            // Calculate the scaling factor needed.
+            this.scaling_factor = Math.fmax(width / metadata.get_page_width(),
+                height / metadata.get_page_height());
         }
 
         /**
-         * Render the given slide_number to a Gdk.Pixmap and return it.
+         * Render the given slide_number to a Cairo.ImageSurface and return it.
          *
          * If the requested slide is not available an
          * RenderError.SLIDE_DOES_NOT_EXIST error is thrown.
          */
-        public override Gdk.Pixmap render_to_pixmap( int slide_number ) 
+        public override Cairo.ImageSurface render_to_surface(int slide_number)
             throws Renderer.RenderError {
-            
+
             var metadata = this.metadata as Metadata.Pdf;
 
             // Check if a valid page is requested, before locking anything.
-            if ( slide_number < 0 || slide_number >= metadata.get_slide_count() ) {
-                throw new Renderer.RenderError.SLIDE_DOES_NOT_EXIST( "The requested slide '%i' does not exist.", slide_number );
+            if (slide_number < 0 || slide_number >= metadata.get_slide_count()) {
+                throw new Renderer.RenderError.SLIDE_DOES_NOT_EXIST(
+                    "The requested slide '%i' does not exist.", slide_number);
             }
 
             // If caching is enabled check for the page in the cache
-            if ( this.cache != null ) {
-                Gdk.Pixmap cache_content;
-                if ( ( cache_content = this.cache.retrieve( slide_number ) ) != null ) {
+            if (this.cache != null) {
+                Cairo.ImageSurface cache_content;
+                if ((cache_content = this.cache.retrieve(slide_number)) != null) {
                     return cache_content;
                 }
             }
 
             // Retrieve the Poppler.Page for the page to render
-            MutexLocks.poppler.lock();
-            var page = metadata.get_document().get_page( slide_number );
-            MutexLocks.poppler.unlock();
+            var page = metadata.get_document().get_page(slide_number);
 
             // A lot of Pdfs have transparent backgrounds defined. We render
             // every page before a white background because of this.
-            Pixmap pixmap = new Pixmap( null, this.width, this.height, 24 );
-            Context cr = Gdk.cairo_create( pixmap );
+            Cairo.ImageSurface surface = new Cairo.ImageSurface(Cairo.Format.RGB24, this.width,
+                this.height);
+            Cairo.Context cr = new Cairo.Context(surface);
 
-            cr.set_source_rgb( 255, 255, 255 );
-            cr.rectangle( 0, 0, this.width, this.height );
+            cr.set_source_rgb(255, 255, 255);
+            cr.rectangle(0, 0, this.width, this.height);
             cr.fill();
 
             cr.scale(this.scaling_factor, this.scaling_factor);
-            MutexLocks.poppler.lock();
+            cr.translate(-metadata.get_horizontal_offset(this.area),
+                -metadata.get_vertical_offset(this.area));
             page.render(cr);
-            MutexLocks.poppler.unlock();
 
             // If the cache is enabled store the newly rendered pixmap
-            if ( this.cache != null ) {
-                this.cache.store( slide_number, pixmap );
+            if (this.cache != null) {
+                this.cache.store( slide_number, surface );
             }
 
-            return pixmap;
+            return surface;
         }
 
-      public override Gdk.Pixmap fade_to_black() {
-            Pixmap pixmap = new Pixmap( null, this.width, this.height, 24 );
-            Context cr = Gdk.cairo_create( pixmap );
+        public override Cairo.ImageSurface fade_to_black() {
+            Cairo.ImageSurface surface = new Cairo.ImageSurface(Cairo.Format.RGB24, this.width,
+                this.height);
+            Cairo.Context cr = new Cairo.Context(surface);
 
-            cr.set_source_rgb( 0, 0, 0 );
-            cr.rectangle( 0, 0, this.width, this.height );
+            cr.set_source_rgb(0, 0, 0);
+            cr.rectangle(0, 0, this.width, this.height);
             cr.fill();
 
             cr.scale(this.scaling_factor, this.scaling_factor);
-            
-            return pixmap;
+
+            return surface;
         }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/scaler.vala
+++ pdf-presenter-console-3.1.1/src/classes/scaler.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -66,13 +66,13 @@ namespace pdfpc {
             } else if ( height == 0 ) {
                 factor = width / this.initial_width;
             } else if ( allow_cutoff == true ) {
-                factor = Math.fmax( 
+                factor = Math.fmax(
                     width / this.initial_width,
                     height / this.initial_height
                 );
             }
             else {
-                factor = Math.fmin( 
+                factor = Math.fmin(
                     width / this.initial_width,
                     height / this.initial_height
                 );
--- pdf-presenter-console-3.1.1.orig/src/classes/timer_label.vala
+++ pdf-presenter-console-3.1.1/src/classes/timer_label.vala
@@ -4,25 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-
 namespace pdfpc {
 
     /**
@@ -60,22 +57,6 @@ namespace pdfpc {
         protected uint timeout = 0;
 
         /**
-         * Color used for normal timer rendering
-         *
-         * This property is public and not accesed using a setter to be able to use
-         * Color.parse directly on it.
-         */
-        public Color normal_color;
-
-        /**
-         * Color used for pre-talk timer rendering
-         *
-         * This property is public and not accesed using a setter to be able to use
-         * Color.parse directly on it.
-         */
-        public Color pretalk_color;
-
-        /**
          * Default constructor taking the initial time as argument, as well as
          * the time to countdown until the talk actually starts.
          *
@@ -85,22 +66,19 @@ namespace pdfpc {
          */
         public TimerLabel( time_t start_time = 0 ) {
             this.start_time = start_time;
-
-            Color.parse( "white", out this.normal_color );
-            Color.parse( "green", out this.pretalk_color );
         }
 
         /**
          * Start the timer
          */
         public virtual void start() {
-            if ( this.timeout != 0 && this.time < 0 ) { 
+            if ( this.timeout != 0 && this.time < 0 ) {
                 // We are in pretalk, with timeout already running.
                 // Jump to talk mode
                 this.time = 0;
             } else if ( this.timeout == 0 ) {
                 // Start the timer if it is not running
-                this.timeout = Timeout.add( 1000, this.on_timeout );
+                this.timeout = GLib.Timeout.add( 1000, this.on_timeout );
             }
         }
 
@@ -161,7 +139,7 @@ namespace pdfpc {
          * Calculate and return the countdown time in (negative) seconds until
          * the talk begins.
          */
-        protected int calculate_countdown() 
+        protected int calculate_countdown()
         {
             time_t now = Time.local( time_t() ).mktime();
             return (int)( now - this.start_time );
@@ -190,8 +168,8 @@ namespace pdfpc {
             hours = timeInSecs / 60 / 60;
             minutes = timeInSecs / 60 % 60;
             seconds = timeInSecs % 60 % 60;
-            
-            this.set_text( 
+
+            this.set_text(
                 "%s%.2u:%.2u:%.2u".printf(
                     prefix,
                     hours,
@@ -214,29 +192,10 @@ namespace pdfpc {
          */
         protected uint last_minutes = 5;
 
-        /**
-         * Color used if last_minutes have been reached
-         *
-         * This property is public and not accesed using a setter to be able to use
-         * Color.parse directly on it.
-         */
-        public Color last_minutes_color;
-        
-        /**
-         * Color used to represent negative number (time is over)
-         *
-         * This property is public and not accesed using a setter to be able to use
-         * Color.parse directly on it.
-         */
-        public Color negative_color;
-
         public CountdownTimer( int duration, uint last_minutes, time_t start_time = 0 ) {
             base(start_time);
             this.duration = duration;
             this.last_minutes = last_minutes;
-
-            Color.parse( "orange", out this.last_minutes_color );
-            Color.parse( "red", out this.negative_color );
         }
 
         /**
@@ -252,38 +211,23 @@ namespace pdfpc {
             // Normally the default is a positive number. Therefore a negative
             // sign is not needed and the prefix is just an empty string.
             string prefix = "";
+            Gtk.StyleContext context = this.get_style_context();
             if ( this.time < 0 ) // pretalk
             {
                 prefix = "-";
                 timeInSecs = -this.time;
-                this.modify_fg( 
-                    StateType.NORMAL, 
-                    this.pretalk_color
-                );
+                context.add_class("pretalk");
             } else {
+                context.remove_class("pretalk");
                 if ( this.time < this.duration ) {
                     timeInSecs = duration - this.time;
                     // Still on presentation time
-                    if ( timeInSecs < this.last_minutes * 60 ) {
-                        this.modify_fg( 
-                            StateType.NORMAL, 
-                            this.last_minutes_color
-                        );
-                    }
-                    else {
-                        this.modify_fg( 
-                            StateType.NORMAL, 
-                            this.normal_color
-                        );
-                    }
-                    
-                }
-                else {
+                    if ( timeInSecs < this.last_minutes * 60 )
+                        context.add_class("last-minutes");
+                } else {
                     // Time is over!
-                    this.modify_fg( 
-                        StateType.NORMAL, 
-                        this.negative_color
-                    );
+                    context.remove_class("last-minutes");
+                    context.add_class("overtime");
                     timeInSecs = this.time - duration;
 
                     // The prefix used for negative time values is a simple minus sign.
@@ -297,7 +241,7 @@ namespace pdfpc {
 
     public class EndTimeTimer : CountdownTimer {
 
-        protected time_t end_time;        
+        protected time_t end_time;
         protected Time end_time_object;
 
         public EndTimeTimer( time_t end_time, uint last_minutes, time_t start_time = 0 ) {
@@ -350,20 +294,15 @@ namespace pdfpc {
             // Normally the default is a positive number. Therefore a negative
             // sign is not needed and the prefix is just an empty string.
             string prefix = "";
+            Gtk.StyleContext context = this.get_style_context();
             if ( this.time < 0 ) // pretalk
             {
                 prefix = "-";
                 timeInSecs = -this.time;
-                this.modify_fg( 
-                               StateType.NORMAL, 
-                               this.pretalk_color
-                              );
+                context.add_class("pretalk");
             } else {
                 timeInSecs = this.time;
-                this.modify_fg( 
-                               StateType.NORMAL, 
-                               this.normal_color
-                              );
+                context.remove_class("pretalk");
             }
             this.show_time(timeInSecs, prefix);
         }
--- pdf-presenter-console-3.1.1.orig/src/classes/view/base.vala
+++ pdf-presenter-console-3.1.1/src/classes/view/base.vala
@@ -4,39 +4,37 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
 namespace pdfpc {
     /**
      * Base class for every slide view
      */
-    public abstract class View.Base: Gtk.DrawingArea {
+    public abstract class View.Base : Gtk.DrawingArea {
         /**
          * Signal fired every time a slide is about to be left
          */
-        public signal void leaving_slide( int from, int to );
+        public signal void leaving_slide(int from, int to);
 
         /**
          * Signal fired every time a slide is entered
          */
-        public signal void entering_slide( int slide_number );
-        
+        public signal void entering_slide(int slide_number);
+
         /**
          * Renderer to be used for rendering the slides
          */
@@ -47,12 +45,9 @@ namespace pdfpc {
          */
         protected Base( Renderer.Base renderer ) {
             this.renderer = renderer;
-            this.set_size_request( 
-                renderer.get_width(),
-                renderer.get_height()
-            );
+            this.set_size_request(renderer.width, renderer.height);
         }
-        
+
         /**
          * Return the used renderer object
          */
@@ -65,7 +60,7 @@ namespace pdfpc {
          *
          * If the slide number does not exist a RenderError.SLIDE_DOES_NOT_EXIST is thrown
          */
-        public abstract void display( int slide_number, bool force_redraw=false )
+        public abstract void display(int slide_number, bool force_redraw=false)
             throws Renderer.RenderError;
 
         /**
@@ -84,3 +79,4 @@ namespace pdfpc {
         public abstract int get_current_slide_number();
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/view/behaviour/base.vala
+++ pdf-presenter-console-3.1.1/src/classes/view/behaviour/base.vala
@@ -4,26 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
-using pdfpc;
-
 namespace pdfpc.View {
     /**
      * Abstract base every View Behaviour implementation has to extend.
@@ -46,7 +42,7 @@ namespace pdfpc.View {
          */
         public Base() {
             // Nothing to do here
-        }        
+        }
 
         /**
          * Return the associated View object
@@ -69,13 +65,13 @@ namespace pdfpc.View {
             }
 
             if ( this.target != null ) {
-                throw new Behaviour.AssociationError.BEHAVIOUR_ALREADY_ASSOCIATED( 
+                throw new Behaviour.AssociationError.BEHAVIOUR_ALREADY_ASSOCIATED(
                     "A behaviour has been associated with two different Views."
                 );
             }
 
             if ( !this.is_supported( target ) ) {
-                throw new Behaviour.AssociationError.VIEW_NOT_SUPPORTED( 
+                throw new Behaviour.AssociationError.VIEW_NOT_SUPPORTED(
                     "The View which should be associated is incompatible to the given Behaviour"
                 );
             }
@@ -102,6 +98,6 @@ namespace pdfpc.View {
      */
     public errordomain Behaviour.AssociationError {
         BEHAVIOUR_ALREADY_ASSOCIATED,
-        VIEW_NOT_SUPPORTED 
+        VIEW_NOT_SUPPORTED
     }
 }
--- /dev/null
+++ pdf-presenter-console-3.1.1/src/classes/view/behaviour/pdf_link.vala
@@ -0,0 +1,163 @@
+/**
+ * Signal Provider for all pdf link related events
+ *
+ * This file is part of pdfpc.
+ *
+ * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+namespace pdfpc.View.Behaviour {
+    /**
+     * Access provider to all signals related to PDF links.
+     */
+    public class PdfLink: Base {
+        /**
+         * The Poppler.LinkMapping which is currently beneath the mouse cursor or null
+         * if there is none.
+         */
+        protected ActionMapping active_mapping = null;
+
+        /**
+         * Poppler.LinkMappings of the current page
+         */
+        protected unowned GLib.List<ActionMapping> page_link_mappings = null;
+
+        /**
+         * Precalculated Gdk.Rectangles for every link mapping
+         */
+        protected Gdk.Rectangle[] precalculated_mapping_rectangles = null;
+
+        public override void associate(View.Base target)
+            throws AssociationError {
+            this.enforce_exclusive_association(target);
+            this.attach(target);
+        }
+
+        /**
+         * Attach a View.Pdf to this signal provider
+         */
+        public void attach( View.Base view ) {
+            this.target = view;
+
+            view.add_events( Gdk.EventMask.BUTTON_PRESS_MASK );
+            view.add_events( Gdk.EventMask.BUTTON_RELEASE_MASK );
+            view.add_events( Gdk.EventMask.POINTER_MOTION_MASK );
+
+            view.button_press_event.connect( this.on_button_press );
+            view.motion_notify_event.connect( this.on_mouse_move );
+            view.entering_slide.connect( this.on_entering_slide );
+            view.leaving_slide.connect( this.on_leaving_slide );
+        }
+
+        /**
+         * Return the Poppler.LinkMapping associated with link for the given
+         * coordinates.
+         *
+         * If there is no link for the given coordinates null is returned
+         * instead.
+         */
+        protected ActionMapping? get_link_mapping_by_coordinates( double x, double y ) {
+            // Try to find a matching link mapping on the page.
+            for( var i=0; i<this.precalculated_mapping_rectangles.length; ++i ) {
+                Gdk.Rectangle r = this.precalculated_mapping_rectangles[i];
+                // A simple bounding box check tells us if the given point lies
+                // within the link area.
+                if ( ( x >= r.x )
+                  && ( x <= r.x + r.width )
+                  && ( y >= r.y )
+                  && ( y <= r.y + r.height ) ) {
+                    return this.page_link_mappings.nth_data( i );
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Called whenever a mouse button is pressed inside the View.Pdf
+         *
+         * Maybe a link has been clicked. Therefore we need to handle this.
+         */
+        protected bool on_button_press( Gtk.Widget source, Gdk.EventButton e ) {
+            // In case the coords belong to a link we will get its action. If
+            // they are pointing nowhere we just get null.
+            ActionMapping mapping = this.get_link_mapping_by_coordinates( e.x, e.y );
+
+            if ( mapping == null ) {
+                return false;
+            }
+
+            return mapping.on_button_press(source, e);
+        }
+
+        /**
+         * Called whenever the mouse is moved on the surface of the View.Pdf
+         *
+         * The signal emitted by this method may for example be used to change
+         * the mouse cursor if the pointer enters or leaves a link
+         */
+        protected bool on_mouse_move( Gtk.Widget source, Gdk.EventMotion event ) {
+            ActionMapping link_mapping = this.get_link_mapping_by_coordinates( event.x, event.y );
+
+            if (link_mapping != this.active_mapping) {
+                if (this.active_mapping != null)
+                    this.active_mapping.on_mouse_leave(source, event);
+                if (link_mapping != null)
+                    link_mapping.on_mouse_enter(source, event);
+            }
+            this.active_mapping = link_mapping;
+            return false;
+        }
+
+        /**
+         * Handle newly entered pdf pages to create a link mapping table for
+         * further requests and checks.
+         */
+        public void on_entering_slide( View.Base source, int page_number ) {
+            // Get the link mapping table
+            bool in_range = true;
+            Metadata.Pdf metadata = source.get_renderer().metadata as Metadata.Pdf;
+            if (page_number < metadata.get_slide_count()) {
+                this.page_link_mappings = metadata.get_action_mapping( page_number );
+            } else {
+                this.page_link_mappings = null;
+                in_range = false;
+            }
+            if (!in_range)
+                return;
+
+            // Precalculate the a Gdk.Rectangle for every link mapping area
+            if ( this.page_link_mappings.length() > 0 ) {
+                this.precalculated_mapping_rectangles = new Gdk.Rectangle[this.page_link_mappings.length()];
+                int i=0;
+                foreach( var mapping in this.page_link_mappings ) {
+                    this.precalculated_mapping_rectangles[i++] = ((View.Pdf)this.target).convert_poppler_rectangle_to_gdk_rectangle(
+                        mapping.area
+                    );
+                }
+            }
+        }
+
+        /**
+         * Free the allocated link mapping tables, which were created on page
+         * entering
+         */
+        public void on_leaving_slide( View.Base source, int from, int to ) {
+            // Free memory of precalculated rectangles
+            this.precalculated_mapping_rectangles = null;
+        }
+    }
+}
--- pdf-presenter-console-3.1.1.orig/src/classes/view/default.vala
+++ pdf-presenter-console-3.1.1/src/classes/view/default.vala
@@ -4,42 +4,36 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Cairo;
-using Gdk;
-
 namespace pdfpc {
     /**
      * Basic view class which is usable with any renderer.
      */
-    public class View.Default: View.Base,
-        View.Prerendering, View.Behaviour.Decoratable {
-
+    public class View.Default : View.Base, View.Prerendering, View.Behaviour.Decoratable {
         /**
          * The currently displayed slide
          */
         protected int current_slide_number;
-        
+
         /**
-         * The pixmap containing the currently shown slide
+         * The surface containing the currently shown slide
          */
-        protected Gdk.Pixmap current_slide;
+        protected Cairo.ImageSurface current_slide;
 
         /**
          * The number of slides in the presentation
@@ -56,27 +50,27 @@ namespace pdfpc {
         /**
          * Base constructor taking the renderer to use as an argument
          */
-        public Default( Renderer.Base renderer) {
-           base( renderer );
+        public Default(Renderer.Base renderer) {
+           base(renderer);
 
            // As we are using our own kind of double buffer and blit in a one
            // time action, we do not need gtk to double buffer as well.
-           this.set_double_buffered( false );
+           this.set_double_buffered(false);
 
            this.current_slide_number = 0;
 
-           this.n_slides = (int)renderer.get_metadata().get_slide_count();
+           this.n_slides = (int) renderer.metadata.get_slide_count();
            this.slide_limit = this.n_slides + 1;
-        
+
            // Render the initial page on first realization.
-           this.add_events( Gdk.EventMask.STRUCTURE_MASK );
-           this.realize.connect( () => {
+           this.add_events(Gdk.EventMask.STRUCTURE_MASK);
+           this.realize.connect(() => {
                 try {
                     this.display( this.current_slide_number );
-                }
-                catch( Renderer.RenderError e ) {
+                } catch( Renderer.RenderError e ) {
                     // There should always be a page 0 but you never know.
-                    error( "Could not render initial page %d: %s", this.current_slide_number, e.message );
+                    error("Could not render initial page %d: %s",
+                        this.current_slide_number, e.message);
                 }
 
                 // Start the prerender cycle if the renderer supports caching
@@ -85,9 +79,8 @@ namespace pdfpc {
                 // Gtk event loop. If it is not proper Gdk thread handling is
                 // impossible.
                 var caching_renderer = this.renderer as Renderer.Caching;
-                if ( caching_renderer != null
-                  && caching_renderer.get_cache() != null 
-                  && caching_renderer.get_cache().allows_prerendering()) {
+                if (caching_renderer != null && caching_renderer.cache != null &&
+                    caching_renderer.cache.allows_prerendering()) {
                     this.register_prerendering();
                 }
            });
@@ -106,13 +99,13 @@ namespace pdfpc {
             int* i = null;
             // The page_count will be transfered into the lamda function as
             // well.
-            var page_count = this.get_renderer().get_metadata().get_slide_count();
-                
+            var page_count = this.get_renderer().metadata.get_slide_count();
+
             this.prerendering_started();
 
             Idle.add(() => {
-                if( i == null ) {
-                    i = malloc( sizeof( int ) );
+                if (i == null) {
+                    i = malloc(sizeof(int));
                     *i = 0;
                 }
 
@@ -121,24 +114,22 @@ namespace pdfpc {
                 // pixmap in the cache if it is enabled. This
                 // is exactly what we want.
                 try {
-                    this.get_renderer().render_to_pixmap( *i );
-                }
-                catch( Renderer.RenderError e ) {
-                    error( "Could not render page '%i' while pre-rendering: %s", *i, e.message );
+                    this.get_renderer().render_to_surface(*i);
+                } catch(Renderer.RenderError e) {
+                    error("Could not render page '%i' while pre-rendering: %s", *i, e.message);
                 }
-                
+
                 // Inform possible observers about the cached slide
                 this.slide_prerendered();
-                
+
                 // Increment one slide for each call and stop the loop if we
                 // have reached the last slide
                 *i = *i + 1;
-                if ( *i >= page_count ) {
+                if (*i >= page_count) {
                     this.prerendering_completed();
-                    free( i );
+                    free(i);
                     return false;
-                }
-                else {
+                } else {
                     return true;
                 }
             });
@@ -150,55 +141,55 @@ namespace pdfpc {
          * The implementation supports an arbitrary amount of different
          * behaviours.
          */
-        public void associate_behaviour( Behaviour.Base behaviour ) {
-            this.behaviours.append( behaviour );
+        public void associate_behaviour(Behaviour.Base behaviour) {
+            this.behaviours.append(behaviour);
             try {
-                behaviour.associate( this );
-            }
-            catch( Behaviour.AssociationError e ) {
-                error( "Behaviour association failure: %s", e.message );
+                behaviour.associate(this);
+            } catch(Behaviour.AssociationError e) {
+                error("Behaviour association failure: %s", e.message);
             }
         }
-        
+
         /**
          * Display a specific slide number
          *
          * If the slide number does not exist a
          * RenderError.SLIDE_DOES_NOT_EXIST is thrown
          */
-        public override void display( int slide_number, bool force_redraw=false )
+        public override void display(int slide_number, bool force_redraw=false)
             throws Renderer.RenderError {
 
             // If the slide is out of bounds render the outer most slide on
             // each side of the document.
-            if ( slide_number < 0 ) {
+            if (slide_number < 0) {
                 slide_number = 0;
             }
-            if ( slide_number >= this.slide_limit ) {
+            if (slide_number >= this.slide_limit) {
                 slide_number = this.slide_limit - 1;
             }
 
-            if ( !force_redraw && slide_number == this.current_slide_number && this.current_slide != null ) {
+            if (!force_redraw && slide_number == this.current_slide_number &&
+                this.current_slide != null) {
                 // The slide does not need to be changed, as the correct one is
                 // already shown.
                 return;
             }
 
             // Notify all listeners
-            this.leaving_slide( this.current_slide_number, slide_number );
+            this.leaving_slide(this.current_slide_number, slide_number);
 
             // Render the requested slide
             // An exception is thrown here, if the slide can not be rendered.
             if (slide_number < this.n_slides)
-                this.current_slide = this.renderer.render_to_pixmap( slide_number );
+                this.current_slide = this.renderer.render_to_surface(slide_number);
             else
                 this.current_slide = this.renderer.fade_to_black();
             this.current_slide_number = slide_number;
 
             // Have Gtk update the widget
-            this.queue_draw_area( 0, 0, this.renderer.get_width(), this.renderer.get_height() );
+            this.queue_draw_area(0, 0, this.renderer.width, this.renderer.height);
 
-            this.entering_slide( this.current_slide_number );
+            this.entering_slide(this.current_slide_number);
         }
 
         /**
@@ -206,7 +197,7 @@ namespace pdfpc {
          */
         public override void fade_to_black() {
             this.current_slide = this.renderer.fade_to_black();
-            this.queue_draw_area( 0, 0, this.renderer.get_width(), this.renderer.get_height() );
+            this.queue_draw_area(0, 0, this.renderer.width, this.renderer.height);
         }
 
         /**
@@ -229,20 +220,9 @@ namespace pdfpc {
          * The implementation does a simple blit from the internal pixmap to
          * the window surface.
          */
-        public override bool expose_event ( Gdk.EventExpose event ) {
-            Context cr = Gdk.cairo_create( this.window );
-            Gdk.cairo_set_source_pixmap(
-                cr,
-                this.current_slide,
-                event.area.x,
-                event.area.y
-            );
-            cr.rectangle(
-                event.area.x,
-                event.area.y,
-                event.area.width,
-                event.area.height
-            );
+        public override bool draw(Cairo.Context cr) {
+            cr.set_source_surface(this.current_slide, 0, 0);
+            cr.rectangle(0, 0, this.current_slide.get_width(), this.current_slide.get_height());
             cr.fill();
 
             // We are the only ones drawing on this context skip everything
@@ -251,3 +231,4 @@ namespace pdfpc {
         }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/view/pdf.vala
+++ pdf-presenter-console-3.1.1/src/classes/view/pdf.vala
@@ -4,25 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-using Gdk;
-
 namespace pdfpc {
     /**
      * View spezialized to work with Pdf renderers.
@@ -32,17 +29,17 @@ namespace pdfpc {
      *
      * By default it does not implement any further functionality.
      */
-    public class View.Pdf: View.Default {
+    public class View.Pdf : View.Default {
         /**
          * Default constructor restricted to Pdf renderers as input parameter
          */
-        public Pdf( Renderer.Pdf renderer, bool allow_black_on_end, PresentationController presentation_controller ) {
-            base( renderer );
+        public Pdf(Renderer.Pdf renderer, bool allow_black_on_end, bool clickable_links,
+            PresentationController presentation_controller) {
+            base(renderer);
 
-            // Enable the PDFLink Behaviour by default on PDF Views
-            this.associate_behaviour( 
-                new View.Behaviour.PdfLink.Implementation( presentation_controller )
-            );
+            if (clickable_links)
+                // Enable the PDFLink Behaviour by default on PDF Views
+                this.associate_behaviour(new View.Behaviour.PdfLink());
         }
 
         /**
@@ -54,21 +51,14 @@ namespace pdfpc {
          * aspect ration. The scale rectangle is provided in the scale_rect
          * argument.
          */
-        public static View.Pdf from_metadata( Metadata.Pdf metadata, int width, int height, bool allow_black_on_end,
-                                              PresentationController presentation_controller,
-                                              out Rectangle scale_rect = null ) {
-            var scaler = new Scaler( 
-                metadata.get_page_width(),
-                metadata.get_page_height()
-            );
-            scale_rect = scaler.scale_to( width, height );
-            var renderer = new Renderer.Pdf( 
-                metadata,
-                scale_rect.width,
-                scale_rect.height
-            );
-            
-            return new View.Pdf( renderer, allow_black_on_end, presentation_controller );
+        public Pdf.from_metadata(Metadata.Pdf metadata, int width, int height,
+            Metadata.Area area, bool allow_black_on_end, bool clickable_links,
+            PresentationController presentation_controller, out Gdk.Rectangle scale_rect = null) {
+            var scaler = new Scaler(metadata.get_page_width(), metadata.get_page_height());
+            scale_rect = scaler.scale_to(width, height);
+            var renderer = new Renderer.Pdf(metadata, scale_rect.width, scale_rect.height, area);
+
+            this(renderer, allow_black_on_end, clickable_links, presentation_controller);
         }
 
         /**
@@ -77,5 +67,37 @@ namespace pdfpc {
         public new Renderer.Pdf get_renderer() {
             return this.renderer as Renderer.Pdf;
         }
+
+        /**
+         * Convert an arbitrary Poppler.Rectangle struct into a Gdk.Rectangle
+         * struct taking into account the measurement differences between pdf
+         * space and screen space.
+         */
+        public Gdk.Rectangle convert_poppler_rectangle_to_gdk_rectangle(
+            Poppler.Rectangle poppler_rectangle) {
+            Gdk.Rectangle gdk_rectangle = Gdk.Rectangle();
+
+            Gtk.Requisition requisition;
+            Gtk.Requisition min_requisition;
+            this.get_preferred_size(out min_requisition, out requisition);
+
+            // We need the page dimensions for coordinate conversion between
+            // pdf coordinates and screen coordinates
+            var metadata = this.get_renderer().metadata as Metadata.Pdf;
+            gdk_rectangle.x = (int) Math.ceil((poppler_rectangle.x1 / metadata.get_page_width()) *
+                requisition.width );
+            gdk_rectangle.width = (int) Math.floor(((poppler_rectangle.x2 - poppler_rectangle.x1 ) /
+                metadata.get_page_width()) * requisition.width);
+
+            // Gdk has its coordinate origin in the upper left, while Poppler
+            // has its origin in the lower left.
+            gdk_rectangle.y = (int) Math.ceil(((metadata.get_page_height() - poppler_rectangle.y2) /
+                metadata.get_page_height()) * requisition.height);
+            gdk_rectangle.height = (int) Math.floor(((poppler_rectangle.y2 - poppler_rectangle.y1) /
+                metadata.get_page_height()) * requisition.height);
+
+            return gdk_rectangle;
+        }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/window/fullscreen.vala
+++ pdf-presenter-console-3.1.1/src/classes/window/fullscreen.vala
@@ -4,25 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-
 namespace pdfpc.Window {
     /**
      * Window extension implementing all the needed functionality, to be
@@ -31,11 +28,11 @@ namespace pdfpc.Window {
      * Methods to specify the monitor to be displayed on in a multi-head setup
      * are provided as well.
      */
-    public class Fullscreen: Gtk.Window {
+    public class Fullscreen : Gtk.Window {
         /**
          * The geometry data of the screen this window is on
          */
-        protected Rectangle screen_geometry;
+        protected Gdk.Rectangle screen_geometry;
 
         /**
          * Timer id which monitors mouse motion to hide the cursor after 5
@@ -53,44 +50,48 @@ namespace pdfpc.Window {
          */
         protected bool frozen = false;
 
-        public Fullscreen( int screen_num ) {
+        public Fullscreen(int screen_num, int width = -1, int height = -1) {
             Gdk.Screen screen;
 
-            if ( screen_num >= 0 ) {
+            if (screen_num >= 0) {
                 // Start in the given monitor
-                screen = Screen.get_default();
-                screen.get_monitor_geometry( screen_num, out this.screen_geometry );
+                screen = Gdk.Screen.get_default();
+                screen.get_monitor_geometry(screen_num, out this.screen_geometry);
             } else {
                 // Start in the monitor the cursor is in
-                var display = Gdk.Display.get_default();
+                var display = Gdk.Display.get_default().get_device_manager().get_client_pointer();
                 int pointerx, pointery;
-                display.get_pointer(out screen, out pointerx, out pointery, null);
+                display.get_position(out screen, out pointerx, out pointery);
                 int current_screen = screen.get_monitor_at_point(pointerx, pointery);
-                screen.get_monitor_geometry( current_screen, out this.screen_geometry );
+                screen.get_monitor_geometry(current_screen, out this.screen_geometry);
             }
 
-            if ( !Options.windowed ) {
+            if (!Options.windowed) {
                 // Move to the correct monitor
                 // This movement is done here and after mapping, to minimize flickering
                 // with window managers, which correctly handle the movement command,
                 // before the window is mapped.
-                this.move( this.screen_geometry.x, this.screen_geometry.y );
-                //this.fullscreen();
+                this.move(this.screen_geometry.x, this.screen_geometry.y);
 
                 // As certain window-managers like Xfwm4 ignore movement request
                 // before the window is initially moved and set up we need to
                 // listen to this event.
-                this.size_allocate.connect( this.on_size_allocate );
-                this.configure_event.connect( this.on_configure );
+                this.size_allocate.connect(this.on_size_allocate);
+                this.configure_event.connect(this.on_configure);
             }
             else {
-                this.screen_geometry.width /= 2;
-                this.screen_geometry.height /= 2;
+                if (width > 0 && height > 0) {
+                        this.screen_geometry.width = width;
+                        this.screen_geometry.height = height;
+                } else {
+                        this.screen_geometry.width /= 2;
+                        this.screen_geometry.height /= 2;
+                }
                 this.resizable = false;
             }
 
-            this.add_events(EventMask.POINTER_MOTION_MASK);
-            this.motion_notify_event.connect( this.on_mouse_move );
+            this.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
+            this.motion_notify_event.connect(this.on_mouse_move);
 
             // Start the 5 seconds timeout after which the mouse curosr is
             // hidden
@@ -98,7 +99,7 @@ namespace pdfpc.Window {
         }
 
         // We got to fullscreen once we have moved
-        protected bool on_configure( Gdk.EventConfigure e) {
+        protected bool on_configure(Gdk.EventConfigure e) {
             this.fullscreen();
             this.configure_event.disconnect(this.on_configure);
             return false;
@@ -111,34 +112,27 @@ namespace pdfpc.Window {
          * movement commands before the window has been displayed for the first
          * time.
          */
-        protected void on_size_allocate( Gtk.Widget source, Rectangle r ) {
-            if ( this.is_mapped() ) {
+        protected void on_size_allocate(Gtk.Allocation allocation) {
+            if (this.get_mapped()) {
                 // We are only interested to handle this event AFTER the window has
                 // been mapped.
 
                 // Remove the signal handler, as we only want to handle this once
-                this.size_allocate.disconnect( this.on_size_allocate );
+                this.size_allocate.disconnect(this.on_size_allocate);
 
                 // We only need to do all this, if the window is not at the
                 // correct position. Otherwise it would only cause flickering
                 // without any effect.
-                int x,y;
-                this.get_position( out x, out y );
-                if ( x == this.screen_geometry.x && y == this.screen_geometry.y ) {
+                int x, y;
+                this.get_position(out x, out y);
+                if (x == this.screen_geometry.x && y == this.screen_geometry.y) {
                     return;
                 }
 
-                // Certain window manager (eg. Xfce4) do not allow window movement
-                // while the window is maximized. Therefore it is ensured the
-                // window is not in this state.
-                //this.unfullscreen();
-                //this.unmaximize();
-
                 // The first movement might not have worked as expected, because of
                 // the before mentioned maximized window problem. Therefore it is
                 // done again
-                this.move( this.screen_geometry.x, this.screen_geometry.y );
-                //this.resize( this.screen_geometry.width, this.screen_geometry.height );
+                this.move(this.screen_geometry.x, this.screen_geometry.y);
 
                 this.fullscreen();
             }
@@ -147,12 +141,12 @@ namespace pdfpc.Window {
         /**
          * Called every time the mouse cursor is moved
          */
-        public bool on_mouse_move( Gtk.Widget source, EventMotion event ) {
+        public bool on_mouse_move(Gtk.Widget source, Gdk.EventMotion event) {
             // Restore the mouse cursor to its default value
-            this.window.set_cursor( null );
+            this.get_window().set_cursor(null);
 
             this.restart_hide_cursor_timer();
-            
+
             return false;
         }
 
@@ -160,14 +154,11 @@ namespace pdfpc.Window {
          * Restart the 5 seconds timeout before hiding the mouse cursor
          */
         protected void restart_hide_cursor_timer(){
-            if ( this.hide_cursor_timeout != 0 ) {
-                Source.remove( this.hide_cursor_timeout );
+            if (this.hide_cursor_timeout != 0) {
+                Source.remove(this.hide_cursor_timeout);
             }
 
-            this.hide_cursor_timeout = Timeout.add_seconds(
-                5,
-                this.on_hide_cursor_timeout
-            );
+            this.hide_cursor_timeout = Timeout.add_seconds(5, this.on_hide_cursor_timeout);
         }
 
         /**
@@ -178,17 +169,12 @@ namespace pdfpc.Window {
             this.hide_cursor_timeout = 0;
 
             // Window might be null in case it has not been mapped
-            if ( this.window != null ) {
-                this.window.set_cursor(
-                    new Gdk.Cursor( 
-                        Gdk.CursorType.BLANK_CURSOR
-                    )
-                );
+            if (this.get_window() != null) {
+                this.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.BLANK_CURSOR));
 
                 // After the timeout disabled the cursor do not run it again
                 return false;
-            }
-            else {
+            } else {
                 // The window was not available. Possibly it was not mapped
                 // yet. We simply try it again if the mouse isn't moved for
                 // another five seconds.
@@ -197,3 +183,4 @@ namespace pdfpc.Window {
         }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/window/overview.vala
+++ pdf-presenter-console-3.1.1/src/classes/window/overview.vala
@@ -4,27 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-
-using pdfpc;
-
 namespace pdfpc.Window {
     /**
      * An overview of all the slides in the form of a table
@@ -34,11 +29,11 @@ namespace pdfpc.Window {
         /*
          * The store of all the slides.
          */
-        protected ListStore slides;
+        protected Gtk.ListStore slides;
         /*
          * The view of the above.
          */
-        protected IconView slides_view;
+        protected Gtk.IconView slides_view;
 
         /**
          * We will need the metadata mainly for converting from user slides to
@@ -102,7 +97,7 @@ namespace pdfpc.Window {
         private int _current_slide = 0;
         public int current_slide {
             get { return _current_slide; }
-            set { var path = new TreePath.from_indices(value);
+            set { var path = new Gtk.TreePath.from_indices(value);
                   this.slides_view.select_path(path);
                   // _current_slide set in on_selection_changed, below
                   this.slides_view.set_cursor(path, null, false);
@@ -131,25 +126,15 @@ namespace pdfpc.Window {
          * Constructor
          */
         public Overview( Metadata.Pdf metadata, PresentationController presentation_controller, Presenter presenter ) {
-            this.slides = new ListStore(1, typeof(Pixbuf));
-            this.slides_view = new IconView.with_model(this.slides);
-            this.slides_view.selection_mode = SelectionMode.SINGLE;
+            this.slides = new Gtk.ListStore(1, typeof(Gdk.Pixbuf));
+            this.slides_view = new Gtk.IconView.with_model(this.slides);
+            this.slides_view.selection_mode = Gtk.SelectionMode.SINGLE;
             var renderer = new CellRendererHighlight();
             this.slides_view.pack_start(renderer, true);
             this.slides_view.add_attribute(renderer, "pixbuf", 0);
             this.slides_view.set_item_padding(0);
+            this.slides_view.show();
             this.add(this.slides_view);
-            this.show_all();
-
-            Color black;
-            Color white;
-            Color.parse("black", out black);
-            Color.parse("white", out white);
-            this.slides_view.modify_base(StateType.NORMAL, black);
-            Gtk.Scrollbar vscrollbar = (Gtk.Scrollbar) this.get_vscrollbar();
-            vscrollbar.modify_bg(StateType.NORMAL, white);
-            vscrollbar.modify_bg(StateType.ACTIVE, black);
-            vscrollbar.modify_bg(StateType.PRELIGHT, white);
 
             this.metadata = metadata;
             this.presentation_controller = presentation_controller;
@@ -160,7 +145,7 @@ namespace pdfpc.Window {
             this.slides_view.button_release_event.connect( this.on_mouse_release );
             this.slides_view.key_press_event.connect( this.on_key_press );
             this.slides_view.selection_changed.connect( this.on_selection_changed );
-            this.parent_set.connect( this.on_parent_set );
+            this.key_press_event.connect((event) => this.slides_view.key_press_event(event));
 
             this.aspect_ratio = this.metadata.get_page_width() / this.metadata.get_page_height();
         }
@@ -171,31 +156,20 @@ namespace pdfpc.Window {
             this.fill_structure();
         }
 
-        /*
-         * Due to a change, the Overview is no longer shown or hidden; instead, its
-         * parent is.  So we need to connect to its parent's show and hide signals.
-         * But we can't do that until the parent has been set.  Thus this signal
-         * handler.  Note that if the Overview is reparented, it will still be
-         * connected to signals from its old parent.  So don't do that.
-         */
-        public void on_parent_set(Widget? old_parent) {
-            if (this.parent != null) {
-                this.parent.show.connect( this.on_show );
-                this.parent.hide.connect( this.on_hide );
-            }
-        }
-
         /**
-         * Get keyboard focus.
+         * Get keyboard focus.  This requires that the window has focus.
          */
-        public void on_show() {
+        public void ensure_focus() {
+            Gtk.Window top = this.get_toplevel() as Gtk.Window;
+            if (top != null && !top.has_toplevel_focus)
+                top.present();
             this.slides_view.grab_focus();
         }
 
         /*
          * Recalculate the structure, if needed.
          */
-        public void on_hide() {
+        public void ensure_structure() {
             if (this.n_slides != this.last_structure_n_slides)
                 this.fill_structure();
         }
@@ -207,20 +181,20 @@ namespace pdfpc.Window {
         protected void fill_structure() {
             if (this.max_width == -1)
                 return;
-            
+
             this.slides_view.set_margin(0);
 
             var margin = this.slides_view.get_margin();
             var padding = this.slides_view.get_item_padding() + 1; // Additional mystery pixel
             var row_spacing = this.slides_view.get_row_spacing();
             var col_spacing = this.slides_view.get_column_spacing();
-            
+
             var eff_max_width = this.max_width - 2 * margin;
             var eff_max_height = this.max_height - 2 * margin;
             int cols = eff_max_width / (Options.min_overview_width + 2 * padding + col_spacing);
             int widthx, widthy, min_width, rows;
             int tc = 0;
-            
+
             // Search for the layout with the widest icons.  We do this by considering
             // layouts with different numbers of columns, and figuring the maximum
             // width for the icon so that all the icons fit both horizontally and
@@ -237,7 +211,7 @@ namespace pdfpc.Window {
                                                                 // doesn't increase height
                 if (widthy < Options.min_overview_width)
                     break;
-                
+
                 min_width = widthx < widthy ? widthx : widthy;
                 if (min_width >= this.target_width) {  // If two layouts give the same width
                     this.target_width = min_width;     // (which happens when they're limited
@@ -252,7 +226,7 @@ namespace pdfpc.Window {
             } else {
                 this.slides_view.columns = tc;
             }
-            this.set_policy(PolicyType.NEVER, PolicyType.AUTOMATIC);
+            this.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
             this.target_height = (int)Math.round(this.target_width / this.aspect_ratio);
             rows = (int)Math.ceil((float)this.n_slides / this.slides_view.columns);
             int full_height = rows*(this.target_height + 2*padding + 2*row_spacing) + 2*margin;
@@ -263,9 +237,10 @@ namespace pdfpc.Window {
             this.last_structure_n_slides = this.n_slides;
 
             this.slides.clear();
-            var pixbuf = new Pixbuf(Colorspace.RGB, true, 8, this.target_width, this.target_height);
+            var pixbuf = new Gdk.Pixbuf(Gdk.Colorspace.RGB, true, 8, this.target_width,
+                this.target_height);
             pixbuf.fill(0x7f7f7fff);
-            var iter = TreeIter();
+            var iter = Gtk.TreeIter();
             for (int i=0; i<this.n_slides; i++) {
                 this.slides.append(out iter);
                 this.slides.set_value(iter, 0, pixbuf);
@@ -288,23 +263,24 @@ namespace pdfpc.Window {
             this.next_undone_preview = 0;
             this.idle_id = GLib.Idle.add(this._fill_previews);
         }
-        
+
         protected bool _fill_previews() {
             if (this.cache == null || this.next_undone_preview >= this.n_slides)
                 return false;
 
             // We get the dimensions from the first button and first slide,
             // should be the same for all
-            int pixmap_width, pixmap_height;
-            this.cache.retrieve(0).get_size(out pixmap_width, out pixmap_height);
-            var pixbuf = new Gdk.Pixbuf(Gdk.Colorspace.RGB, true, 8, pixmap_width, pixmap_height);
-            Gdk.pixbuf_get_from_drawable(pixbuf,
-                this.cache.retrieve(metadata.user_slide_to_real_slide(this.next_undone_preview)),
-                null, 0, 0, 0, 0, pixmap_width, pixmap_height);
+            int surface_width, surface_height;
+            var firstSlide = this.cache.retrieve(0);
+            surface_width = firstSlide.get_width();
+            surface_height = firstSlide.get_height();
+
+            var slideToFill = this.cache.retrieve(metadata.user_slide_to_real_slide(this.next_undone_preview));
+            Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_surface(slideToFill, 0, 0, surface_width, surface_height);
             var pixbuf_scaled = pixbuf.scale_simple(this.target_width, this.target_height,
                                                     Gdk.InterpType.BILINEAR);
 
-            var iter = TreeIter();
+            var iter = Gtk.TreeIter();
             this.slides.get_iter_from_string(out iter, @"$(this.next_undone_preview)");
             this.slides.set_value(iter, 0, pixbuf_scaled);
 
@@ -319,7 +295,7 @@ namespace pdfpc.Window {
             this.cache = cache;
             this.fill_previews();
         }
-        
+
         /**
          * Set the number of slides. If it is different to what we know, it
          * triggers a rebuilding of the widget.
@@ -342,7 +318,7 @@ namespace pdfpc.Window {
          */
         public void remove_current(int newn) {
             this.n_slides = newn;
-            var iter = TreeIter();
+            var iter = Gtk.TreeIter();
             this.slides.get_iter_from_string(out iter, @"$(this.current_slide)");
             this.slides.remove(iter);
             if (this.current_slide >= this.n_slides)
@@ -354,7 +330,7 @@ namespace pdfpc.Window {
          * the standard IconView controls, the rest are passed back to the
          * PresentationController.
          */
-        public bool on_key_press(Gtk.Widget source, EventKey key) {
+        public bool on_key_press(Gtk.Widget source, Gdk.EventKey key) {
             bool handled = false;
             switch ( key.keyval ) {
                 case 0xff51: /* Cursor left */
@@ -373,15 +349,15 @@ namespace pdfpc.Window {
                     this.presentation_controller.goto_user_page(this.current_slide + 1);
                     break;
             }
-                    
+
             return handled;
         }
 
         /*
          * Update the selection when the mouse moves over a new slides.
          */
-        public bool on_mouse_move(Gtk.Widget source, EventMotion event) {
-            TreePath path;
+        public bool on_mouse_move(Gtk.Widget source, Gdk.EventMotion event) {
+            Gtk.TreePath path;
             path = this.slides_view.get_path_at_pos((int)event.x, (int)event.y);
             if (path != null && path.get_indices()[0] != this.current_slide)
                 this.current_slide = path.get_indices()[0];
@@ -393,7 +369,7 @@ namespace pdfpc.Window {
          * click, the button_press event will have set the current slide.  On
          * a drag, the current slide will have been updated by the motion.
          */
-        public bool on_mouse_release(EventButton event) {
+        public bool on_mouse_release(Gdk.EventButton event) {
             if (event.button == 1)
                 this.presentation_controller.goto_user_page(this.current_slide + 1);
             return false;
@@ -403,17 +379,13 @@ namespace pdfpc.Window {
     /*
      * Render a pixbuf that is slightly shaded, unless it is the selected one.
      */
-    public class CellRendererHighlight: CellRendererPixbuf {
-        
-        public override void render(Gdk.Window window, Widget widget,
-                                    Rectangle background_area, Rectangle cell_area,
-                                    Rectangle expose_area, CellRendererState flags) {
-            base.render(window, widget, background_area, cell_area, expose_area, flags);
-            if (flags != CellRendererState.SELECTED) {
-                var cr = Gdk.cairo_create(window);
-                Gdk.cairo_rectangle(cr, expose_area);
-                cr.clip();
-                
+    public class CellRendererHighlight: Gtk.CellRendererPixbuf {
+
+        public override void render(Cairo.Context cr, Gtk.Widget widget,
+                                    Gdk.Rectangle background_area, Gdk.Rectangle cell_area,
+                                    Gtk.CellRendererState flags) {
+            base.render(cr, widget, background_area, cell_area, flags);
+            if ((flags & Gtk.CellRendererState.SELECTED) == 0) {
                 Gdk.cairo_rectangle(cr, cell_area);
                 cr.set_source_rgba(0,0,0,0.2);
                 cr.fill();
--- pdf-presenter-console-3.1.1.orig/src/classes/window/presentation.vala
+++ pdf-presenter-console-3.1.1/src/classes/window/presentation.vala
@@ -4,37 +4,40 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-
-using pdfpc;
-
 namespace pdfpc.Window {
     /**
      * Window showing the currently active slide to be presented on a beamer
      */
-    public class Presentation: Fullscreen, Controllable {
+    public class Presentation : Fullscreen, Controllable {
         /**
-         * Controller handling all the events which might happen. Furthermore it is
-         * responsible to update all the needed visual stuff if needed
+         * The registered PresentationController
          */
-        protected PresentationController presentation_controller = null;
+        public PresentationController presentation_controller { get; protected set; }
+
+        /**
+         * The only view is the main view.
+         */
+        public View.Pdf main_view {
+            get {
+                return this.view as View.Pdf;
+            }
+        }
 
         /**
          * View containing the slide to show
@@ -44,149 +47,78 @@ namespace pdfpc.Window {
         /**
          * Base constructor instantiating a new presentation window
          */
-        public Presentation( Metadata.Pdf metadata, int screen_num, PresentationController presentation_controller ) {
-            base( screen_num );
+        public Presentation(Metadata.Pdf metadata, int screen_num,
+            PresentationController presentation_controller, int width = -1, int height = -1) {
+            base(screen_num, width, height);
+            this.role = "presentation";
 
-            this.destroy.connect( (source) => {
-                Gtk.main_quit();
-            } );
+            this.destroy.connect((source) => presentation_controller.quit());
 
             this.presentation_controller = presentation_controller;
-            this.presentation_controller.register_controllable( this );
 
-            Color black;
-            Color.parse( "black", out black );
-            this.modify_bg( StateType.NORMAL, black );
-
-            var fixedLayout = new Fixed();
+            var fixedLayout = new Gtk.Fixed();
             fixedLayout.set_size_request(this.screen_geometry.width, this.screen_geometry.height);
-            this.add( fixedLayout );
-            
-            Rectangle scale_rect;
-            
-            this.view = View.Pdf.from_metadata( 
-                metadata,
-                this.screen_geometry.width, 
-                this.screen_geometry.height,
-                Options.black_on_end,
-                this.presentation_controller,
-                out scale_rect
-            );
-
-            if ( !Options.disable_caching ) {
-                ((Renderer.Caching)this.view.get_renderer()).set_cache( 
-                    Renderer.Cache.OptionFactory.create( 
-                        metadata
-                    )
-                );
-            }
+            this.add(fixedLayout);
 
-            // Center the scaled pdf on the monitor
-            // In most cases it will however fill the full screen
-            fixedLayout.put(
-                this.view,
-                scale_rect.x,
-                scale_rect.y
-            );
-
-            this.add_events(EventMask.KEY_PRESS_MASK);
-            this.add_events(EventMask.BUTTON_PRESS_MASK);
-            this.add_events(EventMask.SCROLL_MASK);
-
-            this.key_press_event.connect( this.on_key_pressed );
-            this.button_press_event.connect( this.on_button_press );
-            this.scroll_event.connect( this.on_scroll );
-        }
+            Gdk.Rectangle scale_rect;
 
-        /**
-         * Handle keypress vents on the window and, if neccessary send them to the
-         * presentation controller
-         */
-        protected bool on_key_pressed( EventKey key ) {
-            if ( this.presentation_controller != null ) {
-                this.presentation_controller.key_press( key );
+            if (width < 0) {
+                width = this.screen_geometry.width;
             }
-            return false;
-        }
 
-        /**
-         * Handle mouse button events on the window and, if neccessary send
-         * them to the presentation controller
-         */
-        protected bool on_button_press( EventButton button ) {
-            if ( this.presentation_controller != null ) {
-                this.presentation_controller.button_press( button );
+            if (height < 0) {
+                height = this.screen_geometry.height;
             }
-            return false;
-        }
 
-        /**
-         * Handle mouse scrolling events on the window and, if neccessary send
-         * them to the presentation controller
-         */
-        protected bool on_scroll( Gtk.Widget source, EventScroll scroll ) {
-            if ( this.presentation_controller != null ) {
-                this.presentation_controller.scroll( scroll );
+            this.view = new View.Pdf.from_metadata(metadata, width, height, Metadata.Area.CONTENT,
+                Options.black_on_end, true, this.presentation_controller, out scale_rect);
+
+            if (!Options.disable_caching) {
+                ((Renderer.Caching) this.view.get_renderer()).cache =
+                    Renderer.Cache.create(metadata);
             }
-            return false;
+
+            // Center the scaled pdf on the monitor
+            // In most cases it will however fill the full screen
+            fixedLayout.put(this.view, scale_rect.x, scale_rect.y);
+
+            this.add_events(Gdk.EventMask.KEY_PRESS_MASK);
+            this.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
+            this.add_events(Gdk.EventMask.SCROLL_MASK);
+
+            this.key_press_event.connect(this.presentation_controller.key_press);
+            this.button_press_event.connect(this.presentation_controller.button_press);
+            this.scroll_event.connect(this.presentation_controller.scroll);
+
+            this.presentation_controller.register_controllable(this);
         }
 
         /**
          * Set the presentation controller which is notified of keypresses and
          * other observed events
          */
-        public void set_controller( PresentationController controller ) {
+        public void set_controller(PresentationController controller) {
             this.presentation_controller = controller;
         }
 
         /**
-         * Return the PresentationController
-         */
-        public PresentationController? get_controller() {
-            return this.presentation_controller;
-        }
-
-        /**
          * Update the display
          */
         public void update() {
-            if (this.presentation_controller.is_faded_to_black()) {
+            if (this.presentation_controller.faded_to_black) {
                 this.view.fade_to_black();
                 return;
             }
-            if (this.presentation_controller.is_frozen())
+            if (this.presentation_controller.frozen)
                 return;
+
             try {
-                this.view.display(this.presentation_controller.get_current_slide_number(), true);
-            }
-            catch( Renderer.RenderError e ) {
-                GLib.error( "The pdf page %d could not be rendered: %s", this.presentation_controller.get_current_slide_number(), e.message );
+                this.view.display(this.presentation_controller.current_slide_number, true);
+            } catch (Renderer.RenderError e) {
+                error("The pdf page %d could not be rendered: %s",
+                    this.presentation_controller.current_slide_number, e.message );
             }
         }
-            
-        /**
-         * Edit note for current slide. We don't do anything.
-         */
-        public void edit_note() {
-        }
-
-        /**
-         * Ask for the page to jump to. We don't do anything
-         */
-        public void ask_goto_page() {
-        }
-
-        /**
-         * Show an overview. We don't do anything (yet?)
-         */
-        public void show_overview() {
-        }
-
-        /**
-         * Hide the overview. We don't do anything
-         */
-        public void hide_overview() {
-        }
 
         /**
          * Set the cache observer for the Views on this window
@@ -195,11 +127,12 @@ namespace pdfpc.Window {
          * this window correctly with the CacheStatus object to provide acurate
          * cache status measurements.
          */
-        public void set_cache_observer( CacheStatus observer ) {
+        public void set_cache_observer(CacheStatus observer) {
             var prerendering_view = this.view as View.Prerendering;
-            if( prerendering_view != null ) {
-                observer.monitor_view( prerendering_view );
+            if (prerendering_view != null) {
+                observer.monitor_view(prerendering_view);
             }
         }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/classes/window/presenter.vala
+++ pdf-presenter-console-3.1.1/src/classes/window/presenter.vala
@@ -4,27 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-using Gdk;
-
-using pdfpc;
-
 namespace pdfpc.Window {
     /**
      * Window showing the currently active and next slide.
@@ -32,12 +27,20 @@ namespace pdfpc.Window {
      * Other useful information like time slide count, ... can be displayed here as
      * well.
      */
-    public class Presenter: Fullscreen, Controllable {
+    public class Presenter : Fullscreen, Controllable {
         /**
-         * Controller handling all the events which might happen. Furthermore it is
-         * responsible to update all the needed visual stuff if needed
+         * The registered PresentationController
          */
-        protected PresentationController presentation_controller = null;
+        public PresentationController presentation_controller { get; protected set; }
+
+        /**
+         * Only handle links and annotations on the current_view
+         */
+        public View.Pdf main_view {
+            get {
+                return this.current_view as View.Pdf;
+            }
+        }
 
         /**
          * View showing the current slide
@@ -63,9 +66,9 @@ namespace pdfpc.Window {
         /**
          * Slide progress label ( eg. "23/42" )
          */
-        protected Entry slide_progress;
+        protected Gtk.Entry slide_progress;
 
-        protected ProgressBar prerender_progress;
+        protected Gtk.ProgressBar prerender_progress;
 
         /**
          * Indication that the slide is blanked (faded to black)
@@ -85,12 +88,7 @@ namespace pdfpc.Window {
         /**
          * Text box for displaying notes for the slides
          */
-        protected TextView notes_view;
-
-        /**
-         * The views of the slides + notes
-         */
-        protected HBox slideViews = null;
+        protected Gtk.TextView notes_view;
 
         /**
          * The overview of slides
@@ -98,22 +96,9 @@ namespace pdfpc.Window {
         protected Overview overview = null;
 
         /**
-         * The container that shows the overview. This is the one that has to
-         * be shown or hidden
-         */
-        protected Alignment centered_overview = null;
-
-        /**
-         * There may be problems in some configurations if adding the overview
-         * from the beginning, therefore we delay it until it is first shown.
+         * The Stack containing the slides view and the overview.
          */
-        protected bool overview_added = false;
-
-        /**
-         * We will also need to store the layout where we have to add the
-         * overview (see the comment above)
-         */
-        protected VBox fullLayout = null;
+        protected Gtk.Stack slide_stack;
 
         /**
          * Number of slides inside the presentation
@@ -129,34 +114,22 @@ namespace pdfpc.Window {
         protected Metadata.Pdf metadata;
 
         /**
-         * Useful colors
-         */
-        protected Color black;
-        protected Color white;
-
-        /**
          * Base constructor instantiating a new presenter window
          */
-        public Presenter( Metadata.Pdf metadata, int screen_num, PresentationController presentation_controller ) {
-            base( screen_num );
+        public Presenter(Metadata.Pdf metadata, int screen_num,
+            PresentationController presentation_controller) {
+            base(screen_num);
+            this.role = "presenter";
 
-            this.destroy.connect( (source) => {
-                Gtk.main_quit();
-            } );
+            this.destroy.connect((source) => presentation_controller.quit());
 
             this.presentation_controller = presentation_controller;
-            this.presentation_controller.register_controllable( this );
-            
-            this.metadata = metadata;
-
-            Color.parse( "black", out this.black );
-            Color.parse( "white", out this.white );
 
-            this.modify_bg( StateType.NORMAL, this.black );
+            this.metadata = metadata;
 
             // We need the value of 90% height a lot of times. Therefore store it
             // in advance
-            var bottom_position = (int)Math.floor( this.screen_geometry.height * 0.9 );
+            var bottom_position = (int) Math.floor(this.screen_geometry.height * 0.9);
             var bottom_height = this.screen_geometry.height - bottom_position;
 
             // In most scenarios the current slide is displayed bigger than the
@@ -164,15 +137,16 @@ namespace pdfpc.Window {
             // should use as a percentage value. The maximal height is 90% of
             // the screen, as we need a place to display the timer and slide
             // count.
-            Rectangle current_scale_rect;
-            int current_allocated_width = (int)Math.floor( 
-                this.screen_geometry.width * Options.current_size / (double)100 
-            );
-            this.current_view = View.Pdf.from_metadata( 
+            Gdk.Rectangle current_scale_rect;
+            int current_allocated_width = (int) Math.floor(
+                this.screen_geometry.width * Options.current_size / (double) 100);
+            this.current_view = new View.Pdf.from_metadata(
                 metadata,
                 current_allocated_width,
-                (int)Math.floor(0.8*bottom_position),
+                (int) Math.floor(0.8 * bottom_position),
+                Metadata.Area.NOTES,
                 Options.black_on_end,
+                true,
                 this.presentation_controller,
                 out current_scale_rect
             );
@@ -182,151 +156,111 @@ namespace pdfpc.Window {
             //Requisition cv_requisition;
             //this.current_view.size_request(out cv_requisition);
             //current_allocated_width = cv_requisition.width;
-            Rectangle next_scale_rect;
-            var next_allocated_width = this.screen_geometry.width - current_allocated_width-4; // We leave a bit of margin between the two views
-            this.next_view = View.Pdf.from_metadata( 
+            Gdk.Rectangle next_scale_rect;
+            var next_allocated_width = this.screen_geometry.width - current_allocated_width - 4;
+            // We leave a bit of margin between the two views
+            this.next_view = new View.Pdf.from_metadata(
                 metadata,
                 next_allocated_width,
-                (int)Math.floor(0.7*bottom_position),
+                (int) Math.floor(0.7 * bottom_position),
+                Metadata.Area.CONTENT,
                 true,
+                false,
                 this.presentation_controller,
                 out next_scale_rect
             );
 
-            this.strict_next_view = View.Pdf.from_metadata(
+            this.strict_next_view = new View.Pdf.from_metadata(
                 metadata,
-                (int)Math.floor(0.5*current_allocated_width),
-                (int)Math.floor(0.19*bottom_position) - 2,
+                (int) Math.floor(0.5 * current_allocated_width),
+                (int) Math.floor(0.19 * bottom_position) - 2,
+                Metadata.Area.CONTENT,
                 true,
+                false,
                 this.presentation_controller,
                 out next_scale_rect
             );
-            this.strict_prev_view = View.Pdf.from_metadata(
+            this.strict_prev_view = new View.Pdf.from_metadata(
                 metadata,
-                (int)Math.floor(0.5*current_allocated_width),
-                (int)Math.floor(0.19*bottom_position) - 2,
+                (int) Math.floor(0.5 * current_allocated_width),
+                (int) Math.floor(0.19 * bottom_position) - 2,
+                Metadata.Area.CONTENT,
                 true,
+                false,
                 this.presentation_controller,
                 out next_scale_rect
             );
 
             // TextView for notes in the slides
-            var notes_font = Pango.FontDescription.from_string( "Verdana" );
-            notes_font.set_size( 
-                (int)Math.floor( 20 * 0.75 ) * Pango.SCALE
-            );
-            this.notes_view = new TextView();
+            var notes_font = Pango.FontDescription.from_string("Verdana");
+            notes_font.set_size((int) Math.floor(20 * 0.75) * Pango.SCALE);
+            this.notes_view = new Gtk.TextView();
             this.notes_view.editable = false;
             this.notes_view.cursor_visible = false;
-            this.notes_view.wrap_mode = WrapMode.WORD;
-            this.notes_view.modify_font(notes_font); 
-            this.notes_view.modify_base(StateType.NORMAL, black);
-            this.notes_view.modify_text(StateType.NORMAL, white);
+            this.notes_view.wrap_mode = Gtk.WrapMode.WORD;
+            this.notes_view.override_font(notes_font);
             this.notes_view.buffer.text = "";
-            this.notes_view.key_press_event.connect( this.on_key_press_notes_view );
+            this.notes_view.key_press_event.connect(this.on_key_press_notes_view);
 
             // Initial font needed for the labels
             // We approximate the point size using pt = px * .75
-            var font = Pango.FontDescription.from_string( "Verdana" );
-            font.set_size( 
-                (int)Math.floor( bottom_height * 0.8 * 0.75 ) * Pango.SCALE
-            );
+            var font = Pango.FontDescription.from_string("Verdana");
+            font.set_size((int) Math.floor(bottom_height * 0.8 * 0.75) * Pango.SCALE);
 
             // The countdown timer is centered in the 90% bottom part of the screen
             // It takes 3/4 of the available width
             this.timer = this.presentation_controller.getTimer();
-            this.timer.set_justify( Justification.CENTER );
-            this.timer.modify_font( font );
+            this.timer.set_justify(Gtk.Justification.CENTER);
+            this.timer.override_font(font);
 
 
             // The slide counter is centered in the 90% bottom part of the screen
             // It takes 1/4 of the available width on the right
-            this.slide_progress = new Entry();
+            this.slide_progress = new Gtk.Entry();
             this.slide_progress.set_alignment(1f);
-            this.slide_progress.modify_base(StateType.NORMAL, this.black);
-            this.slide_progress.modify_text(StateType.NORMAL, this.white);
-            this.slide_progress.modify_font( font );
-            this.slide_progress.editable = false;
+            this.slide_progress.override_font(font);
+            this.slide_progress.sensitive = false;
             this.slide_progress.has_frame = false;
-            this.slide_progress.key_press_event.connect( this.on_key_press_slide_progress );
-            this.slide_progress.inner_border = new Border();
-    
-            this.prerender_progress = new ProgressBar();
+            this.slide_progress.key_press_event.connect(this.on_key_press_slide_progress);
+
+            this.prerender_progress = new Gtk.ProgressBar();
+            this.prerender_progress.show_text = true;
             this.prerender_progress.text = "Prerendering...";
-            this.prerender_progress.modify_font( notes_font );
-            this.prerender_progress.modify_bg( StateType.NORMAL, this.black );
-            this.prerender_progress.modify_bg( StateType.PRELIGHT, this.white );
-            this.prerender_progress.modify_fg( StateType.NORMAL, this.white );
-            this.prerender_progress.modify_fg( StateType.PRELIGHT, this.black );
+            this.prerender_progress.override_font(notes_font);
             this.prerender_progress.no_show_all = true;
 
             int icon_height = bottom_height - 10;
-            try {
-                var blank_pixbuf = Rsvg.pixbuf_from_file_at_size(icon_path + "blank.svg", (int)Math.floor(1.06*icon_height), icon_height);
-                this.blank_icon = new Gtk.Image.from_pixbuf(blank_pixbuf);
-                this.blank_icon.no_show_all = true;
-            } catch (Error e) {
-                stderr.printf("Warning: Could not load icon %s (%s)\n", icon_path + "blank.svg", e.message);
-                this.blank_icon = new Gtk.Image.from_icon_name("image-missing",
-                                                                Gtk.IconSize.LARGE_TOOLBAR);
-
-            }
-            try {
-                var frozen_pixbuf = Rsvg.pixbuf_from_file_at_size(icon_path + "snow.svg", icon_height, icon_height);
-                this.frozen_icon = new Gtk.Image.from_pixbuf(frozen_pixbuf);
-                this.frozen_icon.no_show_all = true;
-            } catch (Error e) {
-                stderr.printf("Warning: Could not load icon %s (%s)\n", icon_path + "snow.svg", e.message);
-                this.frozen_icon = new Gtk.Image.from_icon_name("image-missing",
-                                                                Gtk.IconSize.LARGE_TOOLBAR);
-
-            }
-            try {
-                var pause_pixbuf = Rsvg.pixbuf_from_file_at_size(icon_path + "pause.svg", icon_height, icon_height);
-                this.pause_icon = new Gtk.Image.from_pixbuf(pause_pixbuf);
-                this.pause_icon.no_show_all = true;
-            } catch (Error e) {
-                stderr.printf("Warning: Could not load icon %s (%s)\n", icon_path + "pause.svg", e.message);
-                this.pause_icon = new Gtk.Image.from_icon_name("image-missing", Gtk.IconSize.LARGE_TOOLBAR);
-            }
 
-            this.add_events(EventMask.KEY_PRESS_MASK);
-            this.add_events(EventMask.BUTTON_PRESS_MASK);
-            this.add_events(EventMask.SCROLL_MASK);
-
-            this.key_press_event.connect( this.on_key_pressed );
-            this.button_press_event.connect( this.on_button_press );
-            this.scroll_event.connect( this.on_scroll );
+            this.blank_icon = this.load_icon("blank.svg", icon_height);
+            this.frozen_icon = this.load_icon("snow.svg", icon_height);
+            this.pause_icon = this.load_icon("pause.svg", icon_height);
+
+            this.add_events(Gdk.EventMask.KEY_PRESS_MASK);
+            this.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
+            this.add_events(Gdk.EventMask.SCROLL_MASK);
+
+            this.key_press_event.connect(this.presentation_controller.key_press);
+            this.button_press_event.connect(this.presentation_controller.button_press);
+            this.scroll_event.connect(this.presentation_controller.scroll);
 
             // Store the slide count once
             this.slide_count = metadata.get_slide_count();
 
-            this.overview = new Overview( this.metadata, this.presentation_controller, this );
-            this.overview.set_n_slides( this.presentation_controller.get_user_n_slides() );
+            this.overview = new Overview(this.metadata, this.presentation_controller, this);
+            this.overview.set_n_slides(this.presentation_controller.user_n_slides);
             this.presentation_controller.set_overview(this.overview);
+            this.presentation_controller.register_controllable(this);
 
             // Enable the render caching if it hasn't been forcefully disabled.
-            if ( !Options.disable_caching ) {               
-                ((Renderer.Caching)this.current_view.get_renderer()).set_cache( 
-                    Renderer.Cache.OptionFactory.create( 
-                        metadata
-                    )
-                );
-                ((Renderer.Caching)this.next_view.get_renderer()).set_cache( 
-                    Renderer.Cache.OptionFactory.create( 
-                        metadata
-                    )
-                );
-                ((Renderer.Caching)this.strict_next_view.get_renderer()).set_cache( 
-                    Renderer.Cache.OptionFactory.create( 
-                        metadata
-                    )
-                );
-                ((Renderer.Caching)this.strict_prev_view.get_renderer()).set_cache( 
-                    Renderer.Cache.OptionFactory.create( 
-                        metadata
-                    )
-                );
+            if (!Options.disable_caching) {
+                ((Renderer.Caching) this.current_view.get_renderer()).cache =
+                    Renderer.Cache.create(metadata);
+                ((Renderer.Caching) this.next_view.get_renderer()).cache =
+                    Renderer.Cache.create(metadata);
+                ((Renderer.Caching) this.strict_next_view.get_renderer()).cache =
+                    Renderer.Cache.create(metadata);
+                ((Renderer.Caching)this.strict_prev_view.get_renderer()).cache =
+                    Renderer.Cache.create(metadata);
             }
 
             this.build_layout();
@@ -334,140 +268,118 @@ namespace pdfpc.Window {
 
         public override void show() {
             base.show();
-            this.overview.set_available_space(this.allocation.width,
-                                              (int)Math.floor(this.allocation.height * 0.9));
+            Gtk.Allocation allocation;
+            this.get_allocation(out allocation);
+            this.overview.set_available_space(allocation.width,
+                (int) Math.floor(allocation.height * 0.9));
+        }
+
+        protected Gtk.Image load_icon(string filename, int icon_height) {
+
+            // attempt to load from a local path (if the user hasn't installed)
+            // if that fails, attempt to load from the global path
+            string load_icon_path = Path.build_filename(Paths.SOURCE_PATH, "icons", filename);
+            File icon_file = File.new_for_path(load_icon_path);
+            if (!icon_file.query_exists()) {
+                load_icon_path = Path.build_filename(Paths.ICON_PATH, filename);
+            }
+
+            Gtk.Image icon;
+            try {
+                Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file_at_size(load_icon_path,
+                    (int) Math.floor(1.06 * icon_height), icon_height);
+                icon = new Gtk.Image.from_pixbuf(pixbuf);
+                icon.no_show_all = true;
+            } catch (Error e) {
+                stderr.printf("Warning: Could not load icon %s (%s)\n", load_icon_path, e.message);
+                icon = new Gtk.Image.from_icon_name("image-missing", Gtk.IconSize.LARGE_TOOLBAR);
+            }
+            return icon;
         }
 
         protected void build_layout() {
-            this.slideViews = new HBox(false, 4);
+            Gtk.Box slide_views = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 4);
 
-            var strict_views = new HBox(false, 0);
+            var strict_views = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
             strict_views.pack_start(this.strict_prev_view, false, false, 0);
             strict_views.pack_end(this.strict_next_view, false, false, 0);
 
-            var center_current_view = new Alignment(0.5f, 0.5f, 0, 0);
-            center_current_view.add(this.current_view);
+            this.current_view.halign = Gtk.Align.CENTER;
+            this.current_view.valign = Gtk.Align.CENTER;
 
-            var current_view_and_stricts = new VBox(false, 2);
-            current_view_and_stricts.pack_start(center_current_view, false, false, 2);
+            var current_view_and_stricts = new Gtk.Box(Gtk.Orientation.VERTICAL, 2);
+            current_view_and_stricts.pack_start(current_view, false, false, 2);
             current_view_and_stricts.pack_start(strict_views, false, false, 2);
 
 
-            this.slideViews.add( current_view_and_stricts );
+            slide_views.pack_start(current_view_and_stricts, true, true, 0);
 
-            var nextViewWithNotes = new VBox(false, 0);
-            var center_next_view = new Alignment(0.5f, 0.5f, 0, 0);
-            center_next_view.add(this.next_view);
-            nextViewWithNotes.pack_start( center_next_view, false, false, 0 );
-            var notes_sw = new ScrolledWindow(null, null);
-            Scrollbar notes_scrollbar = (Gtk.Scrollbar) notes_sw.get_vscrollbar();
-            notes_scrollbar.modify_bg(StateType.NORMAL, white);
-            notes_scrollbar.modify_bg(StateType.ACTIVE, black);
-            notes_scrollbar.modify_bg(StateType.PRELIGHT, white);
-            notes_sw.add( this.notes_view );
-            notes_sw.set_policy( PolicyType.AUTOMATIC, PolicyType.AUTOMATIC );
-            nextViewWithNotes.pack_start( notes_sw, true, true, 5 );
-            this.slideViews.add(nextViewWithNotes);
-
-            var bottomRow = new HBox(true, 0);
-
-            var status = new HBox(false, 2);
-            //blank_label_alignment.add( this.blank_label );
-            status.pack_start( this.blank_icon, false, false, 0 );
-            status.pack_start( this.frozen_icon, false, false, 0 );
-            status.pack_start( this.pause_icon, false, false, 0 );
+            var nextViewWithNotes = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+            this.next_view.halign = Gtk.Align.CENTER;
+            this.next_view.valign = Gtk.Align.CENTER;
+            nextViewWithNotes.pack_start(next_view, false, false, 0);
+            var notes_sw = new Gtk.ScrolledWindow(null, null);
+            notes_sw.add(this.notes_view);
+            notes_sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+            nextViewWithNotes.pack_start(notes_sw, true, true, 5);
+            slide_views.pack_start(nextViewWithNotes, true, true, 0);
+
+            this.overview.halign = Gtk.Align.CENTER;
+            this.overview.valign = Gtk.Align.CENTER;
+
+            this.slide_stack = new Gtk.Stack();
+            this.slide_stack.add_named(slide_views, "slides");
+            this.slide_stack.add_named(this.overview, "overview");
+            this.slide_stack.homogeneous = true;
+
+            var bottom_row = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
+            bottom_row.homogeneous = true;
+
+            var status = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 2);
+            status.pack_start(this.blank_icon, false, false, 0);
+            status.pack_start(this.frozen_icon, false, false, 0);
+            status.pack_start(this.pause_icon, false, false, 0);
 
-            var timer_alignment = new Alignment(0.5f, 0.5f, 0, 0);
-            timer_alignment.add( this.timer );
+            this.timer.halign = Gtk.Align.CENTER;
+            this.timer.valign = Gtk.Align.CENTER;
 
-            var progress_alignment = new HBox(false, 0);
+            var progress_alignment = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
             progress_alignment.pack_end(this.slide_progress);
-            var prerender_alignment = new Alignment(0, 0.5f, 1, 0);
-            prerender_alignment.add(this.prerender_progress);
-            progress_alignment.pack_start(prerender_alignment);
-
-            bottomRow.pack_start( status, true, true, 0);
-            bottomRow.pack_start( timer_alignment, true, true, 0 );
-            bottomRow.pack_end( progress_alignment, true, true, 0);
-
-            //var fullLayout = new VBox(false, 0);
-            this.fullLayout = new VBox(false, 0);
-            this.fullLayout.set_size_request(this.screen_geometry.width, this.screen_geometry.height);
-            this.fullLayout.pack_start( this.slideViews, true, true, 0 );
-            this.fullLayout.pack_end( bottomRow, false, false, 0 );
-            
-            this.add( fullLayout );
+            this.prerender_progress.vexpand = false;
+            this.prerender_progress.valign = Gtk.Align.CENTER;
+            progress_alignment.pack_start(this.prerender_progress, true, true, 0);
+
+            bottom_row.pack_start(status, true, true, 0);
+            bottom_row.pack_start(this.timer, true, true, 0);
+            bottom_row.pack_end(progress_alignment, true, true, 0);
+
+            Gtk.Box full_layout = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+            full_layout.set_size_request(this.screen_geometry.width, this.screen_geometry.height);
+            full_layout.pack_start(this.slide_stack, true, true, 0);
+            full_layout.pack_end(bottom_row, false, false, 0);
 
-            this.centered_overview = new Alignment(0.5f, 0.5f, 0, 0);
-            this.centered_overview.add(this.overview);
-        }
-
-        /**
-         * Handle keypress events on the window and, if neccessary send them to the
-         * presentation controller
-         */
-        protected bool on_key_pressed( Gtk.Widget source, EventKey key ) {
-            if ( this.presentation_controller != null ) {
-                return this.presentation_controller.key_press( key );
-            } else {
-                // Can this happen?
-                return false;
-            }
-        }
-
-        /**
-         * Handle mouse button events on the window and, if neccessary send
-         * them to the presentation controller
-         */
-        protected bool on_button_press( Gtk.Widget source, EventButton button ) {
-            if ( this.presentation_controller != null ) {
-                this.presentation_controller.button_press( button );
-            }
-            return false;
-        }
-
-        /**
-         * Handle mouse scrolling events on the window and, if neccessary send
-         * them to the presentation controller
-         */
-        protected bool on_scroll( Gtk.Widget source, EventScroll scroll ) {
-            if ( this.presentation_controller != null ) {
-                this.presentation_controller.scroll( scroll );
-            }
-            return false;
+            this.add(full_layout);
         }
 
         /**
          * Update the slide count view
          */
         protected void update_slide_count() {
-            this.custom_slide_count(
-                    this.presentation_controller.get_current_user_slide_number() + 1
-            );
+            this.custom_slide_count(this.presentation_controller.current_user_slide_number + 1);
         }
 
         public void custom_slide_count(int current) {
             int total = this.presentation_controller.get_end_user_slide();
-            this.slide_progress.set_text( "%d/%u".printf(current, total) );
-        }
-
-        /**
-         * Return the registered PresentationController
-         */
-        public PresentationController? get_controller() {
-            return this.presentation_controller;
+            this.slide_progress.set_text("%d/%u".printf(current, total));
         }
 
         public void update() {
-            //if (this.overview != null) {
-            //    this.centered_overview.hide();
-            //    this.slideViews.show();
-            //}
-            int current_slide_number = this.presentation_controller.get_current_slide_number();
-            int current_user_slide_number = this.presentation_controller.get_current_user_slide_number();
+            int current_slide_number = this.presentation_controller.current_slide_number;
+            int current_user_slide_number = this.presentation_controller.current_user_slide_number;
             try {
                 this.current_view.display(current_slide_number);
-                this.next_view.display(this.metadata.user_slide_to_real_slide(current_user_slide_number + 1));
+                this.next_view.display(this.metadata.user_slide_to_real_slide(
+                    current_user_slide_number + 1));
                 if (this.presentation_controller.skip_next()) {
                     this.strict_next_view.display(current_slide_number + 1, true);
                 } else {
@@ -480,7 +392,7 @@ namespace pdfpc.Window {
                 }
             }
             catch( Renderer.RenderError e ) {
-                GLib.error( "The pdf page %d could not be rendered: %s", current_slide_number, e.message );
+                error("The pdf page %d could not be rendered: %s", current_slide_number, e.message);
             }
             this.update_slide_count();
             this.update_note();
@@ -488,11 +400,11 @@ namespace pdfpc.Window {
                 this.pause_icon.show();
             else
                 this.pause_icon.hide();
-            if (this.presentation_controller.is_faded_to_black())
+            if (this.presentation_controller.faded_to_black)
                 this.blank_icon.show();
             else
                 this.blank_icon.hide();
-            if (this.presentation_controller.is_frozen())
+            if (this.presentation_controller.frozen)
                 this.frozen_icon.show();
             else
                 this.frozen_icon.hide();
@@ -502,15 +414,12 @@ namespace pdfpc.Window {
         /**
          * Display a specific page
          */
-        public void goto_page( int page_number ) {
+        public void goto_page(int page_number) {
             try {
-                this.current_view.display( page_number );
-                this.next_view.display( 
-                    page_number + 1
-                );
-            }
-            catch( Renderer.RenderError e ) {
-                GLib.error( "The pdf page %d could not be rendered: %s", page_number, e.message );
+                this.current_view.display(page_number);
+                this.next_view.display(page_number + 1);
+            } catch( Renderer.RenderError e ) {
+                error("The pdf page %d could not be rendered: %s", page_number, e.message);
             }
 
             this.update_slide_count();
@@ -522,32 +431,30 @@ namespace pdfpc.Window {
          * Ask for the page to jump to
          */
         public void ask_goto_page() {
-           this.slide_progress.set_text("/%u".printf(this.presentation_controller.get_user_n_slides()));
-           this.slide_progress.modify_cursor(white, null);
-           this.slide_progress.editable = true;
-           this.slide_progress.grab_focus();
-           this.slide_progress.set_position(0);
-           this.presentation_controller.set_ignore_input_events( true );
+            this.slide_progress.set_text("/%u".printf(this.presentation_controller.user_n_slides));
+            this.slide_progress.sensitive = true;
+            this.slide_progress.grab_focus();
+            this.slide_progress.set_position(0);
+            this.presentation_controller.set_ignore_input_events(true);
         }
 
         /**
          * Handle key events for the slide_progress entry field
          */
-        protected bool on_key_press_slide_progress( Gtk.Widget source, EventKey key ) {
-            if ( key.keyval == 0xff0d ) {
+        protected bool on_key_press_slide_progress(Gtk.Widget source, Gdk.EventKey key) {
+            if (key.keyval == Gdk.Key.Return) {
                 // Try to parse the input
-               string input_text = this.slide_progress.text;
-               int destination = int.parse(input_text.substring(0, input_text.index_of("/")));
-               this.slide_progress.modify_cursor(black, null);
-               this.slide_progress.editable = false;
-               this.presentation_controller.set_ignore_input_events( false );
-               if ( destination != 0 )
-                  this.presentation_controller.goto_user_page(destination);
-               else
-                  this.update_slide_count(); // Reset the display we had before
-               return true;
+                string input_text = this.slide_progress.text;
+                int destination = int.parse(input_text.substring(0, input_text.index_of("/")));
+                this.slide_progress.sensitive = false;
+                this.presentation_controller.set_ignore_input_events(false);
+                if (destination != 0)
+                    this.presentation_controller.goto_user_page(destination);
+                else
+                    this.update_slide_count(); // Reset the display we had before
+                return true;
             } else {
-               return false;
+                return false;
             }
         }
 
@@ -558,73 +465,70 @@ namespace pdfpc.Window {
             this.notes_view.editable = true;
             this.notes_view.cursor_visible = true;
             this.notes_view.grab_focus();
-            this.presentation_controller.set_ignore_input_events( true );
+            this.presentation_controller.set_ignore_input_events(true);
         }
 
         /**
          * Handle key presses when editing a note
          */
-        protected bool on_key_press_notes_view( Gtk.Widget source, EventKey key ) {
-            if ( key.keyval == 0xff1b) { /* Escape */
+        protected bool on_key_press_notes_view(Gtk.Widget source, Gdk.EventKey key) {
+            if (key.keyval == Gdk.Key.Escape) { /* Escape */
                 this.notes_view.editable = false;
                 this.notes_view.cursor_visible = false;
-                this.metadata.get_notes().set_note( this.notes_view.buffer.text, this.presentation_controller.get_current_user_slide_number() );
-                this.presentation_controller.set_ignore_input_events( false );
+                this.metadata.get_notes().set_note(this.notes_view.buffer.text,
+                    this.presentation_controller.current_user_slide_number);
+                this.presentation_controller.set_ignore_input_events(false);
                 return true;
             } else {
                 return false;
             }
         }
-        
+
         /**
          * Update the text of the current note
          */
         protected void update_note() {
-            string this_note = this.metadata.get_notes().get_note_for_slide(this.presentation_controller.get_current_user_slide_number());
+            string this_note = this.metadata.get_notes().get_note_for_slide(
+                this.presentation_controller.current_user_slide_number);
             this.notes_view.buffer.text = this_note;
         }
 
         public void show_overview() {
-            this.slideViews.hide();
-            if (!overview_added) {
-                this.fullLayout.pack_start( this.centered_overview, true, true, 0 );
-                overview_added = true;
-            }
-            this.centered_overview.show();
-            this.overview.current_slide = this.presentation_controller.get_current_user_slide_number();
+            this.slide_stack.set_visible_child_name("overview");
+            this.overview.ensure_focus();
+            this.overview.current_slide = this.presentation_controller.current_user_slide_number;
         }
 
         public void hide_overview() {
-            this.centered_overview.hide();
-            this.slideViews.show();
+            this.slide_stack.set_visible_child_name("slides");
+            this.overview.ensure_structure();
         }
 
-        /** 
+        /**
          * Take a cache observer and register it with all prerendering Views
          * shown on the window.
          *
          * Furthermore it is taken care of to add the cache observer to this window
          * for display, as it is a Image widget after all.
          */
-        public void set_cache_observer( CacheStatus observer ) {
+        public void set_cache_observer(CacheStatus observer) {
             var current_prerendering_view = this.current_view as View.Prerendering;
-            if( current_prerendering_view != null ) {
-                observer.monitor_view( current_prerendering_view );
+            if (current_prerendering_view != null) {
+                observer.monitor_view(current_prerendering_view);
             }
             var next_prerendering_view = this.next_view as View.Prerendering;
-            if( next_prerendering_view != null ) {
-                observer.monitor_view( next_prerendering_view );
+            if (next_prerendering_view != null) {
+                observer.monitor_view(next_prerendering_view);
             }
-            
-            //observer.register_entry( this.slide_progress );
-            //observer.register_update( this.prerender_progress.set_fraction, () => this.prerender_progress.hide() );
-            observer.register_update( this.prerender_progress.set_fraction, this.prerender_finished );
+
+            observer.update_progress.connect(this.prerender_progress.set_fraction);
+            observer.update_complete.connect(this.prerender_finished);
             this.prerender_progress.show();
         }
 
         public void prerender_finished() {
-            this.prerender_progress.hide();
-            this.overview.set_cache(((Renderer.Caching)this.next_view.get_renderer()).get_cache());
+            this.prerender_progress.opacity = 0;  // hide() causes a flash for re-layout.
+            this.overview.set_cache(((Renderer.Caching) this.next_view.get_renderer()).cache);
         }
     }
 }
--- pdf-presenter-console-3.1.1.orig/src/interfaces/controllable.vala
+++ pdf-presenter-console-3.1.1/src/interfaces/controllable.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -25,17 +25,16 @@ namespace pdfpc {
      * Every window or object which wants to be controlled by the
      * PresentationController needs to implement this interface.
      */
-    public interface Controllable: GLib.Object {
+    public interface Controllable : GLib.Object {
         /**
-         * Set the presentation controller which needs to be informed of key
-         * presses and such.
+         * The registered PresentationController
          */
-        //public abstract void set_controller( PresentationController controller ) ;
+        public abstract PresentationController presentation_controller { get; protected set; }
 
         /**
-         * Return the registered PresentationController
+         * The view on which links and annotations should be handled.
          */
-        public abstract PresentationController? get_controller();
+        public abstract View.Pdf main_view { get; }
 
         /**
          * Update the display
@@ -45,21 +44,22 @@ namespace pdfpc {
         /**
          * Edit note for current slide
          */
-        public abstract void edit_note();
+        public virtual void edit_note() {}
 
         /**
          * Ask for the page to jump to
          */
-        public abstract void ask_goto_page();
+        public virtual void ask_goto_page() {}
 
         /**
          * Show an overview of all slides
          */
-        public abstract void show_overview();
+        public virtual void show_overview() {}
 
         /**
          * Hide the overview
          */
-        public abstract void hide_overview();
+        public virtual void hide_overview() {}
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/interfaces/renderer/caching.vala
+++ pdf-presenter-console-3.1.1/src/interfaces/renderer/caching.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -25,17 +25,11 @@ namespace pdfpc.Renderer {
      * Every renderer may decide to implement the Caching interface to improve
      * rendering speed.
      */
-    public interface Caching: GLib.Object {
+    public interface Caching : GLib.Object {
         /**
-         * Set a Cache store to be used for caching
+         * A Cache store to be used for caching
          */
-        public abstract void set_cache( Cache.Base cache );
-
-        /**
-         * Retrieve the currently used cache store
-         *
-         * If no cache store is set null will be returned.
-         */
-        public abstract Cache.Base get_cache();
+        public abstract Cache.Base? cache { get; set; }
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/interfaces/view/behaviour/decoratable.vala
+++ pdf-presenter-console-3.1.1/src/interfaces/view/behaviour/decoratable.vala
@@ -4,26 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using GLib;
-
-using pdfpc;
-
 namespace pdfpc.View {
     /**
      * Every View which supports Behaviours needs to implement this interface.
@@ -38,6 +34,7 @@ namespace pdfpc.View {
          * The implementation needs to support an arbitrary amount of different
          * behaviours.
          */
-        public abstract void associate_behaviour( Behaviour.Base behaviour );
+        public abstract void associate_behaviour(Behaviour.Base behaviour);
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/interfaces/view/prerendering.vala
+++ pdf-presenter-console-3.1.1/src/interfaces/view/prerendering.vala
@@ -4,17 +4,17 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -27,7 +27,7 @@ namespace pdfpc.View {
      * view, which does not implement the Renderer.Caching interface the
      * prerender functionallity can't be used.
      */
-    public interface Prerendering: GLib.Object {
+    public interface Prerendering : GLib.Object {
         /**
          * Signal emitted every time a precached slide has been created
          *
@@ -47,3 +47,4 @@ namespace pdfpc.View {
         public signal void prerendering_started();
     }
 }
+
--- pdf-presenter-console-3.1.1.orig/src/paths.in
+++ pdf-presenter-console-3.1.1/src/paths.in
@@ -1,2 +1,6 @@
-const string icon_path = "@CMAKE_INSTALL_PREFIX@/share/pixmaps/pdfpc/";
-const string etc_path = "@SYSCONFDIR@";
+namespace pdfpc.Paths {
+    public const string ICON_PATH = "@CMAKE_INSTALL_PREFIX@/share/pixmaps/pdfpc/";
+    public const string SOURCE_PATH = "@CMAKE_SOURCE_DIR@";
+    public const string CONF_PATH = "@SYSCONFDIR@";
+}
+
--- pdf-presenter-console-3.1.1.orig/src/pdfpc.vala
+++ pdf-presenter-console-3.1.1/src/pdfpc.vala
@@ -4,24 +4,22 @@
  * This file is part of pdfpc.
  *
  * Copyright (C) 2010-2011 Jakob Westhoff <jakob@westhoffswelt.de>
- * 
+ *
  * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-using Gtk;
-
 namespace pdfpc {
     /**
      * Pdf Presenter Console main application class
@@ -74,6 +72,8 @@ namespace pdfpc {
             { "single-screen", 'S', 0, 0, ref Options.single_screen, "Force to use only one screen", null },
             { "list-actions", 'L', 0, 0, ref Options.list_actions, "List actions supported in the config file(s)", null},
             { "windowed", 'w', 0, 0, ref Options.windowed, "Run in windowed mode (devel tool)", null},
+            { "size", 'Z', 0, OptionArg.STRING, ref Options.size, "Size of the presenter console in width:height format (forces windowed mode)", null},
+            { "notes", 'n', 0, OptionArg.STRING, ref Options.notes_position, "Position of notes on the pdf page (either left, right, top or bottom)", "P"},
             { null }
         };
 
@@ -81,26 +81,68 @@ namespace pdfpc {
          * Parse the commandline and apply all found options to there according
          * static class members.
          *
-		 * Returns the name of the pdf file to open (or null if not present)
+         * Returns the name of the pdf file to open (or null if not present)
          */
-        protected string? parse_command_line_options( string[] args ) {
+        protected string? parse_command_line_options( ref unowned string[] args ) {
             var context = new OptionContext( "<pdf-file>" );
 
             context.add_main_entries( options, null );
-            
+
             try {
                 context.parse( ref args );
             }
             catch( OptionError e ) {
-                stderr.printf( "\n%s\n\n", e.message );
-                stderr.printf( "%s", context.get_help( true, null ) );
+                warning("\n%s\n\n", e.message);
+                warning("%s", context.get_help( true, null ));
                 Posix.exit( 1 );
             }
             if ( args.length < 2 ) {
-				return null;
+                return null;
             } else {
-				return args[1];
-			}
+                return args[1];
+            }
+        }
+
+        /**
+         * Set the CSS styling for GTK.
+         */
+        private void set_styling() {
+            Gtk.CssProvider provider = new Gtk.CssProvider();
+            Gtk.StyleContext.add_provider_for_screen(Gdk.Display.get_default().get_default_screen(),
+                provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+            string css = """
+                * {
+                    background-color: black;
+                    background-image: none;
+                    color: white;
+                }
+                /* .slider and .trough are parts of scrollbar */
+                .slider, .progressbar {
+                    border: none;
+                    background-color: white;
+                }
+                GtkProgressBar {
+                    color: gray;
+                }
+                .trough {
+                    border: none;
+                }
+                pdfpcTimerLabel.pretalk {
+                    color: green;
+                }
+                pdfpcTimerLabel.last-minutes {
+                    color: orange;
+                }
+                pdfpcTimerLabel.overtime {
+                    color: red;
+                }
+            """;
+
+            try {
+                provider.load_from_data(css, -1);
+            } catch (Error error) {
+                warning("Could not load styling from data: %s", error.message);
+            }
         }
 
         /**
@@ -119,8 +161,8 @@ namespace pdfpc {
          * Create and return a PresentationWindow using the specified monitor
          * while displaying the given file
          */
-        private Window.Presentation create_presentation_window( Metadata.Pdf metadata, int monitor ) {
-            var presentation_window = new Window.Presentation( metadata, monitor, this.controller );
+        private Window.Presentation create_presentation_window( Metadata.Pdf metadata, int monitor, int width = -1, int height = -1 ) {
+            var presentation_window = new Window.Presentation( metadata, monitor, this.controller, width, height );
             //controller.register_controllable( presentation_window );
             presentation_window.set_cache_observer( this.cache_status );
 
@@ -136,43 +178,71 @@ namespace pdfpc {
                            + "(C) 2012 David Vilar\n"
                            + "(C) 2009-2011 Jakob Westhoff\n\n" );
 
-            Gdk.threads_init();
             Gtk.init( ref args );
 
-            string pdfFilename = this.parse_command_line_options( args );
+            string pdfFilename = this.parse_command_line_options( ref args );
+
+            Gst.init( ref args );
+
             if (Options.list_actions) {
-				stdout.printf("Config file commands accepted by pdfpc:\n");
-				string[] actions = PresentationController.getActionDescriptions();
-				for (int i = 0; i < actions.length; i+=2) {
-					string tabAlignment = "\t";
-					if (actions[i].length < 8)
-						tabAlignment += "\t";
-					stdout.printf("\t%s%s=> %s\n", actions[i], tabAlignment, actions[i+1]);
-				}
+                stdout.printf("Config file commands accepted by pdfpc:\n");
+                string[] actions = PresentationController.getActionDescriptions();
+                for (int i = 0; i < actions.length; i+=2) {
+                    string tabAlignment = "\t";
+                    if (actions[i].length < 8)
+                        tabAlignment += "\t";
+                    stdout.printf("\t%s%s=> %s\n", actions[i], tabAlignment, actions[i+1]);
+                }
                 return;
             }
-			if (pdfFilename == null) {
-				stderr.printf( "Error: No pdf file given\n");
-				Posix.exit(1);
-			}
+            if (pdfFilename == null) {
+                warning("Error: No pdf file given\n");
+                Posix.exit(1);
+            } else if (!GLib.FileUtils.test(pdfFilename, (GLib.FileTest.IS_REGULAR))) {
+                warning("Error: pdf file not found\n");
+                Posix.exit(1);
+            }
+
+            // parse size option
+            // should be in the width:height format
+
+            int width = -1, height = -1;
+            if (Options.size != null) {
+                int colonIndex = Options.size.index_of(":");
+
+                width = int.parse(Options.size.substring(0, colonIndex));
+                height = int.parse(Options.size.substring(colonIndex + 1));
+
+                if (width < 1 || height < 1) {
+                    warning("Error: Failed to parse size\n");
+                    Posix.exit(1);
+
+                }
 
-            // Initialize the application wide mutex objects
-            MutexLocks.init();
+                Options.windowed = true;
+            }
 
             stdout.printf( "Initializing rendering...\n" );
 
-            var metadata = new Metadata.Pdf( pdfFilename );
+            pdfpc.Metadata.NotesPosition notes_position = pdfpc.Metadata.NotesPosition.from_string(Options.notes_position);
+            var metadata = new Metadata.Pdf( pdfFilename, notes_position );
             if ( Options.duration != 987654321u )
                 metadata.set_duration(Options.duration);
 
+
             // Initialize global controller and CacheStatus, to manage
             // crosscutting concerns between the different windows.
             this.controller = new PresentationController( metadata, Options.black_on_end );
             this.cache_status = new CacheStatus();
 
             ConfigFileReader configFileReader = new ConfigFileReader(this.controller);
-            configFileReader.readConfig(etc_path + "/pdfpcrc");
-            configFileReader.readConfig(Environment.get_home_dir() + "/.pdfpcrc");
+
+            configFileReader.readConfig(Path.build_filename(Paths.SOURCE_PATH, "rc/pdfpcrc"));
+            configFileReader.readConfig(Path.build_filename(Paths.CONF_PATH, "pdfpcrc"));
+            configFileReader.readConfig(Path.build_filename(Environment.get_home_dir(),
+                ".pdfpcrc"));
+
+            set_styling();
 
             var screen = Gdk.Screen.get_default();
             if ( !Options.windowed && !Options.single_screen && screen.get_n_monitors() > 1 ) {
@@ -182,22 +252,22 @@ namespace pdfpc {
                 else
                     presenter_monitor    = (screen.get_primary_monitor() + 1) % 2;
                 presentation_monitor = (presenter_monitor + 1) % 2;
-                this.presentation_window = 
-                    this.create_presentation_window( metadata, presentation_monitor );
-                this.presenter_window = 
+                this.presenter_window =
                     this.create_presenter_window( metadata, presenter_monitor );
+                this.presentation_window =
+                    this.create_presentation_window( metadata, presentation_monitor, width, height );
             } else if (Options.windowed && !Options.single_screen) {
                 this.presenter_window =
                     this.create_presenter_window( metadata, -1 );
                 this.presentation_window =
-                    this.create_presentation_window( metadata, -1 );
+                    this.create_presentation_window( metadata, -1, width, height );
             } else {
                     if ( !Options.display_switch)
                         this.presenter_window =
                             this.create_presenter_window( metadata, -1 );
                     else
                         this.presentation_window =
-                            this.create_presentation_window( metadata, -1 );
+                            this.create_presentation_window( metadata, -1, width, height );
             }
 
             // The windows are always displayed at last to be sure all caches have
@@ -206,7 +276,7 @@ namespace pdfpc {
                 this.presentation_window.show_all();
                 this.presentation_window.update();
             }
-            
+
             if ( this.presenter_window != null ) {
                 this.presenter_window.show_all();
                 this.presenter_window.update();
@@ -214,9 +284,7 @@ namespace pdfpc {
 
             // Enter the Glib eventloop
             // Everything from this point on is completely signal based
-            Gdk.threads_enter();
             Gtk.main();
-            Gdk.threads_leave();
         }
 
         /**
