=== modified file 'include/server/mir/graphics/display.h'
--- include/server/mir/graphics/display.h	2013-03-13 04:54:15 +0000
+++ include/server/mir/graphics/display.h	2013-04-10 12:56:24 +0000
@@ -40,6 +40,9 @@
 
     virtual std::shared_ptr<DisplayConfiguration> configuration() = 0;
 
+    virtual void pause() = 0;
+    virtual void resume() = 0;
+
 protected:
     Display() = default;
     ~Display() = default;

=== modified file 'include/server/mir/graphics/platform.h'
--- include/server/mir/graphics/platform.h	2013-03-21 03:32:59 +0000
+++ include/server/mir/graphics/platform.h	2013-04-10 12:56:24 +0000
@@ -21,10 +21,13 @@
 #define MIR_GRAPHICS_PLATFORM_H_
 
 #include <memory>
-
+#include <functional>
 
 namespace mir
 {
+
+class MainLoop;
+
 namespace compositor
 {
 class GraphicBufferAllocator;
@@ -53,6 +56,14 @@
         const std::shared_ptr<BufferInitializer>& buffer_initializer) = 0;
     virtual std::shared_ptr<Display> create_display() = 0;
     virtual std::shared_ptr<PlatformIPCPackage> get_ipc_package() = 0;
+
+    virtual void register_pause_resume_handlers(
+        MainLoop& main_loop,
+        std::function<void()> const& pause_handler,
+        std::function<void()> const& resume_handler) = 0;
+
+    virtual void pause() = 0;
+    virtual void resume() = 0;
 };
 
 // Create and return a new graphics platform.

=== modified file 'include/server/mir/server_configuration.h'
--- include/server/mir/server_configuration.h	2013-04-08 16:20:33 +0000
+++ include/server/mir/server_configuration.h	2013-04-10 12:56:24 +0000
@@ -33,6 +33,7 @@
 }
 namespace graphics
 {
+class Platform;
 class Display;
 }
 namespace input
@@ -48,6 +49,7 @@
 public:
     virtual std::shared_ptr<frontend::Communicator> the_communicator() = 0;
     virtual std::shared_ptr<frontend::Shell> the_frontend_shell() = 0;
+    virtual std::shared_ptr<graphics::Platform> the_graphics_platform() = 0;
     virtual std::shared_ptr<graphics::Display> the_display() = 0;
     virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0;
     virtual std::shared_ptr<input::InputManager> the_input_manager() = 0;

=== modified file 'include/test/mir_test_doubles/mock_display.h'
--- include/test/mir_test_doubles/mock_display.h	2013-03-13 04:54:15 +0000
+++ include/test/mir_test_doubles/mock_display.h	2013-04-10 12:56:24 +0000
@@ -35,6 +35,8 @@
     MOCK_CONST_METHOD0(view_area, geometry::Rectangle ());
     MOCK_METHOD1(for_each_display_buffer, void (std::function<void(graphics::DisplayBuffer&)> const&));
     MOCK_METHOD0(configuration, std::shared_ptr<graphics::DisplayConfiguration>());
+    MOCK_METHOD0(pause, void());
+    MOCK_METHOD0(resume, void());
 };
 
 }

=== modified file 'include/test/mir_test_doubles/null_display.h'
--- include/test/mir_test_doubles/null_display.h	2013-03-13 04:54:15 +0000
+++ include/test/mir_test_doubles/null_display.h	2013-04-10 12:56:24 +0000
@@ -42,6 +42,8 @@
     {
         return std::shared_ptr<graphics::DisplayConfiguration>();
     }
+    void pause() {}
+    void resume() {}
 };
 
 }

=== added file 'include/test/mir_test_doubles/null_virtual_terminal.h'
--- include/test/mir_test_doubles/null_virtual_terminal.h	1970-01-01 00:00:00 +0000
+++ include/test/mir_test_doubles/null_virtual_terminal.h	2013-04-10 12:56:24 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
+ */
+
+#ifndef MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_
+#define MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_
+
+#include "src/server/graphics/gbm/virtual_terminal.h"
+
+namespace mir
+{
+namespace test
+{
+namespace doubles
+{
+
+class NullVirtualTerminal : public graphics::gbm::VirtualTerminal
+{
+public:
+    void set_graphics_mode() {}
+
+    void register_switch_handlers(MainLoop&,
+                                  std::function<void()> const&,
+                                  std::function<void()> const&)
+    {
+    }
+};
+
+}
+}
+}
+
+#endif /* MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_ */

=== modified file 'src/server/display_server.cpp'
--- src/server/display_server.cpp	2013-04-08 16:20:33 +0000
+++ src/server/display_server.cpp	2013-04-10 12:56:24 +0000
@@ -25,6 +25,7 @@
 #include "mir/compositor/compositor.h"
 #include "mir/frontend/shell.h"
 #include "mir/frontend/communicator.h"
+#include "mir/graphics/platform.h"
 #include "mir/graphics/display.h"
 #include "mir/input/input_manager.h"
 
@@ -36,15 +37,31 @@
 struct mir::DisplayServer::Private
 {
     Private(ServerConfiguration& config)
-        : display{config.the_display()},
+        : graphics_platform{config.the_graphics_platform()},
+          display{config.the_display()},
           compositor{config.the_compositor()},
           shell{config.the_frontend_shell()},
           communicator{config.the_communicator()},
           input_manager{config.the_input_manager()},
           main_loop{config.the_main_loop()}
     {
+        graphics_platform->register_pause_resume_handlers(
+            *main_loop,
+            [this]
+            {
+                compositor->stop();
+                display->pause();
+                graphics_platform->pause();
+            },
+            [this]
+            {
+                graphics_platform->resume();
+                display->resume();
+                compositor->start();
+            });
     }
 
+    std::shared_ptr<mg::Platform> graphics_platform;
     std::shared_ptr<mg::Display> display;
     std::shared_ptr<mc::Compositor> compositor;
     std::shared_ptr<frontend::Shell> shell;

=== modified file 'src/server/graphics/android/android_display.cpp'
--- src/server/graphics/android/android_display.cpp	2013-03-21 03:32:59 +0000
+++ src/server/graphics/android/android_display.cpp	2013-04-10 12:56:24 +0000
@@ -142,6 +142,14 @@
     return std::make_shared<NullDisplayConfiguration>();
 }
 
+void mga::AndroidDisplay::pause()
+{
+}
+
+void mga::AndroidDisplay::resume()
+{
+}
+
 void mga::AndroidDisplay::make_current()
 {
     if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)

=== modified file 'src/server/graphics/android/android_display.h'
--- src/server/graphics/android/android_display.h	2013-03-22 18:33:51 +0000
+++ src/server/graphics/android/android_display.h	2013-04-10 12:56:24 +0000
@@ -50,6 +50,9 @@
 
     std::shared_ptr<DisplayConfiguration> configuration();
 
+    void pause();
+    void resume();
+
     void make_current();
 private:
     std::shared_ptr<AndroidFramebufferWindowQuery> native_window;

=== modified file 'src/server/graphics/android/android_platform.cpp'
--- src/server/graphics/android/android_platform.cpp	2013-04-02 15:06:31 +0000
+++ src/server/graphics/android/android_platform.cpp	2013-04-10 12:56:24 +0000
@@ -52,3 +52,18 @@
 {
     return std::make_shared<mga::AndroidPlatform>();
 }
+
+void mga::AndroidPlatform::register_pause_resume_handlers(
+    MainLoop&,
+    std::function<void()> const&,
+    std::function<void()> const&)
+{
+}
+
+void mga::AndroidPlatform::pause()
+{
+}
+
+void mga::AndroidPlatform::resume()
+{
+}

