1 /*
2  * Virt Viewer: A virtual machine console viewer
3  *
4  * Copyright (c) 2016 Red Hat, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Author: Cole Robinson <crobinso@redhat.com>
21  * Author: Fabiano Fidêncio <fidencio@redhat.com>
22  */
23 
24 #include <config.h>
25 
26 #include "virt-viewer-timed-revealer.h"
27 
28 struct _VirtViewerTimedRevealer
29 {
30     GtkEventBox parent;
31     gboolean fullscreen;
32     guint timeout_id;
33 
34     GtkWidget *revealer;
35 };
36 
G_DEFINE_TYPE(VirtViewerTimedRevealer,virt_viewer_timed_revealer,GTK_TYPE_EVENT_BOX)37 G_DEFINE_TYPE(VirtViewerTimedRevealer, virt_viewer_timed_revealer, GTK_TYPE_EVENT_BOX)
38 
39 static void
40 virt_viewer_timed_revealer_unregister_timeout(VirtViewerTimedRevealer *self)
41 {
42     if (self->timeout_id) {
43         g_source_remove(self->timeout_id);
44         self->timeout_id = 0;
45     }
46 }
47 
48 static gboolean
schedule_unreveal_timeout_cb(VirtViewerTimedRevealer * self)49 schedule_unreveal_timeout_cb(VirtViewerTimedRevealer *self)
50 {
51     gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), FALSE);
52     self->timeout_id = 0;
53 
54     return FALSE;
55 }
56 
57 static void
virt_viewer_timed_revealer_schedule_unreveal_timeout(VirtViewerTimedRevealer * self,guint timeout)58 virt_viewer_timed_revealer_schedule_unreveal_timeout(VirtViewerTimedRevealer *self,
59                                                      guint timeout)
60 {
61     if (self->timeout_id != 0)
62         return;
63 
64     self->timeout_id = g_timeout_add(timeout,
65                                      (GSourceFunc)schedule_unreveal_timeout_cb,
66                                      self);
67 }
68 
69 static void
virt_viewer_timed_revealer_grab_notify(VirtViewerTimedRevealer * self,gboolean was_grabbed,gpointer user_data G_GNUC_UNUSED)70 virt_viewer_timed_revealer_grab_notify(VirtViewerTimedRevealer *self,
71                                        gboolean was_grabbed,
72                                        gpointer user_data G_GNUC_UNUSED)
73 {
74     if (was_grabbed)
75         virt_viewer_timed_revealer_schedule_unreveal_timeout(self, 1000);
76 }
77 
78 static gboolean
virt_viewer_timed_revealer_enter_notify(VirtViewerTimedRevealer * self,GdkEventCrossing * event G_GNUC_UNUSED,gpointer user_data G_GNUC_UNUSED)79 virt_viewer_timed_revealer_enter_notify(VirtViewerTimedRevealer *self,
80                                         GdkEventCrossing *event G_GNUC_UNUSED,
81                                         gpointer user_data G_GNUC_UNUSED)
82 {
83     if (!self->fullscreen)
84         return FALSE;
85 
86     virt_viewer_timed_revealer_unregister_timeout(self);
87     if (!gtk_revealer_get_reveal_child(GTK_REVEALER(self->revealer))) {
88         gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE);
89     }
90 
91     return FALSE;
92 }
93 
94 static gboolean
virt_viewer_timed_revealer_leave_notify(VirtViewerTimedRevealer * self,GdkEventCrossing * event G_GNUC_UNUSED,gpointer user_data G_GNUC_UNUSED)95 virt_viewer_timed_revealer_leave_notify(VirtViewerTimedRevealer *self,
96                                         GdkEventCrossing *event G_GNUC_UNUSED,
97                                         gpointer user_data G_GNUC_UNUSED)
98 {
99     if (!self->fullscreen)
100         return FALSE;
101 
102     /*
103      * Pointer exited the toolbar, and toolbar is revealed. Schedule
104      * a timeout to close it, if one isn't already scheduled.
105      */
106     if (gtk_revealer_get_reveal_child(GTK_REVEALER(self->revealer))) {
107         virt_viewer_timed_revealer_schedule_unreveal_timeout(self, 1000);
108     }
109 
110     return FALSE;
111 }
112 
113 static void
virt_viewer_timed_revealer_init(VirtViewerTimedRevealer * self G_GNUC_UNUSED)114 virt_viewer_timed_revealer_init(VirtViewerTimedRevealer *self G_GNUC_UNUSED)
115 {
116 }
117 
118 static void
virt_viewer_timed_revealer_dispose(GObject * object)119 virt_viewer_timed_revealer_dispose(GObject *object)
120 {
121     VirtViewerTimedRevealer *self = VIRT_VIEWER_TIMED_REVEALER(object);
122 
123     self->revealer = NULL;
124 
125     if (self->timeout_id) {
126         g_source_remove(self->timeout_id);
127         self->timeout_id = 0;
128     }
129 
130     G_OBJECT_CLASS(virt_viewer_timed_revealer_parent_class)->dispose(object);
131 }
132 
133 
134 static void
virt_viewer_timed_revealer_class_init(VirtViewerTimedRevealerClass * klass)135 virt_viewer_timed_revealer_class_init(VirtViewerTimedRevealerClass *klass)
136 {
137    GObjectClass *object_class = G_OBJECT_CLASS(klass);
138 
139    object_class->dispose = virt_viewer_timed_revealer_dispose;
140 }
141 
142 VirtViewerTimedRevealer *
virt_viewer_timed_revealer_new(GtkWidget * toolbar)143 virt_viewer_timed_revealer_new(GtkWidget *toolbar)
144 {
145     VirtViewerTimedRevealer *self;
146 
147     self = g_object_new(VIRT_VIEWER_TYPE_TIMED_REVEALER, NULL);
148 
149     self->fullscreen = FALSE;
150     self->timeout_id = 0;
151 
152     self->revealer = gtk_revealer_new();
153     gtk_container_add(GTK_CONTAINER(self->revealer), toolbar);
154 
155     /*
156      * Adding the revealer to the eventbox seems to ensure the
157      * GtkEventBox always has 1 invisible pixel showing at the top of the
158      * screen, which we can use to grab the pointer event to show
159      * the hidden toolbar.
160      */
161 
162     gtk_container_add(GTK_CONTAINER(self), self->revealer);
163     gtk_widget_show(self->revealer);
164     gtk_widget_set_halign(GTK_WIDGET(self), GTK_ALIGN_CENTER);
165     gtk_widget_set_valign(GTK_WIDGET(self), GTK_ALIGN_START);
166     gtk_widget_show(GTK_WIDGET(self));
167 
168     g_signal_connect(self,
169                      "grab-notify",
170                      G_CALLBACK(virt_viewer_timed_revealer_grab_notify),
171                      NULL);
172     g_signal_connect(self,
173                      "enter-notify-event",
174                      G_CALLBACK(virt_viewer_timed_revealer_enter_notify),
175                      NULL);
176     g_signal_connect(self,
177                      "leave-notify-event",
178                      G_CALLBACK(virt_viewer_timed_revealer_leave_notify),
179                      NULL);
180 
181     return self;
182 }
183 
184 void
virt_viewer_timed_revealer_force_reveal(VirtViewerTimedRevealer * self,gboolean fullscreen)185 virt_viewer_timed_revealer_force_reveal(VirtViewerTimedRevealer *self,
186                                         gboolean fullscreen)
187 {
188     g_return_if_fail(VIRT_VIEWER_IS_TIMED_REVEALER(self));
189 
190     virt_viewer_timed_revealer_unregister_timeout(self);
191     self->fullscreen = fullscreen;
192     gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), fullscreen);
193     virt_viewer_timed_revealer_schedule_unreveal_timeout(self, 2000);
194 }
195