vlcplugin_gtk.cpp 13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*****************************************************************************
 * vlcplugin_gtk.cpp: a VLC plugin for Mozilla (GTK+ interface)
 *****************************************************************************
 * Copyright (C) 2002-2010 the VideoLAN team
 * $Id$
 *
 * Authors: Cheng Sun <chengsun9@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

24 25 26 27
#include "vlcplugin_gtk.h"
#include <gdk/gdkx.h>
#include <cstring>

Cheng Sun's avatar
Cheng Sun committed
28 29
static uint32_t get_xid(GtkWidget *widget)
{
30 31 32 33 34 35 36 37
    GdkDrawable *video_drawable = gtk_widget_get_window(widget);
    return (uint32_t)gdk_x11_drawable_get_xid(video_drawable);
}

VlcPluginGtk::VlcPluginGtk(NPP instance, NPuint16_t mode) :
    VlcPluginBase(instance, mode),
    parent(NULL),
    parent_vbox(NULL),
38 39 40 41
    video_container(NULL),
    toolbar(NULL),
    fullscreen_win(NULL),
    is_fullscreen(false)
42
{
43
    memset(&video_xwindow, 0, sizeof(Window));
44 45 46 47 48 49
}

VlcPluginGtk::~VlcPluginGtk()
{
}

50 51 52 53 54 55
Display *VlcPluginGtk::get_display()
{
    return ( (NPSetWindowCallbackStruct *)
             npwindow.ws_info )->display;
}

56 57 58
void VlcPluginGtk::set_player_window()
{
    libvlc_media_player_set_xwindow(libvlc_media_player,
59
                                    video_xwindow);
60
    libvlc_video_set_mouse_input(libvlc_media_player, 0);
61 62 63 64
}

void VlcPluginGtk::toggle_fullscreen()
{
65
    set_fullscreen(!get_fullscreen());
66 67 68 69
}

void VlcPluginGtk::set_fullscreen(int yes)
{
70 71 72 73 74
    /* we have to reparent windows */
    /* note that the xid of video_container changes after reparenting */
    Display *display = get_display();
    g_signal_handler_block(video_container, video_container_size_handler_id);

Cheng Sun's avatar
Cheng Sun committed
75 76 77 78
    XUnmapWindow(display, video_xwindow);
    XReparentWindow(display, video_xwindow,
                    gdk_x11_get_default_root_xwindow(), 0,0);
    if (yes) {
79 80 81 82 83 84 85 86
        gtk_widget_reparent(GTK_WIDGET(parent_vbox), GTK_WIDGET(fullscreen_win));
        gtk_widget_show(fullscreen_win);
        gtk_window_fullscreen(GTK_WINDOW(fullscreen_win));
    } else {
        gtk_widget_hide(fullscreen_win);
        gtk_widget_reparent(GTK_WIDGET(parent_vbox), GTK_WIDGET(parent));
        gtk_widget_show_all(GTK_WIDGET(parent));
    }
Cheng Sun's avatar
Cheng Sun committed
87 88 89
    XReparentWindow(display, video_xwindow, get_xid(video_container), 0,0);
    XMapWindow(display, video_xwindow);

90 91 92 93 94
//    libvlc_set_fullscreen(libvlc_media_player, yes);
    g_signal_handler_unblock(video_container, video_container_size_handler_id);
    gtk_widget_queue_resize_no_redraw(video_container);

    is_fullscreen = yes;
95 96 97 98
}

int  VlcPluginGtk::get_fullscreen()
{
99
    return is_fullscreen;
100 101 102 103
}

void VlcPluginGtk::show_toolbar()
{
104 105
    gtk_box_pack_start(GTK_BOX(parent_vbox), toolbar, false, false, 0);
    gtk_widget_show_all(toolbar);
106 107 108 109
}

void VlcPluginGtk::hide_toolbar()
{
110 111 112 113
    gtk_widget_hide(toolbar);
    gtk_container_remove(GTK_CONTAINER(parent_vbox), toolbar);
}

114 115 116 117 118 119 120
void VlcPluginGtk::resize_video_xwindow(GdkRectangle *rect)
{
    Display *display = get_display();
    XResizeWindow(display, video_xwindow,
                  rect->width, rect->height);
}

121 122 123 124 125 126 127 128 129 130
struct tool_actions_t
{
    const gchar *stock_id;
    vlc_toolbar_clicked_t clicked;
};
static const tool_actions_t tool_actions[] = {
    {GTK_STOCK_MEDIA_PLAY, clicked_Play},
    {GTK_STOCK_MEDIA_PAUSE, clicked_Pause},
    {GTK_STOCK_MEDIA_STOP, clicked_Stop},
    {"gtk-volume-muted", clicked_Mute},
131 132
    {"gtk-volume-unmuted", clicked_Unmute},
    {GTK_STOCK_FULLSCREEN, clicked_Fullscreen}
133 134
};