=== modified file 'src/server/graphics/android/android_platform.h'
--- src/server/graphics/android/android_platform.h	2013-03-22 17:08:01 +0000
+++ src/server/graphics/android/android_platform.h	2013-04-10 12:56:24 +0000
@@ -36,6 +36,12 @@
             const std::shared_ptr<BufferInitializer>& buffer_initializer);
     std::shared_ptr<Display> create_display();
     std::shared_ptr<PlatformIPCPackage> get_ipc_package();
+    void register_pause_resume_handlers(
+        MainLoop& main_loop,
+        std::function<void()> const& pause_handler,
+        std::function<void()> const& resume_handler);
+    void pause();
+    void resume();
 };
 
 }

=== modified file 'src/server/graphics/gbm/CMakeLists.txt'
--- src/server/graphics/gbm/CMakeLists.txt	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/CMakeLists.txt	2013-04-10 12:56:24 +0000
@@ -24,6 +24,7 @@
   kms_output.cpp
   kms_output_container.cpp
   kms_page_flipper.cpp
+  linux_virtual_terminal.cpp
 )
 
 target_link_libraries(

=== modified file 'src/server/graphics/gbm/gbm_display.cpp'
--- src/server/graphics/gbm/gbm_display.cpp	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/gbm_display.cpp	2013-04-10 12:56:24 +0000
@@ -43,6 +43,10 @@
     shared_egl.make_current();
 }
 
+mgg::GBMDisplay::~GBMDisplay()
+{
+}
+
 geom::Rectangle mgg::GBMDisplay::view_area() const
 {
     return display_buffers[0]->view_area();
@@ -92,8 +96,18 @@
                                                         max_size.height.as_uint32_t());
 
     /* Create a single DisplayBuffer that displays the surface on all the outputs */
-    std::unique_ptr<DisplayBuffer> db{new GBMDisplayBuffer{platform, listener, enabled_outputs,
-                                                           std::move(surface), max_size,
-                                                           shared_egl.context()}};
+    std::unique_ptr<GBMDisplayBuffer> db{new GBMDisplayBuffer{platform, listener, enabled_outputs,
+                                                              std::move(surface), max_size,
+                                                              shared_egl.context()}};
     display_buffers.push_back(std::move(db));
 }
+
+void mgg::GBMDisplay::pause()
+{
+}
+
+void mgg::GBMDisplay::resume()
+{
+    for (auto& db_ptr : display_buffers)
+        db_ptr->schedule_set_crtc();
+}

=== modified file 'src/server/graphics/gbm/gbm_display.h'
--- src/server/graphics/gbm/gbm_display.h	2013-03-21 03:32:59 +0000
+++ src/server/graphics/gbm/gbm_display.h	2013-04-10 12:56:24 +0000
@@ -20,7 +20,6 @@
 #define MIR_GRAPHICS_GBM_GBM_DISPLAY_H_
 
 #include "mir/graphics/display.h"
-#include "mir/graphics/display_buffer.h"
 #include "kms_output_container.h"
 #include "gbm_display_helpers.h"
 
@@ -36,31 +35,37 @@
 {
 
 class DisplayReport;
+class DisplayBuffer;
 
 namespace gbm
 {
 
 class GBMPlatform;
 class KMSOutput;
+class GBMDisplayBuffer;
 
 class GBMDisplay : public Display
 {
 public:
     GBMDisplay(std::shared_ptr<GBMPlatform> const& platform,
                std::shared_ptr<DisplayReport> const& listener);
+    ~GBMDisplay();
 
     geometry::Rectangle view_area() const;
     void for_each_display_buffer(std::function<void(DisplayBuffer&)> const& f);
 
     std::shared_ptr<DisplayConfiguration> configuration();
 
+    void pause();
+    void resume();
+
 private:
     void configure(std::shared_ptr<DisplayConfiguration> const& conf);
 
     std::shared_ptr<GBMPlatform> const platform;
     std::shared_ptr<DisplayReport> const listener;
     helpers::EGLHelper shared_egl;
-    std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;
+    std::vector<std::unique_ptr<GBMDisplayBuffer>> display_buffers;
     KMSOutputContainer output_container;
 };
 

=== modified file 'src/server/graphics/gbm/gbm_display_buffer.cpp'
--- src/server/graphics/gbm/gbm_display_buffer.cpp	2013-03-21 03:32:59 +0000
+++ src/server/graphics/gbm/gbm_display_buffer.cpp	2013-04-10 12:56:24 +0000
@@ -105,7 +105,8 @@
       drm(platform->drm),
       outputs(outputs),
       surface_gbm{std::move(surface_gbm_param)},
-      size(size)
+      size(size),
+      needs_set_crtc{false}
 {
     egl.setup(platform->gbm, surface_gbm.get(), shared_context);
 
@@ -180,11 +181,20 @@
      * If the flip fails, release the buffer object to make it available
      * for future rendering.
      */
-    if (!schedule_and_wait_for_page_flip(bufobj))
+    if (!needs_set_crtc && !schedule_and_wait_for_page_flip(bufobj))
     {
         bufobj->release();
         return false;
     }
+    else if (needs_set_crtc)
+    {
+        for (auto& output : outputs)
+        {
+            if (!output->set_crtc(bufobj->get_drm_fb_id()))
+                BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));
+        }
+        needs_set_crtc = false;
+    }
 
     /*
      * Release the last flipped buffer object (which is not displayed anymore)
@@ -265,3 +275,8 @@
         BOOST_THROW_EXCEPTION(std::runtime_error("Failed to make EGL surface current"));
     }
 }
+
+void mgg::GBMDisplayBuffer::schedule_set_crtc()
+{
+    needs_set_crtc = true;
+}

=== modified file 'src/server/graphics/gbm/gbm_display_buffer.h'
--- src/server/graphics/gbm/gbm_display_buffer.h	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/gbm_display_buffer.h	2013-04-10 12:56:24 +0000
@@ -24,6 +24,7 @@
 
 #include <vector>
 #include <memory>
+#include <atomic>
 
 namespace mir
 {
@@ -55,6 +56,8 @@
     void clear();
     bool post_update();
 
+    void schedule_set_crtc();
+
 private:
     BufferObject* get_front_buffer_object();
     bool schedule_and_wait_for_page_flip(BufferObject* bufobj);
@@ -68,6 +71,7 @@
     GBMSurfaceUPtr surface_gbm;
     helpers::EGLHelper egl;
     geometry::Size size;
+    std::atomic<bool> needs_set_crtc;
 };
 
 }

=== modified file 'src/server/graphics/gbm/gbm_display_helpers.cpp'
--- src/server/graphics/gbm/gbm_display_helpers.cpp	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/gbm_display_helpers.cpp	2013-04-10 12:56:24 +0000
@@ -96,6 +96,47 @@
     }
 }
 
+void mggh::DRMHelper::drop_master() const
+{
+    /* We must have our own device fd first, so that it has become the DRM master */
+    if (fd < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            std::runtime_error("Tried to drop DRM master without a DRM device"));
+    }
+
+    int ret = drmDropMaster(fd);
+
+    if (ret < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to drop DRM master"))
+                    << boost::errinfo_errno(ret));
+    }
+}
+
+void mggh::DRMHelper::set_master() const
+{
+    /* We must have our own device fd first, so that it has become the DRM master */
+    if (fd < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            std::runtime_error("Tried to set DRM master without a DRM device"));
+    }
+
+    int ret = drmSetMaster(fd);
+
+    if (ret < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to set DRM master"))
+                    << boost::errinfo_errno(ret));
+    }
+}
+
+
 int mggh::DRMHelper::open_drm_device()
 {
     static const char *drivers[] = {

=== modified file 'src/server/graphics/gbm/gbm_display_helpers.h'
--- src/server/graphics/gbm/gbm_display_helpers.h	2013-03-21 03:32:59 +0000
+++ src/server/graphics/gbm/gbm_display_helpers.h	2013-04-10 12:56:24 +0000
@@ -57,6 +57,9 @@
     int get_authenticated_fd();
     void auth_magic(drm_magic_t magic) const;
 
+    void drop_master() const;
+    void set_master() const;
+
     int fd;
 
 private:

=== modified file 'src/server/graphics/gbm/gbm_platform.cpp'
--- src/server/graphics/gbm/gbm_platform.cpp	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/gbm_platform.cpp	2013-04-10 12:56:24 +0000
@@ -21,6 +21,7 @@
 #include <boost/throw_exception.hpp>
 #include "gbm_buffer_allocator.h"
 #include "gbm_display.h"
+#include "linux_virtual_terminal.h"
 #include "mir/graphics/platform_ipc_package.h"
 #include "mir/logging/logger.h"
 #include "mir/logging/dumb_console_logger.h"
@@ -52,9 +53,12 @@
 
 }
 
-mgg::GBMPlatform::GBMPlatform(std::shared_ptr<DisplayReport> const& listener) :
-    listener(listener)
+mgg::GBMPlatform::GBMPlatform(std::shared_ptr<DisplayReport> const& listener,
+                              std::shared_ptr<VirtualTerminal> const& vt)
+    : listener{listener},
+      vt{vt}
 {
+    vt->set_graphics_mode();
     drm.setup();
     gbm.setup(drm);
 }
@@ -78,6 +82,24 @@
     return std::make_shared<GBMPlatformIPCPackage>(drm.get_authenticated_fd());
 }
 
