gnome: fix clutter fullscreen issues

Seems to fix the ocasional image "corruption"
which happens when the video is put into fullscreen
by creating a new VideoWidget in the fullscreen
window and pausing rendering in the VideoWidget of
the main window, instead of moving the VideoWidget.

Refs #74096

Change-Id: Iae87f815545065cf8a799bfa50dd9865a0b8f26e
diff --git a/src/currentcallview.cpp b/src/currentcallview.cpp
index 18df982..ffe6d40 100644
--- a/src/currentcallview.cpp
+++ b/src/currentcallview.cpp
@@ -43,6 +43,7 @@
 #include <media/text.h>
 #include <media/textrecording.h>
 #include "models/gtkqtreemodel.h"
+#include "video/videowindow.h"
 
 struct _CurrentCallView
 {
@@ -70,6 +71,7 @@
     GtkWidget *button_chat_input;
     GtkWidget *entry_chat_input;
     GtkWidget *scrolledwindow_chat;
+    GtkWidget *fullscreen_window;
 
     Call *call;
 
@@ -101,6 +103,11 @@
     QObject::disconnect(priv->media_added_connection);
     QObject::disconnect(priv->new_message_connection);
 
+    if (priv->fullscreen_window) {
+        gtk_widget_destroy(priv->fullscreen_window);
+        priv->fullscreen_window = NULL;
+    }
+
     G_OBJECT_CLASS(current_call_view_parent_class)->dispose(object);
 }
 
@@ -209,32 +216,15 @@
 }
 
 static void
-fullscreen_destroy(CurrentCallView *view)
+on_fullscreen_destroy(CurrentCallView *view)
 {
     g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
     CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
 
-    /* check if the video widgets parent is the the fullscreen window */
-    GtkWidget *parent = gtk_widget_get_parent(priv->video_widget);
-    if (parent != NULL && parent != priv->frame_video) {
-        /* put the videw widget back in the call view */
-        g_object_ref(priv->video_widget);
-        gtk_container_remove(GTK_CONTAINER(parent), priv->video_widget);
-        gtk_container_add(GTK_CONTAINER(priv->frame_video), priv->video_widget);
-        g_object_unref(priv->video_widget);
-        /* destroy the fullscreen window */
-        gtk_widget_destroy(parent);
-    }
-}
-
-static gboolean
-fullscreen_handle_keys(GtkWidget *self, GdkEventKey *event, G_GNUC_UNUSED gpointer user_data)
-{
-    if (event->keyval == GDK_KEY_Escape)
-        gtk_widget_destroy(self);
-
-    /* the event has been fully handled */
-    return TRUE;
+    /* fullscreen is being destroyed, clear the pointer and un-pause the rendering
+     * in this window */
+    priv->fullscreen_window = NULL;
+    video_widget_pause_rendering(VIDEO_WIDGET(priv->video_widget), FALSE);
 }
 
 static gboolean