Cheng Sun's avatar
Cheng Sun committed
135
static void toolbar_handler(GtkToolButton *btn, gpointer user_data)
136
{
Cheng Sun's avatar
Cheng Sun committed
137
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
138 139 140 141 142 143 144
    const gchar *stock_id = gtk_tool_button_get_stock_id(btn);
    for (int i = 0; i < sizeof(tool_actions)/sizeof(tool_actions_t); ++i) {
        if (!strcmp(stock_id, tool_actions[i].stock_id)) {
            plugin->control_handler(tool_actions[i].clicked);
            return;
        }
    }
Cheng Sun's avatar
Cheng Sun committed
145
    fprintf(stderr, "WARNING: No idea what toolbar button you just clicked on (%s)\n", stock_id?stock_id:"NULL");
146 147
}

148 149 150 151 152 153 154 155 156 157 158 159 160
static void menu_handler(GtkMenuItem *menuitem, gpointer user_data)
{
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
    const gchar *stock_id = gtk_menu_item_get_label(GTK_MENU_ITEM(menuitem));
    for (int i = 0; i < sizeof(tool_actions)/sizeof(tool_actions_t); ++i) {
        if (!strcmp(stock_id, tool_actions[i].stock_id)) {
            plugin->control_handler(tool_actions[i].clicked);
            return;
        }
    }
    fprintf(stderr, "WARNING: No idea what menu item you just clicked on (%s)\n", stock_id?stock_id:"NULL");
}

161
void VlcPluginGtk::popup_menu()
162 163
{
    /* construct menu */
164
    GtkWidget *popupmenu = gtk_menu_new();
165 166 167 168 169 170 171 172
    GtkWidget *menuitem;

    /* play/pause */
    menuitem = gtk_image_menu_item_new_from_stock(
                        playlist_isplaying() ?
                        GTK_STOCK_MEDIA_PAUSE :
                        GTK_STOCK_MEDIA_PLAY, NULL);
    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_handler), this);
173
    gtk_menu_shell_append(GTK_MENU_SHELL(popupmenu), menuitem);
174 175 176 177
    /* stop */
    menuitem = gtk_image_menu_item_new_from_stock(
                                GTK_STOCK_MEDIA_STOP, NULL);
    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_handler), this);
178
    gtk_menu_shell_append(GTK_MENU_SHELL(popupmenu), menuitem);
179 180 181 182 183 184
    /* set fullscreen */
    menuitem = gtk_image_menu_item_new_from_stock(
                                GTK_STOCK_FULLSCREEN, NULL);
    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_handler), this);
    gtk_menu_shell_append(GTK_MENU_SHELL(popupmenu), menuitem);

185

186
    /* show menu */
187
    gtk_widget_show_all(popupmenu);
188
    gtk_menu_attach_to_widget(GTK_MENU(popupmenu), video_container, NULL);
189 190
    gtk_menu_popup(GTK_MENU(popupmenu), NULL, NULL, NULL, NULL,
                   0, gtk_get_current_event_time());
191 192 193 194 195 196
}

static bool video_button_handler(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
    if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
197
        plugin->popup_menu();
198 199
        return true;
    }
200 201 202
    if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
        plugin->toggle_fullscreen();
    }
203 204 205 206 207
    return false;
}

static bool video_popup_handler(GtkWidget *widget, gpointer user_data) {
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
208
    plugin->popup_menu();
209 210 211
    return true;
}

212 213 214 215 216 217
static bool video_size_handler(GtkWidget *widget, GdkRectangle *rect, gpointer user_data) {
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
    plugin->resize_video_xwindow(rect);
    return true;
}

Cheng Sun's avatar
Cheng Sun committed
218
static bool time_slider_handler(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer user_data)
219
{
Cheng Sun's avatar
Cheng Sun committed
220
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
221 222 223 224
    libvlc_media_player_set_position(plugin->getMD(), value/100.0);
    return false;
}

Cheng Sun's avatar
Cheng Sun committed
225
static bool vol_slider_handler(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer user_data)
226
{
Cheng Sun's avatar
Cheng Sun committed
227
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
228 229
    libvlc_audio_set_volume(plugin->getMD(), value);
    return false;
230 231
}

232 233 234 235 236 237
static void fullscreen_win_visibility_handler(GtkWidget *widget, gpointer user_data)
{
    VlcPluginGtk *plugin = (VlcPluginGtk *) user_data;
    plugin->set_fullscreen(gtk_widget_get_visible(widget));
}