+void mgg::GBMPlatform::register_pause_resume_handlers(
+    MainLoop& main_loop,
+    std::function<void()> const& pause_handler,
+    std::function<void()> const& resume_handler)
+{
+    vt->register_switch_handlers(main_loop, pause_handler, resume_handler);
+}
+
+void mgg::GBMPlatform::pause()
+{
+    drm.drop_master();
+}
+
+void mgg::GBMPlatform::resume()
+{
+    drm.set_master();
+}
+
 void mgg::GBMPlatform::drm_auth_magic(drm_magic_t magic)
 {
     drm.auth_magic(magic);
@@ -85,5 +107,7 @@
 
 std::shared_ptr<mg::Platform> mg::create_platform(std::shared_ptr<DisplayReport> const& report)
 {
-    return std::make_shared<mgg::GBMPlatform>(report);
+    return std::make_shared<mgg::GBMPlatform>(
+        report,
+        std::make_shared<mgg::LinuxVirtualTerminal>());
 }

=== modified file 'src/server/graphics/gbm/gbm_platform.h'
--- src/server/graphics/gbm/gbm_platform.h	2013-03-13 04:54:15 +0000
+++ src/server/graphics/gbm/gbm_platform.h	2013-04-10 12:56:24 +0000
@@ -30,18 +30,27 @@
 namespace gbm
 {
 
+class VirtualTerminal;
+
 class GBMPlatform : public Platform,
                     public DRMAuthenticator,
                     public std::enable_shared_from_this<GBMPlatform>
 {
 public:
-    explicit GBMPlatform(std::shared_ptr<DisplayReport> const& reporter);
+    explicit GBMPlatform(std::shared_ptr<DisplayReport> const& reporter,
+                         std::shared_ptr<VirtualTerminal> const& vt);
 
     /* From Platform */
     std::shared_ptr<compositor::GraphicBufferAllocator> create_buffer_allocator(
             const std::shared_ptr<BufferInitializer>& buffer_initializer);
     std::shared_ptr<Display> create_display();
     std::shared_ptr<PlatformIPCPackage> get_ipc_package();
+    void register_pause_resume_handlers(
+        MainLoop& main_loop,
+        std::function<void()> const& pause_handler,
+        std::function<void()> const& resume_handler);
+    void pause();
+    void resume();
 
     /* From DRMAuthenticator */
     void drm_auth_magic(drm_magic_t magic);
@@ -49,7 +58,8 @@
     helpers::DRMHelper drm;
     helpers::GBMHelper gbm;
 
-    std::shared_ptr<DisplayReport> listener;
+    std::shared_ptr<DisplayReport> const listener;
+    std::shared_ptr<VirtualTerminal> const vt;
 };
 
 }

=== added file 'src/server/graphics/gbm/linux_virtual_terminal.cpp'
--- src/server/graphics/gbm/linux_virtual_terminal.cpp	1970-01-01 00:00:00 +0000
+++ src/server/graphics/gbm/linux_virtual_terminal.cpp	2013-04-10 12:56:24 +0000
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
+ */
+
+#include "linux_virtual_terminal.h"
+#include "mir/main_loop.h"
+
+#include <boost/throw_exception.hpp>
+#include <boost/exception/errinfo_errno.hpp>
+#include <boost/exception/errinfo_file_name.hpp>
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+#include <csignal>
+
+#include <fcntl.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <sys/ioctl.h>
+
+namespace mgg = mir::graphics::gbm;
+
+namespace
+{
+
+int find_active_vt_number()
+{
+    static std::vector<std::string> const paths{"/dev/tty", "/dev/tty0"};
+    int active_vt{-1};
+
+    for (auto& p : paths)
+    {
+        auto fd = open(p.c_str(), O_RDONLY, 0);
+        if (fd < 0)
+            fd = open(p.c_str(), O_WRONLY, 0);
+
+        if (fd >= 0)
+        {
+            struct vt_stat vts;
+            auto status = ioctl(fd, VT_GETSTATE, &vts);
+            close(fd);
+
+            if (status >= 0)
+            {
+                active_vt = vts.v_active;
+                break;
+            }
+        }
+    }
+
+    if (active_vt < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            std::runtime_error("Failed to find the current VT"));
+    }
+
+    return active_vt;
+}
+
+int open_vt(int vt_number)
+{
+    std::stringstream vt_path_stream;
+    vt_path_stream << "/dev/tty" << vt_number;
+
+    std::string const active_vt_path{vt_path_stream.str()};
+
+    auto vt_fd = open(active_vt_path.c_str(), O_RDONLY | O_NDELAY, 0);
+
+    if (vt_fd < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to open current VT"))
+                    << boost::errinfo_file_name(active_vt_path)
+                    << boost::errinfo_errno(errno));
+    }
+
+    return vt_fd;
+}
+
+}
+
+mgg::LinuxVirtualTerminal::LinuxVirtualTerminal()
+    : vt_fd{open_vt(find_active_vt_number())},
+      prev_kd_mode{0},
+      prev_vt_mode(),
+      active{true}
+{
+    if (ioctl(vt_fd.fd(), KDGETMODE, &prev_kd_mode) < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to get current VT mode"))
+                    << boost::errinfo_errno(errno));
+    }
+
+    if (ioctl(vt_fd.fd(), VT_GETMODE, &prev_vt_mode) < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to get the current VT")) << boost::errinfo_errno(errno));
+    }
+}
+
+mgg::LinuxVirtualTerminal::~LinuxVirtualTerminal() noexcept(true)
+{
+    if (vt_fd.fd() > 0)
+    {
+        ioctl(vt_fd.fd(), KDSETMODE, prev_kd_mode);
+        ioctl(vt_fd.fd(), VT_SETMODE, &prev_vt_mode);
+    }
+}
+
+void mgg::LinuxVirtualTerminal::set_graphics_mode()
+{
+    if (ioctl(vt_fd.fd(), KDSETMODE, KD_GRAPHICS) < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to set VT to graphics mode"))
+                    << boost::errinfo_errno(errno));
+    }
+}
+
+void mgg::LinuxVirtualTerminal::register_switch_handlers(
+    MainLoop& main_loop,
+    std::function<void()> const& switch_away,
+    std::function<void()> const& switch_back)
+{
+    main_loop.register_signal_handler(
+        {SIGUSR1},
+        [this, switch_away, switch_back](int)
+        {
+            active = !active;
+            if (active)
+            {
+                static int const allow_switch{2};
+                switch_back();
+                ioctl(vt_fd.fd(), VT_RELDISP, allow_switch);
+            }
+            else
+            {
+                switch_away();
+                ioctl(vt_fd.fd(), VT_RELDISP, VT_ACKACQ);
+            }
+        });
+
+    struct vt_mode vtm
+    {
+        VT_PROCESS,
+        0,
+        SIGUSR1,
+        SIGUSR1,
+        0
+    };
+
+    if (ioctl(vt_fd.fd(), VT_SETMODE, &vtm) < 0)
+    {
+        BOOST_THROW_EXCEPTION(
+            boost::enable_error_info(
+                std::runtime_error("Failed to set the current VT mode"))
+                    << boost::errinfo_errno(errno));
+    }
+}