@@ -246,29 +236,26 @@
 
     /* on double click */
     if (event->type == GDK_2BUTTON_PRESS) {
-
-        /* get the parent to check if its in fullscreen window or not */
-        GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(self));
-        if (parent == priv->frame_video){
-            /* not fullscreen, so put it in a separate widget and make it so */
-            GtkWidget *fullscreen_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-            gtk_window_set_decorated(GTK_WINDOW(fullscreen_window), FALSE);
-            gtk_window_set_transient_for(GTK_WINDOW(fullscreen_window),
-                                         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
-            g_object_ref(self);
-            gtk_container_remove(GTK_CONTAINER(priv->frame_video), self);
-            gtk_container_add(GTK_CONTAINER(fullscreen_window), self);
-            g_object_unref(self);
-            /* connect signals to make sure we can un-fullscreen */
-            g_signal_connect_swapped(fullscreen_window, "destroy", G_CALLBACK(fullscreen_destroy), view);
-            g_signal_connect(view, "destroy", G_CALLBACK(fullscreen_destroy), NULL);
-            g_signal_connect(fullscreen_window, "key_press_event", G_CALLBACK(fullscreen_handle_keys), NULL);
-            /* present the fullscreen widnow */
-            gtk_window_present(GTK_WINDOW(fullscreen_window));
-            gtk_window_fullscreen(GTK_WINDOW(fullscreen_window));
+        if (priv->fullscreen_window) {
+            /* destroy the fullscreen */
+            gtk_widget_destroy(priv->fullscreen_window);
         } else {
-            /* put it back in the call view */
-            fullscreen_destroy(view);
+            /* pause rendering in this window and create fullscreen */
+            video_widget_pause_rendering(VIDEO_WIDGET(priv->video_widget), TRUE);
+
+            priv->fullscreen_window = video_window_new(priv->call,
+                GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
+
+            /* connect to destruction of fullscreen so we know when to un-pause
+             * the rendering in thiw window */
+            g_signal_connect_swapped(priv->fullscreen_window,
+                                     "destroy",
+                                     G_CALLBACK(on_fullscreen_destroy),
+                                     view);
+
+            /* present the fullscreen widnow */
+            gtk_window_present(GTK_WINDOW(priv->fullscreen_window));
+            gtk_window_fullscreen(GTK_WINDOW(priv->fullscreen_window));
         }
     }
 
diff --git a/src/currentcallview.h b/src/currentcallview.h
index 7f2fc17..887e669 100644
--- a/src/currentcallview.h
+++ b/src/currentcallview.h
@@ -32,7 +32,8 @@
 #define _CURRENTCALLVIEW_H
 
 #include <gtk/gtk.h>
-#include <call.h>
+
+class QModelIndex;
 
 G_BEGIN_DECLS
 
@@ -52,4 +53,4 @@
 
 G_END_DECLS
 
-#endif /* _CURRENTCALLVIEW_H */
\ No newline at end of file
+#endif /* _CURRENTCALLVIEW_H */
diff --git a/src/video/video_widget.cpp b/src/video/video_widget.cpp
index ac0080d..6e6b72f 100644
--- a/src/video/video_widget.cpp
+++ b/src/video/video_widget.cpp
@@ -98,6 +98,7 @@
      * this will be set back to false once the black frame is rendered
      */
     std::atomic_bool         show_black_frame;
+    std::atomic_bool         pause_rendering;
     QMetaObject::Connection  frame_update;
     QMetaObject::Connection  render_stop;
     QMetaObject::Connection  render_start;
@@ -411,6 +412,9 @@
     auto actor = wg_renderer->actor;
     g_return_if_fail(CLUTTER_IS_ACTOR(actor));
 
+    if (wg_renderer->pause_rendering)
+        return;
+
     if (wg_renderer->show_black_frame) {
         /* render a black frame set the bool back to false, this is likely done
          * when the renderer is stopped so we ignore whether or not it is running
@@ -671,3 +675,13 @@
 
     g_async_queue_push(priv->new_renderer_queue, new_video_renderer);
 }
+
+void
+video_widget_pause_rendering(VideoWidget *self, gboolean pause)
+{
+    g_return_if_fail(IS_VIDEO_WIDGET(self));
+    VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
+
+    priv->local->pause_rendering = pause;
+    priv->remote->pause_rendering = pause;
+}
diff --git a/src/video/video_widget.h b/src/video/video_widget.h
index b456a4e..64fa81f 100644
--- a/src/video/video_widget.h
+++ b/src/video/video_widget.h
@@ -55,6 +55,7 @@
 GType           video_widget_get_type          (void) G_GNUC_CONST;
 GtkWidget*      video_widget_new               (void);
 void            video_widget_push_new_renderer (VideoWidget *, Video::Renderer *, VideoRendererType);
+void            video_widget_pause_rendering   (VideoWidget *self, gboolean pause);
 
 G_END_DECLS
 
diff --git a/src/video/videowindow.cpp b/src/video/videowindow.cpp
new file mode 100644
index 0000000..232eaed
--- /dev/null
+++ b/src/video/videowindow.cpp
@@ -0,0 +1,175 @@
+/*
+ *  Copyright (C) 2015 Savoir-Faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.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 3 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.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#include "videowindow.h"
+
+#include <gtk/gtk.h>
+#include <call.h>
+#include "video_widget.h"
+#include <video/previewmanager.h>
+
+struct _VideoWindow
+{
+    GtkWindow parent;
+};
+
+struct _VideoWindowClass
+{
+    GtkWindowClass parent_class;
+};
+
+typedef struct _VideoWindowPrivate VideoWindowPrivate;
+
+struct _VideoWindowPrivate
+{
+    GtkWidget *video_widget;
+
+    QMetaObject::Connection local_renderer_connection;
+    QMetaObject::Connection remote_renderer_connection;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(VideoWindow, video_window, GTK_TYPE_WINDOW);
+
+#define VIDEO_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), VIDEO_WINDOW_TYPE, VideoWindowPrivate))
+
+static void
+video_window_dispose(GObject *object)
+{
+    VideoWindow *self;
+    VideoWindowPrivate *priv;
+
+    self = VIDEO_WINDOW(object);
+    priv = VIDEO_WINDOW_GET_PRIVATE(self);
+
+    QObject::disconnect(priv->local_renderer_connection);
+    QObject::disconnect(priv->remote_renderer_connection);
+
+    G_OBJECT_CLASS(video_window_parent_class)->dispose(object);
+}
+
+static gboolean
+handle_keys(GtkWidget *self, GdkEventKey *event, G_GNUC_UNUSED gpointer user_data)
+{
+    if (event->keyval == GDK_KEY_Escape) {
+        gtk_widget_destroy(self);
+        /* the event has been fully handled */
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+handle_button_press(GtkWidget *widget, GdkEventButton *event, VideoWindow *self)
+{
+    g_return_val_if_fail(IS_VIDEO_WIDGET(widget), FALSE);
+    g_return_val_if_fail(IS_VIDEO_WINDOW(self), FALSE);
+
+    /* on double click */
+    if (event->type == GDK_2BUTTON_PRESS) {
+        gtk_widget_destroy(GTK_WIDGET(self));
+        /* the event has been fully handled */
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+video_window_init(VideoWindow *self)
+{
+    VideoWindowPrivate *priv = VIDEO_WINDOW_GET_PRIVATE(self);
+
+    gtk_window_set_decorated(GTK_WINDOW(self), FALSE);
+
+    /* video widget */
+    priv->video_widget = video_widget_new();
+    gtk_container_add(GTK_CONTAINER(self), priv->video_widget);
+    gtk_widget_show_all(priv->video_widget);
+
+    /* catch double click to exot full screen */
+    g_signal_connect(priv->video_widget, "button-press-event",
+                     G_CALLBACK(handle_button_press),
+                     self);
+
+    /* catch esc to exit fullscreen */
+    g_signal_connect(self, "key_press_event", G_CALLBACK(handle_keys), NULL);
+}
+
+static void
+video_window_class_init(VideoWindowClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = video_window_dispose;
+}
+
+GtkWidget *
+video_window_new(Call *call, GtkWindow *parent)
+{
+    GtkWidget *self = (GtkWidget *)g_object_new(VIDEO_WINDOW_TYPE, NULL);
+    VideoWindowPrivate *priv = VIDEO_WINDOW_GET_PRIVATE(self);
+
+    if (parent)
+        gtk_window_set_transient_for(GTK_WINDOW(self), parent);
+
+    /* check if we already have a renderer */
+    video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
+                                   call->videoRenderer(),
+                                   VIDEO_RENDERER_REMOTE);
+
+    /* callback for remote renderer */
+    priv->remote_renderer_connection = QObject::connect(
+        call,
+        &Call::videoStarted,
+        [=](Video::Renderer *renderer) {
+            video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
+                                           renderer,
+                                           VIDEO_RENDERER_REMOTE);
+        }
+    );
+
+    /* local renderer */
+    if (Video::PreviewManager::instance()->isPreviewing())
+        video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
+                                       Video::PreviewManager::instance()->previewRenderer(),
+                                       VIDEO_RENDERER_LOCAL);
+
+    /* callback for local renderer */
+    priv->local_renderer_connection = QObject::connect(
+        Video::PreviewManager::instance(),
+        &Video::PreviewManager::previewStarted,
+        [=](Video::Renderer *renderer) {
+            video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
+                                           renderer,
+                                           VIDEO_RENDERER_LOCAL);
+        }
+    );
+
+    return self;
+}
diff --git a/src/video/videowindow.h b/src/video/videowindow.h
new file mode 100644
index 0000000..3dd6248
--- /dev/null
+++ b/src/video/videowindow.h
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2015 Savoir-Faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.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 3 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.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#ifndef _VIDEOWINDOW_H
+#define _VIDEOWINDOW_H
+
+#include <gtk/gtk.h>
+
+class Call;
+
+G_BEGIN_DECLS
+
+#define VIDEO_WINDOW_TYPE            (video_window_get_type ())
+#define VIDEO_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIDEO_WINDOW_TYPE, VideoWindow))
+#define VIDEO_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), VIDEO_WINDOW_TYPE, VideoWindowClass))
+#define IS_VIDEO_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), VIDEO_WINDOW_TYPE))
+#define IS_VIDEO_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), VIDEO_WINDOW_TYPE))
+
+typedef struct _VideoWindow      VideoWindow;
+typedef struct _VideoWindowClass VideoWindowClass;
+
+
+GType      video_window_get_type      (void) G_GNUC_CONST;
+GtkWidget *video_window_new           (Call *call, GtkWindow *parent);
+
+G_END_DECLS
+
+#endif /* _VIDEOWINDOW_H */