238 239
void VlcPluginGtk::update_controls()
{
240 241 242 243 244 245 246
    GtkToolItem *toolbutton;

    /* play/pause button */
    const gchar *stock_id = playlist_isplaying() ? GTK_STOCK_MEDIA_PAUSE : GTK_STOCK_MEDIA_PLAY;
    toolbutton = gtk_toolbar_get_nth_item(GTK_TOOLBAR(toolbar), 0);
    if (strcmp(gtk_tool_button_get_stock_id(GTK_TOOL_BUTTON(toolbutton)), stock_id)) {
        gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(toolbutton), stock_id);
Cheng Sun's avatar
Cheng Sun committed
247
        /* work around firefox not displaying the icon properly after change */
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
        g_object_ref(toolbutton);
        gtk_container_remove(GTK_CONTAINER(toolbar), GTK_WIDGET(toolbutton));
        gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolbutton, 0);
        g_object_unref(toolbutton);
    }

    /* time slider */
    if (!libvlc_media_player ||
            !libvlc_media_player_is_seekable(libvlc_media_player)) {
        gtk_widget_set_sensitive(time_slider, false);
        gtk_range_set_value(GTK_RANGE(time_slider), 0);
    } else {
        gtk_widget_set_sensitive(time_slider, true);
        gdouble timepos = 100*libvlc_media_player_get_position(libvlc_media_player);
        gtk_range_set_value(GTK_RANGE(time_slider), timepos);
    }

    gtk_widget_show_all(toolbar);
266 267 268 269 270 271 272 273 274 275 276 277 278 279
}

bool VlcPluginGtk::create_windows()
{
    Window socket = (Window) npwindow.window;
    GdkColor color_black;
    gdk_color_parse("black", &color_black);

    parent = gtk_plug_new(socket);
    gtk_widget_modify_bg(parent, GTK_STATE_NORMAL, &color_black);

    parent_vbox = gtk_vbox_new(false, 0);
    gtk_container_add(GTK_CONTAINER(parent), parent_vbox);

280 281 282
    video_container = gtk_drawing_area_new();
    gtk_widget_modify_bg(video_container, GTK_STATE_NORMAL, &color_black);
    gtk_widget_add_events(video_container,
283 284
            GDK_BUTTON_PRESS_MASK
          | GDK_BUTTON_RELEASE_MASK);
285 286 287
    g_signal_connect(G_OBJECT(video_container), "button-press-event", G_CALLBACK(video_button_handler), this);
    g_signal_connect(G_OBJECT(video_container), "popup-menu", G_CALLBACK(video_popup_handler), this);
    gtk_box_pack_start(GTK_BOX(parent_vbox), video_container, true, true, 0);
288 289 290

    gtk_widget_show_all(parent);

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
    /* fullscreen top-level */
    fullscreen_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_decorated(GTK_WINDOW(fullscreen_win), false);
    g_signal_connect(G_OBJECT(fullscreen_win), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), this);
    g_signal_connect(G_OBJECT(fullscreen_win), "show", G_CALLBACK(fullscreen_win_visibility_handler), this);
    g_signal_connect(G_OBJECT(fullscreen_win), "hide", G_CALLBACK(fullscreen_win_visibility_handler), this);

    /* actual video window */
    /* libvlc is handed this window's xid. A raw X window is used because
     * GTK+ is incapable of reparenting without changing xid
     */
    Display *display = get_display();
    int blackColor = BlackPixel(display, DefaultScreen(display));
    video_xwindow = XCreateSimpleWindow(display, get_xid(video_container), 0, 0,
                   1, 1,
                   0, blackColor, blackColor);
    XMapWindow(display, video_xwindow);

    /* connect video_container resizes to video_xwindow */
    video_container_size_handler_id = g_signal_connect(
                G_OBJECT(video_container), "size-allocate",
                G_CALLBACK(video_size_handler), this);
    gtk_widget_queue_resize_no_redraw(video_container);
Cheng Sun's avatar
Cheng Sun committed
314 315 316

    /*** TOOLBAR ***/

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
    toolbar = gtk_toolbar_new();
    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
    GtkToolItem *toolitem;
    /* play/pause */
    toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
    g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(toolbar_handler), this);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
    /* stop */
    toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
    g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(toolbar_handler), this);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);

    /* time slider */
    toolitem = gtk_tool_item_new();
    time_slider = gtk_hscale_new_with_range(0, 100, 10);
    gtk_scale_set_draw_value(GTK_SCALE(time_slider), false);
    g_signal_connect(G_OBJECT(time_slider), "change-value", G_CALLBACK(time_slider_handler), this);
    gtk_container_add(GTK_CONTAINER(toolitem), time_slider);
    gtk_tool_item_set_expand(toolitem, true);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
    
    /* volume slider */
    toolitem = gtk_tool_item_new();
    GtkWidget *vol_slider = gtk_hscale_new_with_range(0, 200, 10);
    gtk_scale_set_draw_value(GTK_SCALE(vol_slider), false);
    g_signal_connect(G_OBJECT(vol_slider), "change-value", G_CALLBACK(vol_slider_handler), this);
    gtk_range_set_value(GTK_RANGE(vol_slider), 100);
    gtk_widget_set_size_request(vol_slider, 100, -1);
    gtk_container_add(GTK_CONTAINER(toolitem), vol_slider);
    gtk_tool_item_set_expand(toolitem, false);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);

    update_controls();
    show_toolbar();

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    return true;
}

bool VlcPluginGtk::resize_windows()
{
    GtkRequisition req;
    req.width = npwindow.width;
    req.height = npwindow.height;
    gtk_widget_size_request(parent, &req);
}

bool VlcPluginGtk::destroy_windows()
{
    /* TODO */
}