=== added file 'src/server/graphics/gbm/linux_virtual_terminal.h'
--- src/server/graphics/gbm/linux_virtual_terminal.h	1970-01-01 00:00:00 +0000
+++ src/server/graphics/gbm/linux_virtual_terminal.h	2013-04-10 12:56:24 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
+ */
+
+#ifndef MIR_GRAPHICS_GBM_LINUX_VIRTUAL_TERMINAL_H_
+#define MIR_GRAPHICS_GBM_LINUX_VIRTUAL_TERMINAL_H_
+
+#include "virtual_terminal.h"
+
+#include <linux/vt.h>
+#include <unistd.h>
+
+namespace mir
+{
+namespace graphics
+{
+namespace gbm
+{
+
+class LinuxVirtualTerminal : public VirtualTerminal
+{
+public:
+    LinuxVirtualTerminal();
+    ~LinuxVirtualTerminal() noexcept(true);
+
+    void set_graphics_mode();
+    void register_switch_handlers(
+        MainLoop& main_loop,
+        std::function<void()> const& switch_away,
+        std::function<void()> const& switch_back);
+
+private:
+    class FDWrapper
+    {
+    public:
+        FDWrapper(int fd) : fd_{fd} {}
+        ~FDWrapper() { if (fd_ >= 0) close(fd_); }
+        int fd() const { return fd_; }
+    private: 
+        int const fd_;
+    };
+    
+    FDWrapper const vt_fd;
+    int prev_kd_mode;
+    struct vt_mode prev_vt_mode;
+    bool active;
+};
+
+}
+}
+}
+
+#endif /* MIR_GRAPHICS_GBM_LINUX_VIRTUAL_TERMINAL_H_ */

=== added file 'src/server/graphics/gbm/virtual_terminal.h'
--- src/server/graphics/gbm/virtual_terminal.h	1970-01-01 00:00:00 +0000
+++ src/server/graphics/gbm/virtual_terminal.h	2013-04-10 12:56:24 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
+ */
+
+#ifndef MIR_GRAPHICS_GBM_VIRTUAL_TERMINAL_H_
+#define MIR_GRAPHICS_GBM_VIRTUAL_TERMINAL_H_
+
+#include <functional>
+
+namespace mir
+{
+
+class MainLoop;
+
+namespace graphics
+{
+namespace gbm
+{
+
+class VirtualTerminal
+{
+public:
+    virtual ~VirtualTerminal() = default;
+
+    virtual void set_graphics_mode() = 0;
+    virtual void register_switch_handlers(
+        MainLoop& main_loop,
+        std::function<void()> const& switch_away,
+        std::function<void()> const& switch_back) = 0;
+
+protected:
+    VirtualTerminal() = default;
+    VirtualTerminal(VirtualTerminal const&) = delete;
+    VirtualTerminal& operator=(VirtualTerminal const&) = delete;
+};
+
+}
+}
+}
+
+#endif /* MIR_GRAPHICS_GBM_VIRTUAL_TERMINAL_H_ */

=== modified file 'tests/behavior-tests/session_management_context.cpp'
--- tests/behavior-tests/session_management_context.cpp	2013-03-29 16:51:35 +0000
+++ tests/behavior-tests/session_management_context.cpp	2013-04-10 12:56:24 +0000
@@ -105,6 +105,9 @@
         return std::shared_ptr<mg::DisplayConfiguration>();
     }
 
+    void pause() {}
+    void resume() {}
+
     geom::Rectangle area;
 };
 

=== modified file 'tests/integration-tests/cucumber/test_session_management_context.cpp'
--- tests/integration-tests/cucumber/test_session_management_context.cpp	2013-04-08 16:20:33 +0000
+++ tests/integration-tests/cucumber/test_session_management_context.cpp	2013-04-10 12:56:24 +0000
@@ -53,6 +53,7 @@
     MOCK_METHOD0(the_display, std::shared_ptr<mg::Display>());
     MOCK_METHOD0(the_compositor, std::shared_ptr<mc::Compositor>());
     MOCK_METHOD0(the_main_loop, std::shared_ptr<mir::MainLoop>());
+    MOCK_METHOD0(the_graphics_platform, std::shared_ptr<mg::Platform>());
 };
 
 MATCHER_P(NamedWindowWithNoGeometry, name, "")

=== modified file 'tests/integration-tests/graphics/gbm/test_buffer_integration.cpp'
--- tests/integration-tests/graphics/gbm/test_buffer_integration.cpp	2013-03-13 08:09:52 +0000
+++ tests/integration-tests/graphics/gbm/test_buffer_integration.cpp	2013-04-10 12:56:24 +0000
@@ -99,6 +99,15 @@
     {
         return std::shared_ptr<mg::PlatformIPCPackage>();
     }
+
+    virtual void register_pause_resume_handlers(mir::MainLoop&,
+                                                std::function<void()> const&,
+                                                std::function<void()> const&)
+    {
+    }
+
+    virtual void pause() {}
+    virtual void resume() {}
 };
 
 class GBMBufferIntegration : public ::testing::Test

=== modified file 'tests/integration-tests/test_display_info.cpp'
--- tests/integration-tests/test_display_info.cpp	2013-03-22 10:34:16 +0000
+++ tests/integration-tests/test_display_info.cpp	2013-04-10 12:56:24 +0000
@@ -104,6 +104,14 @@
         return std::make_shared<mg::PlatformIPCPackage>();
     }
 
+    void register_pause_resume_handlers(mir::MainLoop&,
+                                        std::function<void()> const&,
+                                        std::function<void()> const&)
+    {
+    }
+
+    void pause() {}
+    void resume() {}
 };
 
 void connection_callback(MirConnection* connection, void* context)

=== modified file 'tests/integration-tests/test_display_server_main_loop_events.cpp'
--- tests/integration-tests/test_display_server_main_loop_events.cpp	2013-04-08 16:20:33 +0000
+++ tests/integration-tests/test_display_server_main_loop_events.cpp	2013-04-10 12:56:24 +0000
@@ -17,6 +17,9 @@
  */
 
 #include "mir/compositor/compositor.h"
+#include "mir/graphics/platform.h"
+#include "mir/graphics/display.h"
+#include "mir/main_loop.h"
 
 #include "mir_test_framework/testing_server_configuration.h"
 #include "mir_test_doubles/mock_input_manager.h"
@@ -30,6 +33,7 @@
 
 namespace mi = mir::input;
 namespace mc = mir::compositor;
+namespace mg = mir::graphics;
 namespace mtd = mir::test::doubles;
 namespace mtf = mir_test_framework;
 
