2 * Copyright (C) 2013 Igalia S.L
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 #if ENABLE(VIDEO) && USE(GSTREAMER) && USE(NATIVE_FULLSCREEN_VIDEO)
24 #include "FullscreenVideoControllerGtk.h"
26 #include "FullscreenVideoControllerGStreamer.h"
27 #include "GRefPtrGtk.h"
28 #include "GStreamerGWorld.h"
29 #include "GtkVersioning.h"
30 #include "MediaPlayer.h"
31 #include "MediaPlayerPrivateGStreamerBase.h"
34 #include <gdk/gdkkeysyms.h>
35 #include <glib/gi18n-lib.h>
37 #include <wtf/text/CString.h>
39 #define HUD_AUTO_HIDE_INTERVAL 3000 // 3 seconds
40 #define PROGRESS_BAR_UPDATE_INTERVAL 150 // 150ms
42 // Use symbolic icons only if we build with GTK+-3 support. They could
43 // be enabled for the GTK+2 build but we'd need to bump the required
44 // version to at least 2.22.
45 #if GTK_MAJOR_VERSION > 2
46 #define ICON_NAME_SUFFIX "-symbolic"
48 #define ICON_NAME_SUFFIX
51 #define PLAY_ICON_NAME "media-playback-start"ICON_NAME_SUFFIX
52 #define PAUSE_ICON_NAME "media-playback-pause"ICON_NAME_SUFFIX
53 #define EXIT_FULLSCREEN_ICON_NAME "view-restore"ICON_NAME_SUFFIX
57 static gboolean hideHudCallback(FullscreenVideoControllerGtk* controller)
59 controller->hideHud();
63 static gboolean onFullscreenGtkMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event, FullscreenVideoControllerGtk* controller)
65 controller->showHud(true);
69 static void onFullscreenGtkActiveNotification(GtkWidget* widget, GParamSpec* property, FullscreenVideoControllerGtk* controller)
71 if (!gtk_window_is_active(GTK_WINDOW(widget)))
72 controller->hideHud();
75 static gboolean onFullscreenGtkConfigureEvent(GtkWidget* widget, GdkEventConfigure* event, FullscreenVideoControllerGtk* controller)
77 controller->gtkConfigure(event);
81 static void onFullscreenGtkDestroy(GtkWidget* widget, FullscreenVideoControllerGtk* controller)
83 controller->exitFullscreen();
86 static void togglePlayPauseActivated(GtkAction* action, FullscreenVideoControllerGtk* controller)
88 controller->togglePlay();
91 static void exitFullscreenActivated(GtkAction* action, FullscreenVideoControllerGtk* controller)
93 controller->exitOnUserRequest();
96 static gboolean progressBarUpdateCallback(FullscreenVideoControllerGtk* controller)
98 return controller->updateHudProgressBar();
101 static gboolean timeScaleButtonPressed(GtkWidget* widget, GdkEventButton* event, FullscreenVideoControllerGtk* controller)
103 if (event->type != GDK_BUTTON_PRESS)
106 controller->beginSeek();
110 static gboolean timeScaleButtonReleased(GtkWidget* widget, GdkEventButton* event, FullscreenVideoControllerGtk* controller)
112 controller->endSeek();
116 static void timeScaleValueChanged(GtkWidget* widget, FullscreenVideoControllerGtk* controller)
118 controller->doSeek();
121 static void volumeValueChanged(GtkScaleButton *button, gdouble value, FullscreenVideoControllerGtk* controller)
123 controller->setVolume(static_cast<float>(value));
127 FullscreenVideoControllerGtk::FullscreenVideoControllerGtk(MediaPlayerPrivateGStreamerBase* player)
128 : FullscreenVideoControllerGStreamer(player)
130 , m_progressBarUpdateId(0)
135 , m_keyPressSignalId(0)
136 , m_destroySignalId(0)
137 , m_isActiveSignalId(0)
138 , m_motionNotifySignalId(0)
139 , m_configureEventSignalId(0)
140 , m_hudMotionNotifySignalId(0)
141 , m_timeScaleButtonPressedSignalId(0)
142 , m_timeScaleButtonReleasedSignalId(0)
143 , m_playActionActivateSignalId(0)
144 , m_exitFullcreenActionActivateSignalId(0)
148 void FullscreenVideoControllerGtk::gtkConfigure(GdkEventConfigure* event)
153 void FullscreenVideoControllerGtk::showHud(bool autoHide)
158 if (m_hudTimeoutId) {
159 g_source_remove(m_hudTimeoutId);
164 GdkWindow* window = gtk_widget_get_window(m_window);
165 gdk_window_set_cursor(window, 0);
167 // Update the progress bar immediately before showing the window.
168 updateHudProgressBar();
169 gtk_widget_show_all(m_hudWindow);
172 // Start periodic updates of the progress bar.
173 if (!m_progressBarUpdateId)
174 m_progressBarUpdateId = g_timeout_add(PROGRESS_BAR_UPDATE_INTERVAL, reinterpret_cast<GSourceFunc>(progressBarUpdateCallback), this);
176 // Hide the hud in few seconds, if requested.
178 m_hudTimeoutId = g_timeout_add(HUD_AUTO_HIDE_INTERVAL, reinterpret_cast<GSourceFunc>(hideHudCallback), this);
181 void FullscreenVideoControllerGtk::hideHud()
183 if (m_hudTimeoutId) {
184 g_source_remove(m_hudTimeoutId);
191 // Keep the hud visible if a seek is in progress or if the volume
193 GtkWidget* volumePopup = gtk_scale_button_get_popup(GTK_SCALE_BUTTON(m_volumeButton));
194 if (m_seekLock || gtk_widget_get_visible(volumePopup)) {
199 GdkWindow* window = gtk_widget_get_window(m_window);
200 GRefPtr<GdkCursor> cursor = adoptGRef(gdk_cursor_new(GDK_BLANK_CURSOR));
201 gdk_window_set_cursor(window, cursor.get());
203 gtk_widget_hide(m_hudWindow);
205 if (m_progressBarUpdateId) {
206 g_source_remove(m_progressBarUpdateId);
207 m_progressBarUpdateId = 0;
211 static gboolean onFullscreenGtkKeyPressEvent(GtkWidget* widget, GdkEventKey* event, FullscreenVideoControllerGtk* controller)
213 switch (event->keyval) {
215 controller->exitOnUserRequest();
219 controller->togglePlay();
222 controller->increaseVolume();
225 controller->decreaseVolume();
234 void FullscreenVideoControllerGtk::initializeWindow()
236 m_window = reinterpret_cast<GtkWidget*>(m_gstreamerGWorld->platformVideoWindow()->window());
241 // Ensure black background.
242 #ifdef GTK_API_VERSION_2
243 GdkColor color = { 1, 0, 0, 0 };
244 gtk_widget_modify_bg(m_window, GTK_STATE_NORMAL, &color);
246 GdkRGBA color = { 0, 0, 0, 1};
247 gtk_widget_override_background_color(m_window, GTK_STATE_FLAG_NORMAL, &color);
249 gtk_widget_set_double_buffered(m_window, FALSE);
251 m_keyPressSignalId = g_signal_connect(m_window, "key-press-event", G_CALLBACK(onFullscreenGtkKeyPressEvent), this);
252 m_destroySignalId = g_signal_connect(m_window, "destroy", G_CALLBACK(onFullscreenGtkDestroy), this);
253 m_isActiveSignalId = g_signal_connect(m_window, "notify::is-active", G_CALLBACK(onFullscreenGtkActiveNotification), this);
255 gtk_widget_show_all(m_window);
257 GdkWindow* window = gtk_widget_get_window(m_window);
258 GRefPtr<GdkCursor> cursor = adoptGRef(gdk_cursor_new(GDK_BLANK_CURSOR));
259 gdk_window_set_cursor(window, cursor.get());
261 m_motionNotifySignalId = g_signal_connect(m_window, "motion-notify-event", G_CALLBACK(onFullscreenGtkMotionNotifyEvent), this);
262 m_configureEventSignalId = g_signal_connect(m_window, "configure-event", G_CALLBACK(onFullscreenGtkConfigureEvent), this);
264 gtk_window_fullscreen(GTK_WINDOW(m_window));
268 void FullscreenVideoControllerGtk::updateHudPosition()
273 // Get the screen rectangle.
274 GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(m_window));
275 GdkWindow* window = gtk_widget_get_window(m_window);
276 GdkRectangle fullscreenRectangle;
277 gdk_screen_get_monitor_geometry(screen, gdk_screen_get_monitor_at_window(screen, window), &fullscreenRectangle);
279 // Get the popup window size.
280 int hudWidth, hudHeight;
281 gtk_window_get_size(GTK_WINDOW(m_hudWindow), &hudWidth, &hudHeight);
283 // Resize the hud to the full width of the screen.
284 gtk_window_resize(GTK_WINDOW(m_hudWindow), fullscreenRectangle.width, hudHeight);
286 // Move the hud to the bottom of the screen.
287 gtk_window_move(GTK_WINDOW(m_hudWindow), fullscreenRectangle.x, fullscreenRectangle.height + fullscreenRectangle.y - hudHeight);
290 void FullscreenVideoControllerGtk::destroyWindow()
295 g_signal_handler_disconnect(m_window, m_keyPressSignalId);
296 g_signal_handler_disconnect(m_window, m_destroySignalId);
297 g_signal_handler_disconnect(m_window, m_isActiveSignalId);
298 g_signal_handler_disconnect(m_window, m_motionNotifySignalId);
299 g_signal_handler_disconnect(m_window, m_configureEventSignalId);
300 g_signal_handler_disconnect(m_hudWindow, m_hudMotionNotifySignalId);
301 g_signal_handler_disconnect(m_timeHScale, m_timeScaleButtonPressedSignalId);
302 g_signal_handler_disconnect(m_timeHScale, m_timeScaleButtonReleasedSignalId);
303 g_signal_handler_disconnect(m_timeHScale, m_hscaleUpdateId);
304 g_signal_handler_disconnect(m_volumeButton, m_volumeUpdateId);
305 g_signal_handler_disconnect(m_playPauseAction, m_playActionActivateSignalId);
306 g_signal_handler_disconnect(m_exitFullscreenAction, m_exitFullcreenActionActivateSignalId);
308 if (m_hudTimeoutId) {
309 g_source_remove(m_hudTimeoutId);
313 if (m_progressBarUpdateId) {
314 g_source_remove(m_progressBarUpdateId);
315 m_progressBarUpdateId = 0;
318 gtk_widget_hide(m_window);
321 gtk_widget_destroy(m_hudWindow);
325 void FullscreenVideoControllerGtk::playStateChanged()
327 if (m_client->mediaPlayerIsPaused())
328 g_object_set(m_playPauseAction, "tooltip", _("Play"), "icon-name", PLAY_ICON_NAME, NULL);
330 g_object_set(m_playPauseAction, "tooltip", _("Pause"), "icon-name", PAUSE_ICON_NAME, NULL);
331 showHud(!m_client->mediaPlayerIsPaused());
334 void FullscreenVideoControllerGtk::volumeChanged()
339 g_signal_handler_block(m_volumeButton, m_volumeUpdateId);
340 gtk_scale_button_set_value(GTK_SCALE_BUTTON(m_volumeButton), m_player->volume());
341 g_signal_handler_unblock(m_volumeButton, m_volumeUpdateId);
344 void FullscreenVideoControllerGtk::muteChanged()
349 g_signal_handler_block(m_volumeButton, m_volumeUpdateId);
350 gtk_scale_button_set_value(GTK_SCALE_BUTTON(m_volumeButton), m_player->muted() ? 0 : m_player->volume());
351 g_signal_handler_unblock(m_volumeButton, m_volumeUpdateId);
354 void FullscreenVideoControllerGtk::beginSeek()
359 void FullscreenVideoControllerGtk::doSeek()
364 m_player->seek(gtk_range_get_value(GTK_RANGE(m_timeHScale))*m_player->duration() / 100);
367 void FullscreenVideoControllerGtk::endSeek()
372 gboolean FullscreenVideoControllerGtk::updateHudProgressBar()
374 float mediaDuration(m_player->duration());
375 float mediaPosition(m_player->currentTime());
380 if (mediaPosition && mediaDuration)
381 value = (mediaPosition * 100.0) / mediaDuration;
383 GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(m_timeHScale));
384 gtk_adjustment_set_value(adjustment, value);
387 gtk_range_set_fill_level(GTK_RANGE(m_timeHScale), (m_player->maxTimeLoaded() / mediaDuration)* 100);
389 gchar* label = g_strdup_printf("%s / %s", timeToString(mediaPosition).utf8().data(), timeToString(mediaDuration).utf8().data());
390 gtk_label_set_text(GTK_LABEL(m_timeLabel), label);
395 void FullscreenVideoControllerGtk::createHud()
397 m_hudWindow = gtk_window_new(GTK_WINDOW_POPUP);
398 gtk_window_set_gravity(GTK_WINDOW(m_hudWindow), GDK_GRAVITY_SOUTH_WEST);
399 gtk_window_set_type_hint(GTK_WINDOW(m_hudWindow), GDK_WINDOW_TYPE_HINT_NORMAL);
401 m_hudMotionNotifySignalId = g_signal_connect(m_hudWindow, "motion-notify-event", G_CALLBACK(onFullscreenGtkMotionNotifyEvent), this);
403 #ifdef GTK_API_VERSION_2
404 GtkWidget* hbox = gtk_hbox_new(FALSE, 4);
406 GtkWidget* hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
408 gtk_container_add(GTK_CONTAINER(m_hudWindow), hbox);
410 m_playPauseAction = gtk_action_new("play", _("Play / Pause"), _("Play or pause the media"), PAUSE_ICON_NAME);
411 m_playActionActivateSignalId = g_signal_connect(m_playPauseAction, "activate", G_CALLBACK(togglePlayPauseActivated), this);
413 GtkWidget* item = gtk_action_create_tool_item(m_playPauseAction);
414 gtk_box_pack_start(GTK_BOX(hbox), item, FALSE, TRUE, 0);
416 GtkWidget* label = gtk_label_new(_("Time:"));
417 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
419 GtkAdjustment* adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 100.0, 0.1, 1.0, 1.0));
420 #ifdef GTK_API_VERSION_2
421 m_timeHScale = gtk_hscale_new(adjustment);
423 m_timeHScale = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, adjustment);
425 gtk_scale_set_draw_value(GTK_SCALE(m_timeHScale), FALSE);
426 gtk_range_set_show_fill_level(GTK_RANGE(m_timeHScale), TRUE);
427 m_timeScaleButtonPressedSignalId = g_signal_connect(m_timeHScale, "button-press-event", G_CALLBACK(timeScaleButtonPressed), this);
428 m_timeScaleButtonReleasedSignalId = g_signal_connect(m_timeHScale, "button-release-event", G_CALLBACK(timeScaleButtonReleased), this);
429 m_hscaleUpdateId = g_signal_connect(m_timeHScale, "value-changed", G_CALLBACK(timeScaleValueChanged), this);
431 gtk_box_pack_start(GTK_BOX(hbox), m_timeHScale, TRUE, TRUE, 0);
433 m_timeLabel = gtk_label_new("");
434 gtk_box_pack_start(GTK_BOX(hbox), m_timeLabel, FALSE, TRUE, 0);
437 m_volumeButton = gtk_volume_button_new();
438 gtk_box_pack_start(GTK_BOX(hbox), m_volumeButton, FALSE, TRUE, 0);
439 gtk_scale_button_set_value(GTK_SCALE_BUTTON(m_volumeButton), m_player->volume());
440 m_volumeUpdateId = g_signal_connect(m_volumeButton, "value-changed", G_CALLBACK(volumeValueChanged), this);
442 m_exitFullscreenAction = gtk_action_new("exit", _("Exit Fullscreen"), _("Exit from fullscreen mode"), EXIT_FULLSCREEN_ICON_NAME);
443 m_exitFullcreenActionActivateSignalId = g_signal_connect(m_exitFullscreenAction, "activate", G_CALLBACK(exitFullscreenActivated), this);
444 g_object_set(m_exitFullscreenAction, "icon-name", EXIT_FULLSCREEN_ICON_NAME, NULL);
445 item = gtk_action_create_tool_item(m_exitFullscreenAction);
446 gtk_box_pack_start(GTK_BOX(hbox), item, FALSE, TRUE, 0);
448 m_progressBarUpdateId = g_timeout_add(PROGRESS_BAR_UPDATE_INTERVAL, reinterpret_cast<GSourceFunc>(progressBarUpdateCallback), this);
453 } // namespace WebCore