@@ -43,6 +47,85 @@
     MOCK_METHOD0(stop, void());
 };
 
+class MockGraphicsPlatform : public mg::Platform
+{
+public:
+    MockGraphicsPlatform(std::shared_ptr<mg::Platform> const& platform,
+                         int pause_signal, int resume_signal)
+        : platform{platform},
+          pause_signal{pause_signal},
+          resume_signal{resume_signal}
+    {
+    }
+
+    std::shared_ptr<mc::GraphicBufferAllocator> create_buffer_allocator(
+        std::shared_ptr<mg::BufferInitializer> const& buffer_initializer)
+    {
+        return platform->create_buffer_allocator(buffer_initializer);
+    }
+
+    std::shared_ptr<mg::Display> create_display()
+    {
+        return platform->create_display();
+    }
+
+    std::shared_ptr<mg::PlatformIPCPackage> get_ipc_package()
+    {
+        return platform->get_ipc_package();
+    }
+
+    void register_pause_resume_handlers(
+        mir::MainLoop& main_loop,
+        std::function<void()> const& pause_handler,
+        std::function<void()> const& resume_handler)
+    {
+        main_loop.register_signal_handler(
+            {pause_signal},
+            [pause_handler](int) { pause_handler(); });
+        main_loop.register_signal_handler(
+            {resume_signal},
+            [resume_handler](int) { resume_handler(); });
+    }
+
+    MOCK_METHOD0(pause, void());
+    MOCK_METHOD0(resume, void());
+
+private:
+    std::shared_ptr<mg::Platform> const platform;
+    int const pause_signal;
+    int const resume_signal;
+};
+
+class MockDisplay : public mg::Display
+{
+public:
+    MockDisplay(std::shared_ptr<mg::Display> const& display)
+        : display{display}
+    {
+    }
+
+    mir::geometry::Rectangle view_area() const
+    {
+        return display->view_area();
+    }
+
+    void for_each_display_buffer(std::function<void(mg::DisplayBuffer&)> const& f)
+    {
+        display->for_each_display_buffer(f);
+    }
+
+    std::shared_ptr<mg::DisplayConfiguration> configuration()
+    {
+        return display->configuration();
+    }
+
+    MOCK_METHOD0(pause, void());
+    MOCK_METHOD0(resume, void());
+
+private:
+    std::shared_ptr<mg::Display> const display;
+};
+
 class ServerConfig : public mtf::TestingServerConfiguration
 {
 public:
@@ -77,6 +160,84 @@
     std::shared_ptr<MockCompositor> mock_compositor;
 };
 
+
+class PauseResumeServerConfig : public mtf::TestingServerConfiguration
+{
+public:
+    PauseResumeServerConfig()
+        : pause_signal{SIGUSR1}, resume_signal{SIGUSR2}
+    {
+    }
+
+    std::shared_ptr<mg::Platform> the_graphics_platform() override
+    {
+        if (!mock_graphics_platform)
+        {
+            auto graphics_platform = mtf::TestingServerConfiguration::the_graphics_platform();
+            mock_graphics_platform =
+                std::make_shared<MockGraphicsPlatform>(graphics_platform,
+                                                       pause_signal,
+                                                       resume_signal);
+        }
+
+        return mock_graphics_platform;
+    }
+
+    std::shared_ptr<mg::Display> the_display() override
+    {
+        if (!mock_display)
+        {
+            auto display = mtf::TestingServerConfiguration::the_display();
+            mock_display = std::make_shared<MockDisplay>(display);
+        }
+
+        return mock_display;
+    }
+
+    std::shared_ptr<mc::Compositor> the_compositor() override
+    {
+        if (!mock_compositor)
+            mock_compositor = std::make_shared<MockCompositor>();
+
+        return mock_compositor;
+    }
+
+    std::shared_ptr<MockGraphicsPlatform> the_mock_graphics_platform()
+    {
+        the_graphics_platform();
+        return mock_graphics_platform;
+    }
+
+    std::shared_ptr<MockDisplay> the_mock_display()
+    {
+        the_display();
+        return mock_display;
+    }
+
+    std::shared_ptr<MockCompositor> the_mock_compositor()
+    {
+        the_compositor();
+        return mock_compositor;
+    }
+
+    void emit_pause_event()
+    {
+        kill(getpid(), pause_signal);
+    }
+
+    void emit_resume_event()
+    {
+        kill(getpid(), resume_signal);
+    }
+private:
+    std::shared_ptr<MockGraphicsPlatform> mock_graphics_platform;
+    std::shared_ptr<MockCompositor> mock_compositor;
+    std::shared_ptr<MockDisplay> mock_display;
+
+    int const pause_signal;
+    int const resume_signal;
+};
+
 }
 
 TEST(DisplayServerMainLoopEvents, display_server_shuts_down_properly_on_sigint)
@@ -100,3 +261,75 @@
                     kill(getpid(), SIGTERM);
                  });
 }
+
+TEST(DisplayServerMainLoopEvents, display_server_components_pause_and_resume)
+{
+    using namespace testing;
+
+    PauseResumeServerConfig server_config;
+
+    auto mock_compositor = server_config.the_mock_compositor();
+    auto mock_display = server_config.the_mock_display();
+    auto mock_graphics_platform = server_config.the_mock_graphics_platform();
+
+    {
+        InSequence s;
+
+        /* Start */
+        EXPECT_CALL(*mock_compositor, start()).Times(1);
+
+        /* Pause */
+        EXPECT_CALL(*mock_compositor, stop()).Times(1);
+        EXPECT_CALL(*mock_display, pause()).Times(1);
+        EXPECT_CALL(*mock_graphics_platform, pause()).Times(1);
+
+        /* Resume */
+        EXPECT_CALL(*mock_graphics_platform, resume()).Times(1);
+        EXPECT_CALL(*mock_display, resume()).Times(1);
+        EXPECT_CALL(*mock_compositor, start()).Times(1);
+
+        /* Stop */
+        EXPECT_CALL(*mock_compositor, stop()).Times(1);
+    }
+
+    mir::run_mir(server_config,
+                 [&server_config](mir::DisplayServer&)
+                 {
+                    server_config.emit_pause_event();
+                    server_config.emit_resume_event();
+                    kill(getpid(), SIGTERM);
+                 });
+}
+
+TEST(DisplayServerMainLoopEvents, display_server_quits_when_paused)
+{
+    using namespace testing;
+
+    PauseResumeServerConfig server_config;
+
+    auto mock_compositor = server_config.the_mock_compositor();
+    auto mock_display = server_config.the_mock_display();
+    auto mock_graphics_platform = server_config.the_mock_graphics_platform();
+
+    {
+        InSequence s;
+
+        /* Start */
+        EXPECT_CALL(*mock_compositor, start()).Times(1);
+
+        /* Pause */
+        EXPECT_CALL(*mock_compositor, stop()).Times(1);
+        EXPECT_CALL(*mock_display, pause()).Times(1);
+        EXPECT_CALL(*mock_graphics_platform, pause()).Times(1);
+
+        /* Stop */
+        EXPECT_CALL(*mock_compositor, stop()).Times(1);
+    }
+
+    mir::run_mir(server_config,
+                 [&server_config](mir::DisplayServer&)
+                 {
+                    server_config.emit_pause_event();
+                    kill(getpid(), SIGTERM);
+                 });
+}

=== modified file 'tests/integration-tests/test_drm_auth_magic.cpp'
--- tests/integration-tests/test_drm_auth_magic.cpp	2013-03-22 10:34:16 +0000
+++ tests/integration-tests/test_drm_auth_magic.cpp	2013-04-10 12:56:24 +0000
@@ -80,6 +80,15 @@
         return std::make_shared<mg::PlatformIPCPackage>();
     }
 
+    void register_pause_resume_handlers(mir::MainLoop&,
+                                        std::function<void()> const&,
+                                        std::function<void()> const&)
+    {
+    }
+
+    void pause() {}
+    void resume() {}
+
     MOCK_METHOD1(drm_auth_magic, void(unsigned int));
 };
 

=== modified file 'tests/integration-tests/test_surfaceloop.cpp'
--- tests/integration-tests/test_surfaceloop.cpp	2013-04-08 04:03:40 +0000
+++ tests/integration-tests/test_surfaceloop.cpp	2013-04-10 12:56:24 +0000
@@ -133,6 +133,8 @@
         auto null_configuration = std::shared_ptr<mg::DisplayConfiguration>();
         return null_configuration;
     }
+    void pause() {}
+    void resume() {}
 };
 
 struct SurfaceSync
@@ -346,6 +348,15 @@
         {
             return std::make_shared<mg::PlatformIPCPackage>();
         }
+
+        void register_pause_resume_handlers(mir::MainLoop&,
+                                            std::function<void()> const&,
+                                            std::function<void()> const&)
+        {
+        }
+
+        void pause() {}
+        void resume() {}
     };
 
     std::shared_ptr<mg::Platform> the_graphics_platform()
@@ -473,6 +484,15 @@
         {
             return std::make_shared<mg::PlatformIPCPackage>();
         }
+
+        void register_pause_resume_handlers(mir::MainLoop&,
+                                            std::function<void()> const&,
+                                            std::function<void()> const&)
+        {
+        }
+
+        void pause() {}
+        void resume() {}
     };
 
     std::shared_ptr<mg::Platform> the_graphics_platform()

=== modified file 'tests/mir_test_framework/testing_server_options.cpp'
--- tests/mir_test_framework/testing_server_options.cpp	2013-03-29 16:51:35 +0000
+++ tests/mir_test_framework/testing_server_options.cpp	2013-04-10 12:56:24 +0000
@@ -86,6 +86,8 @@
         auto null_configuration = std::shared_ptr<mg::DisplayConfiguration>();
         return null_configuration;
     }
+    void pause() {}
+    void resume() {}
 };
 
 class StubGraphicPlatform : public mg::Platform
@@ -105,6 +107,16 @@
     {
         return std::make_shared<mg::PlatformIPCPackage>();
     }
+
+    virtual void register_pause_resume_handlers(
+        mir::MainLoop&,
+        std::function<void()> const&,
+        std::function<void()> const&)
+    {
+    }
+
+    virtual void pause() {}
+    virtual void resume() {}
 };
 
 class StubRenderer : public mg::Renderer

=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp	2013-03-27 15:12:09 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp	2013-04-10 12:56:24 +0000
@@ -54,6 +54,9 @@
         return std::shared_ptr<mg::DisplayConfiguration>();
     }
 
+    void pause() {}
+    void resume() {}
+
 private:
     std::vector<mtd::NullDisplayBuffer> buffers;
 };

=== modified file 'tests/unit-tests/frontend/test_session_mediator.cpp'
--- tests/unit-tests/frontend/test_session_mediator.cpp	2013-03-29 16:51:35 +0000
+++ tests/unit-tests/frontend/test_session_mediator.cpp	2013-04-10 12:56:24 +0000
@@ -121,6 +121,15 @@
     {
         return std::make_shared<mg::PlatformIPCPackage>();
     }
+
+    void register_pause_resume_handlers(mir::MainLoop&,
+                                        std::function<void()> const&,
+                                        std::function<void()> const&)
+    {
+    }
+
+    void pause() {}
+    void resume() {}
 };
 
 }

=== modified file 'tests/unit-tests/frontend/test_session_mediator_android.cpp'
--- tests/unit-tests/frontend/test_session_mediator_android.cpp	2013-03-21 03:32:59 +0000
+++ tests/unit-tests/frontend/test_session_mediator_android.cpp	2013-04-10 12:56:24 +0000
@@ -78,6 +78,15 @@
     {
         return std::make_shared<mg::PlatformIPCPackage>();
     }
+
+    void register_pause_resume_handlers(mir::MainLoop&,
+                                        std::function<void()> const&,
+                                        std::function<void()> const&)
+    {
+    }
+
+    void pause() {}
+    void resume() {}
 };
 
 }

=== modified file 'tests/unit-tests/frontend/test_session_mediator_gbm.cpp'
--- tests/unit-tests/frontend/test_session_mediator_gbm.cpp	2013-03-21 03:32:59 +0000
+++ tests/unit-tests/frontend/test_session_mediator_gbm.cpp	2013-04-10 12:56:24 +0000
@@ -82,6 +82,15 @@
         return std::make_shared<mg::PlatformIPCPackage>();
     }
 
+    void register_pause_resume_handlers(mir::MainLoop&,
+                                        std::function<void()> const&,
+                                        std::function<void()> const&)
+    {
+    }
+
+    void pause() {}
+    void resume() {}
+
     MOCK_METHOD1(drm_auth_magic, void(drm_magic_t));
 };
 

=== modified file 'tests/unit-tests/graphics/gbm/mock_drm.cpp'
--- tests/unit-tests/graphics/gbm/mock_drm.cpp	2013-02-05 16:18:10 +0000
+++ tests/unit-tests/graphics/gbm/mock_drm.cpp	2013-04-10 12:56:24 +0000
@@ -341,3 +341,13 @@
 {
     return global_mock->drmAuthMagic(fd, magic);
 }
+
+int drmSetMaster(int fd)
+{
+    return global_mock->drmSetMaster(fd);
+}
+
+int drmDropMaster(int fd)
+{
+    return global_mock->drmDropMaster(fd);
+}

=== modified file 'tests/unit-tests/graphics/gbm/mock_drm.h'
--- tests/unit-tests/graphics/gbm/mock_drm.h	2013-02-05 16:18:10 +0000
+++ tests/unit-tests/graphics/gbm/mock_drm.h	2013-04-10 12:56:24 +0000
@@ -114,6 +114,9 @@
     MOCK_METHOD2(drmGetMagic, int(int fd, drm_magic_t *magic));
     MOCK_METHOD2(drmAuthMagic, int(int fd, drm_magic_t magic));
 
+    MOCK_METHOD1(drmSetMaster, int(int fd));
+    MOCK_METHOD1(drmDropMaster, int(int fd));
+
     FakeDRMResources fake_drm;
 };
 

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp	2013-03-13 08:09:52 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp	2013-04-10 12:56:24 +0000
@@ -27,6 +27,7 @@
 #include "mir/graphics/buffer_initializer.h"
 #include "mir/compositor/buffer_ipc_package.h"
 #include "mir/compositor/buffer_properties.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
 
 #include "mir/graphics/null_display_report.h"
 
@@ -42,6 +43,7 @@
 namespace mg=mir::graphics;
 namespace mgg=mir::graphics::gbm;
 namespace geom=mir::geometry;
+namespace mtd=mir::test::doubles;
 
 class GBMGraphicBufferBasic : public ::testing::Test
 {
@@ -77,7 +79,8 @@
         ON_CALL(mock_egl, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES")))
             .WillByDefault(Return(reinterpret_cast<func_ptr_t>(glEGLImageTargetTexture2DOES)));
 
-        platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>(),
+                                                      std::make_shared<mtd::NullVirtualTerminal>());
         null_init = std::make_shared<mg::NullBufferInitializer>();
         allocator.reset(new mgg::GBMBufferAllocator(platform, null_init));
     }

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_buffer_allocator.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_buffer_allocator.cpp	2013-03-13 08:09:52 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_buffer_allocator.cpp	2013-04-10 12:56:24 +0000
@@ -27,6 +27,7 @@
 #include "mir_test/egl_mock.h"
 #include "mir_test/gl_mock.h"
 #include "mir_test_doubles/mock_buffer_initializer.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
 #include "mir/graphics/null_display_report.h"
 
 #include <memory>
@@ -67,7 +68,8 @@
         ON_CALL(mock_egl, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES")))
             .WillByDefault(Return(reinterpret_cast<func_ptr_t>(glEGLImageTargetTexture2DOES)));
 
-        platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>(),
+                                                      std::make_shared<mtd::NullVirtualTerminal>());
         mock_buffer_initializer = std::make_shared<testing::NiceMock<mtd::MockBufferInitializer>>();
         allocator.reset(new mgg::GBMBufferAllocator(platform, mock_buffer_initializer));
     }

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_display.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_display.cpp	2013-03-22 11:37:34 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_display.cpp	2013-04-10 12:56:24 +0000
@@ -20,11 +20,13 @@
 #include "src/server/graphics/gbm/gbm_display.h"
 #include "mir/logging/display_report.h"
 #include "mir/logging/logger.h"
+#include "mir/graphics/display_buffer.h"
 
 #include "mir_test/egl_mock.h"
 #include "mir_test/gl_mock.h"
 #include "mir/graphics/null_display_report.h"
 #include "mir_test_doubles/mock_display_report.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
 
 #include "mock_drm.h"
 #include "mock_gbm.h"
@@ -78,6 +80,12 @@
         .Times(AtLeast(0));
     }
 
+    std::shared_ptr<mgg::GBMPlatform> create_platform()
+    {
+        return std::make_shared<mgg::GBMPlatform>(
+            std::make_shared<mg::NullDisplayReport>(),
+            std::make_shared<mtd::NullVirtualTerminal>());
+    }
 
     void setup_post_update_expectations()
     {
@@ -232,7 +240,7 @@
 
     EXPECT_NO_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     });
 }
@@ -273,7 +281,7 @@
 
     EXPECT_NO_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     });
 }
@@ -291,7 +299,7 @@
 
     EXPECT_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     }, std::runtime_error);
 }
@@ -310,7 +318,7 @@
     EXPECT_CALL(mock_drm, drmClose(_))
         .Times(Exactly(1));
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
 
     EXPECT_THROW({
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
@@ -332,8 +340,8 @@
         .Times(Exactly(1));
 
     EXPECT_THROW({
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
-    }, std::runtime_error) << "Expected c'tor of GBMDisplay to throw an exception";
+        auto platform = create_platform();
+    }, std::runtime_error) << "Expected c'tor of GBMPlatform to throw an exception";
 }
 
 namespace
@@ -394,7 +402,7 @@
 
     EXPECT_NO_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
 
         display->for_each_display_buffer([](mg::DisplayBuffer& db)
@@ -434,7 +442,7 @@
 
     EXPECT_NO_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
 
         display->for_each_display_buffer([](mg::DisplayBuffer& db)
@@ -469,7 +477,7 @@
 
     EXPECT_NO_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     });
 }
@@ -478,7 +486,7 @@
 {
     using namespace ::testing;
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto logger = std::make_shared<MockLogger>();
 
     auto reporter = std::make_shared<ml::DisplayReport>(logger);
@@ -497,7 +505,7 @@
 {
     using namespace ::testing;
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto logger = std::make_shared<MockLogger>();
 
     auto reporter = std::make_shared<ml::DisplayReport>(logger);
@@ -516,7 +524,7 @@
 {
     using namespace ::testing;
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto logger = std::make_shared<MockLogger>();
 
     auto reporter = std::make_shared<ml::DisplayReport>(logger);
@@ -535,7 +543,7 @@
 {
     using namespace ::testing;
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto logger = std::make_shared<MockLogger>();
 
     auto reporter = std::make_shared<ml::DisplayReport>(logger);
@@ -561,7 +569,7 @@
 
     EXPECT_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     }, std::runtime_error);
 }
@@ -577,7 +585,7 @@
 
     EXPECT_THROW(
     {
-        auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
     }, std::runtime_error);
 }
@@ -586,7 +594,7 @@
 {
     using namespace ::testing;
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto display = std::make_shared<mgg::GBMDisplay>(platform, mock_report);
 
     int callback_count{0};

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_display_configuration.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_display_configuration.cpp	2013-03-13 08:09:52 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_display_configuration.cpp	2013-04-10 12:56:24 +0000
@@ -25,6 +25,7 @@
 #include "mir_test/egl_mock.h"
 #include "mir_test/gl_mock.h"
 #include "mir/graphics/null_display_report.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
 
 #include "mock_drm.h"
 #include "mock_gbm.h"
@@ -37,6 +38,7 @@
 namespace mg = mir::graphics;
 namespace mgg = mir::graphics::gbm;
 namespace geom = mir::geometry;
+namespace mtd = mir::test::doubles;
 
 namespace
 {
@@ -59,8 +61,7 @@
 class GBMDisplayConfigurationTest : public ::testing::Test
 {
 public:
-    GBMDisplayConfigurationTest() :
-        null_listener{std::make_shared<mg::NullDisplayReport>()}
+    GBMDisplayConfigurationTest()
     {
         using namespace testing;
 
@@ -81,6 +82,13 @@
         setup_sample_modes();
     }
 
+    std::shared_ptr<mgg::GBMPlatform> create_platform()
+    {
+        return std::make_shared<mgg::GBMPlatform>(
+            std::make_shared<mg::NullDisplayReport>(),
+            std::make_shared<mtd::NullVirtualTerminal>());
+    }
+
     void setup_sample_modes()
     {
         /* Add DRM modes */
@@ -98,7 +106,6 @@
     ::testing::NiceMock<mir::GLMock> mock_gl;
     ::testing::NiceMock<mgg::MockDRM> mock_drm;
     ::testing::NiceMock<mgg::MockGBM> mock_gbm;
-    std::shared_ptr<mg::DisplayReport> const null_listener;
 
     std::vector<drmModeModeInfo> modes0;
     std::vector<mg::DisplayConfigurationMode> conf_modes0;
@@ -176,7 +183,7 @@
     };
 
     /* Test body */
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 
     auto conf = display->configuration();
@@ -214,7 +221,7 @@
     resources.prepare();
 
     /* Test body */
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 
     auto conf = display->configuration();
@@ -253,7 +260,7 @@
     resources.prepare();
 
     /* Test body */
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 
     auto conf = display->configuration();

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_display_multi_monitor.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_display_multi_monitor.cpp	2013-03-13 08:09:52 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_display_multi_monitor.cpp	2013-04-10 12:56:24 +0000
@@ -23,6 +23,7 @@
 #include "mir_test/egl_mock.h"
 #include "mir_test/gl_mock.h"
 #include "mir/graphics/null_display_report.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
 
 #include "mock_drm.h"
 #include "mock_gbm.h"
@@ -33,6 +34,7 @@
 namespace mg = mir::graphics;
 namespace mgg = mir::graphics::gbm;
 namespace geom = mir::geometry;
+namespace mtd = mir::test::doubles;
 
 namespace
 {
@@ -41,7 +43,6 @@
 {
 public:
     GBMDisplayMultiMonitorTest()
-        : null_listener{std::make_shared<mg::NullDisplayReport>()}
     {
         using namespace testing;
 
@@ -69,6 +70,13 @@
             .Times(AtLeast(0));
     }
 
+    std::shared_ptr<mgg::GBMPlatform> create_platform()
+    {
+        return std::make_shared<mgg::GBMPlatform>(
+            std::make_shared<mg::NullDisplayReport>(),
+            std::make_shared<mtd::NullVirtualTerminal>());
+    }
+
     void setup_outputs(int n)
     {
         mgg::FakeDRMResources& resources(mock_drm.fake_drm);
@@ -117,7 +125,6 @@
     testing::NiceMock<mir::GLMock> mock_gl;
     testing::NiceMock<mgg::MockDRM> mock_drm;
     testing::NiceMock<mgg::MockGBM> mock_gbm;
-    std::shared_ptr<mg::DisplayReport> const null_listener;
 
     std::vector<drmModeModeInfo> modes0;
     std::vector<drmModeModeInfo> modes_empty;
@@ -168,7 +175,7 @@
             .After(crtc_setups);
     }
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 }
 
@@ -201,7 +208,7 @@
             .Times(1);
     }
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 }
 
@@ -254,7 +261,7 @@
         .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0)))
         .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0)));
 
-    auto platform = std::make_shared<mgg::GBMPlatform>(null_listener);
+    auto platform = create_platform();
     auto display = platform->create_display();
 
     display->for_each_display_buffer([](mg::DisplayBuffer& buffer)

=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_platform.cpp'
--- tests/unit-tests/graphics/gbm/test_gbm_platform.cpp	2013-03-13 04:54:15 +0000
+++ tests/unit-tests/graphics/gbm/test_gbm_platform.cpp	2013-04-10 12:56:24 +0000
@@ -16,9 +16,11 @@
  * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
  */
 
-#include "mir/graphics/platform.h"
 #include "mir/graphics/platform_ipc_package.h"
 #include "mir/graphics/drm_authenticator.h"
+#include "src/server/graphics/gbm/gbm_platform.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
+#include "mir/main_loop.h"
 
 #include "mir/graphics/null_display_report.h"
 
@@ -28,13 +30,30 @@
 #include "mock_gbm.h"
 
 #include <gtest/gtest.h>
+#include <gmock/gmock.h>
 
 #include <stdexcept>
 
 namespace mg = mir::graphics;
+namespace mgg = mir::graphics::gbm;
+namespace mtd = mir::test::doubles;
 
 namespace
 {
+
+class MockVirtualTerminal : public mgg::VirtualTerminal
+{
+public:
+    ~MockVirtualTerminal() noexcept(true) {}
+
+    MOCK_METHOD0(set_graphics_mode, void());
+    MOCK_METHOD3(register_switch_handlers,
+                 void(mir::MainLoop&,
+                      std::function<void()> const&,
+                      std::function<void()> const&));
+};
+
+
 class GBMGraphicsPlatform : public ::testing::Test
 {
 public:
@@ -43,6 +62,14 @@
         ::testing::Mock::VerifyAndClearExpectations(&mock_drm);
         ::testing::Mock::VerifyAndClearExpectations(&mock_gbm);
     }
+
+    std::shared_ptr<mg::Platform> create_platform()
+    {
+        return std::make_shared<mgg::GBMPlatform>(
+            std::make_shared<mg::NullDisplayReport>(),
+            std::make_shared<mtd::NullVirtualTerminal>());
+    }
+
     ::testing::NiceMock<mg::gbm::MockDRM> mock_drm;
     ::testing::NiceMock<mg::gbm::MockGBM> mock_gbm;
 };
@@ -69,7 +96,7 @@
     EXPECT_CALL(mock_drm, drmClose(auth_fd));
 
     EXPECT_NO_THROW (
-        auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto pkg = platform->get_ipc_package();
 
         ASSERT_TRUE(pkg.get());
@@ -87,7 +114,7 @@
 
     try
     {
-        auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
     } catch(...)
     {
         return;
@@ -105,7 +132,7 @@
     EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(),magic))
         .WillOnce(Return(0));
 
-    auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto authenticator = std::dynamic_pointer_cast<mg::DRMAuthenticator>(platform);
     authenticator->drm_auth_magic(magic);
 }
@@ -119,10 +146,68 @@
     EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(),magic))
         .WillOnce(Return(-1));
 
-    auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto authenticator = std::dynamic_pointer_cast<mg::DRMAuthenticator>(platform);
 
     EXPECT_THROW({
         authenticator->drm_auth_magic(magic);
     }, std::runtime_error);
 }
+
+TEST_F(GBMGraphicsPlatform, sets_vt_graphics_mode)
+{
+    using namespace testing;
+
+    auto mock_vt = std::make_shared<MockVirtualTerminal>();
+
+    EXPECT_CALL(*mock_vt, set_graphics_mode())
+        .Times(1);
+
+    mgg::GBMPlatform platform{std::make_shared<mg::NullDisplayReport>(),
+                              mock_vt};
+}
+
+TEST_F(GBMGraphicsPlatform, pause_drops_drm_master)
+{
+    using namespace testing;
+
+    EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd()))
+        .Times(1);
+
+    auto platform = create_platform();
+    platform->pause();
+}
+
+TEST_F(GBMGraphicsPlatform, resume_sets_drm_master)
+{
+    using namespace testing;
+
+    InSequence s;
+
+    EXPECT_CALL(mock_drm, drmSetMaster(mock_drm.fake_drm.fd()))
+        .Times(1);
+
+    auto platform = create_platform();
+    platform->resume();
+}
+
+TEST_F(GBMGraphicsPlatform, set_or_drop_drm_master_failure_throws)
+{
+    using namespace testing;
+
+    EXPECT_CALL(mock_drm, drmDropMaster(_))
+        .WillOnce(Return(-1));
+
+    EXPECT_CALL(mock_drm, drmSetMaster(_))
+        .WillOnce(Return(-1));
+
+    auto platform = create_platform();
+
+    EXPECT_THROW({
+        platform->pause();
+    }, std::runtime_error);
+
+    EXPECT_THROW({
+        platform->resume();
+    }, std::runtime_error);
+}

=== modified file 'tests/unit-tests/graphics/test_graphics_platform.cpp'
--- tests/unit-tests/graphics/test_graphics_platform.cpp	2013-03-22 17:08:01 +0000
+++ tests/unit-tests/graphics/test_graphics_platform.cpp	2013-04-10 12:56:24 +0000
@@ -25,6 +25,8 @@
 #ifndef ANDROID
 #include "gbm/mock_drm.h"
 #include "gbm/mock_gbm.h"
+#include "mir_test_doubles/null_virtual_terminal.h"
+#include "src/server/graphics/gbm/gbm_platform.h"
 #else
 #include "mir_test/hw_mock.h"
 #endif
@@ -69,6 +71,17 @@
 #endif
     }
 
+    std::shared_ptr<mg::Platform> create_platform()
+    {
+#ifdef ANDROID
+        return mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+#else
+        return std::make_shared<mg::gbm::GBMPlatform>(
+            std::make_shared<mg::NullDisplayReport>(),
+            std::make_shared<mir::test::doubles::NullVirtualTerminal>());
+#endif
+    }
+
     std::shared_ptr<ml::Logger> logger;
     std::shared_ptr<mg::BufferInitializer> buffer_initializer;
 
@@ -87,7 +100,7 @@
     using namespace testing;
 
     EXPECT_NO_THROW (
-        auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+        auto platform = create_platform();
         auto allocator = platform->create_buffer_allocator(buffer_initializer);
 
         EXPECT_TRUE(allocator.get());
@@ -97,7 +110,7 @@
 
 TEST_F(GraphicsPlatform, buffer_creation)
 {
-    auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto allocator = platform->create_buffer_allocator(buffer_initializer);
     auto supported_pixel_formats = allocator->supported_pixel_formats();
 
@@ -119,7 +132,7 @@
 
 TEST_F(GraphicsPlatform, get_ipc_package)
 {
-    auto platform = mg::create_platform(std::make_shared<mg::NullDisplayReport>());
+    auto platform = create_platform();
     auto pkg = platform->get_ipc_package();
 
     ASSERT_TRUE(pkg.get() != NULL);

