[vlc-devel] [PATCH 2/8] Extensions: Lua implementation for dialogs

Jean-Philippe André jpeg at videolan.org
Tue Dec 22 23:44:09 CET 2009


New vlc.dialog library, that allows users to create scriptable dialogs.
These dialogs work with Extensions but also with other Lua scripts, as
long as Qt has declared itself as dialog provider (this is the case by
default)
---
 modules/misc/lua/libs.h        |    2 +-
 modules/misc/lua/libs/dialog.c |  930 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 931 insertions(+), 1 deletions(-)
 create mode 100644 modules/misc/lua/libs/dialog.c

diff --git a/modules/misc/lua/libs.h b/modules/misc/lua/libs.h
index 689025e..0f4a0ef 100644
--- a/modules/misc/lua/libs.h
+++ b/modules/misc/lua/libs.h
@@ -26,7 +26,7 @@
 
 void luaopen_acl( lua_State * );
 void luaopen_config( lua_State * );
-void luaopen_volume( lua_State * );
+void luaopen_dialog( lua_State *L, void *opaque );
 void luaopen_httpd( lua_State * );
 void luaopen_input( lua_State * );
 void luaopen_msg( lua_State * );
diff --git a/modules/misc/lua/libs/dialog.c b/modules/misc/lua/libs/dialog.c
new file mode 100644
index 0000000..7cc1239
--- /dev/null
+++ b/modules/misc/lua/libs/dialog.c
@@ -0,0 +1,930 @@
+/*****************************************************************************
+ * dialog.c: Functions to create interface dialogs from Lua extensions
+ *****************************************************************************
+ * Copyright (C) 2009 VideoLAN and authors
+ * $Id$
+ *
+ * Authors: Jean-Philippe André < jpeg # videolan.org >
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_extensions.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+
+#include "../vlc.h"
+#include "../libs.h"
+// #include "../extension.h"
+
+#include "assert.h"
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/* Dialog functions */
+static int vlclua_dialog_create( lua_State *L );
+static int vlclua_dialog_delete( lua_State *L );
+static int vlclua_dialog_show( lua_State *L );
+static int vlclua_dialog_hide( lua_State *L );
+
+static int vlclua_dialog_add_button( lua_State *L );
+static int vlclua_dialog_add_label( lua_State *L );
+static int vlclua_dialog_add_html( lua_State *L );
+static int vlclua_dialog_add_text_inner( lua_State *L, int );
+static inline int vlclua_dialog_add_text_input( lua_State *L )
+{
+    return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_TEXT_FIELD );
+}
+static inline int vlclua_dialog_add_password( lua_State *L )
+{
+    return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_PASSWORD );
+}
+static inline int vlclua_dialog_add_html( lua_State *L )
+{
+    return vlclua_dialog_add_text_inner( L, EXTENSION_WIDGET_HTML );
+}
+static int vlclua_dialog_add_check_box( lua_State *L );
+static int vlclua_dialog_add_list( lua_State *L );
+static int vlclua_dialog_add_dropdown( lua_State *L );
+static int vlclua_dialog_add_image( lua_State *L );
+static int vlclua_create_widget_inner( lua_State *L, int i_args,
+                                       extension_widget_t *p_widget);
+
+static int vlclua_dialog_delete_widget( lua_State *L );
+
+/* Widget methods */
+static int vlclua_widget_set_text( lua_State *L );
+static int vlclua_widget_get_text( lua_State *L );
+static int vlclua_widget_set_checked( lua_State *L );
+static int vlclua_widget_get_checked( lua_State *L );
+static int vlclua_widget_add_value( lua_State *L );
+static int vlclua_widget_get_value( lua_State *L );
+static int vlclua_widget_clear( lua_State *L );
+static int vlclua_widget_get_selection( lua_State *L );
+
+/* Helpers */
+static int AddWidget( extension_dialog_t *p_dialog,
+                      extension_widget_t *p_widget );
+static int DeleteWidget( extension_dialog_t *p_dialog,
+                         extension_widget_t *p_widget );
+static int SignalInterface( vlc_object_t *p_this,
+                            extension_dialog_t *p_dialog );
+
+static const luaL_Reg vlclua_dialog_reg[] = {
+    { "show", vlclua_dialog_show },
+    { "hide", vlclua_dialog_hide },
+    { "close", vlclua_dialog_delete },
+
+    { "add_button", vlclua_dialog_add_button },
+    { "add_label", vlclua_dialog_add_label },
+    { "add_html", vlclua_dialog_add_html },
+    { "add_text_input", vlclua_dialog_add_text_input },
+    { "add_password", vlclua_dialog_add_password },
+    { "add_check_box", vlclua_dialog_add_check_box },
+    { "add_dropdown", vlclua_dialog_add_dropdown },
+    { "add_list", vlclua_dialog_add_list },
+    { "add_image", vlclua_dialog_add_image },
+
+    { "del_widget", vlclua_dialog_delete_widget },
+    { NULL, NULL }
+};
+
+static const luaL_Reg vlclua_widget_reg[] = {
+    { "set_text", vlclua_widget_set_text },
+    { "get_text", vlclua_widget_get_text },
+    { "set_checked", vlclua_widget_set_checked },
+    { "get_checked", vlclua_widget_get_checked },
+    { "add_value", vlclua_widget_add_value },
+    { "get_value", vlclua_widget_get_value },
+    { "clear", vlclua_widget_clear },
+    { "get_selection", vlclua_widget_get_selection },
+    { NULL, NULL }
+};
+
+static const char key_opaque = 'A';
+
+/**
+ * Open dialog library for Lua
+ * @param L lua_State
+ * @param opaque Object associated to this lua state
+ * @note opaque will be p_ext for extensions, p_sd for service discoveries
+ */
+void luaopen_dialog( lua_State *L, void *opaque )
+{
+    lua_getglobal( L, "vlc" );
+    lua_pushcfunction( L, vlclua_dialog_create );
+    lua_setfield( L, -2, "dialog" );
+
+    /* Add a private pointer (opaque) in the registry
+     * The &key pointer is used to have a unique entry in the registry
+     */
+    lua_pushlightuserdata( L, (void*) &key_opaque );
+    lua_pushlightuserdata( L, opaque );
+    lua_settable( L, LUA_REGISTRYINDEX );
+}
+
+static int vlclua_dialog_create( lua_State *L )
+{
+    if( !lua_isstring( L, 1 ) )
+        return luaL_error( L, "vlc.dialog() usage: (title)" );
+    const char *psz_title = luaL_checkstring( L, 1 );
+
+    vlc_object_t *p_this = vlclua_get_this( L );
+
+    extension_dialog_t *p_dlg = calloc( 1, sizeof( extension_dialog_t ) );
+    if( !p_dlg )
+        return 0; // luaL_error( L, "Out Of Memory" );
+
+    lua_getglobal( L, "vlc" );
+    lua_getfield( L, -1, "__dialog" );
+    if( lua_topointer( L, lua_gettop( L ) ) != NULL )
+        return luaL_error( L, "Only one dialog allowed per extension!" );
+
+    p_dlg->p_object = p_this;
+    p_dlg->psz_title = strdup( psz_title );
+    p_dlg->b_kill = false;
+
+    /* Read the opaque value stored while loading the dialog library */
+    lua_pushlightuserdata( L, (void*) &key_opaque );
+    lua_gettable( L, LUA_REGISTRYINDEX );
+    p_dlg->p_sys = (void*) lua_topointer( L, -1 ); // "const" discarded
+    lua_pop( L, 1 );
+
+    vlc_mutex_init( &p_dlg->lock );
+    vlc_cond_init( &p_dlg->cond );
+
+    /** @todo Use the registry instead of __dialog,
+        so that the user can't tamper with it */
+
+    lua_getglobal( L, "vlc" );
+    lua_pushlightuserdata( L, p_dlg );
+    lua_setfield( L, -2, "__dialog" );
+    lua_pop( L, 1 );
+
+    extension_dialog_t **pp_dlg = lua_newuserdata( L, sizeof( void* ) );
+    *pp_dlg = p_dlg;
+
+    if( luaL_newmetatable( L, "dialog" ) )
+    {
+        lua_newtable( L );
+        luaL_register( L, NULL, vlclua_dialog_reg );
+        lua_setfield( L, -2, "__index" );
+        lua_pushcfunction( L, vlclua_dialog_delete );
+        lua_setfield( L, -2, "__gc" );
+    }
+
+    lua_setmetatable( L, -2 );
+
+    msg_Dbg( p_this, "Creating dialog '%s'", psz_title );
+
+    int i_ret = SignalInterface( p_this, p_dlg );
+
+    return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
+}
+
+static int vlclua_dialog_delete( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+
+    /* Get dialog descriptor */
+    extension_dialog_t **pp_dlg =
+            (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
+
+    if( !pp_dlg || !*pp_dlg )
+        return luaL_error( L, "Can't get pointer to dialog" );
+
+    extension_dialog_t *p_dlg = *pp_dlg;
+    *pp_dlg = NULL;
+
+    /* Remove private __dialog field */
+    lua_getglobal( L, "vlc" );
+    lua_pushnil( L );
+    lua_setfield( L, -2, "__dialog" );
+
+    assert( !p_dlg->b_kill );
+
+    msg_Dbg( p_this, "Deleting dialog '%s'", p_dlg->psz_title );
+    p_dlg->b_kill = true;
+    SignalInterface( p_this, p_dlg );
+
+    /* After SignalInterface, the UI thread must take the lock asap and
+     * then signal us when it's done deleting the dialog.
+     * Note: Wait up to 2s.
+     */
+    mtime_t abstime = mdate() + 2000000;
+    vlc_mutex_lock( &p_dlg->lock );
+    if( p_dlg->p_sys_intf != NULL )
+        vlc_cond_timedwait( &p_dlg->cond, &p_dlg->lock, abstime );
+    vlc_mutex_unlock( &p_dlg->lock );
+    if( p_dlg->p_sys_intf != NULL )
+    {
+        /* 2s after SignalIntf, the dialog is still open. In order to avoid
+         * unnecessary crashes, we leak the dialog.
+         * This case should not happen (and does not in normal conditions).
+         */
+        msg_Warn( p_this, "Could not delete the dialog properly in 2s. "
+                  "Leaking its descriptor." );
+        p_dlg->p_sys_intf = NULL;
+        return 1;
+    }
+
+    free( p_dlg->psz_title );
+    p_dlg->psz_title = NULL;
+
+    /* Destroy widgets */
+    for( int a = 0; a < p_dlg->i_num_widgets; a++ )
+    {
+        extension_widget_t *p_widget = p_dlg->pp_widgets[a];
+        if( !p_widget ) continue;
+        free( p_widget->psz_text );
+
+        /* Free data */
+        struct extension_widget_value_t *p_value, *p_next;
+        for( p_value = p_widget->p_values; p_value != NULL; p_value = p_next )
+        {
+            p_next = p_value->p_next;
+            free( p_value->psz_text );
+            free( p_value );
+        }
+    }
+    free( p_dlg->pp_widgets );
+    p_dlg->pp_widgets = NULL;
+
+    /* Note: At this point, the UI must not use these resources */
+    vlc_mutex_destroy( &p_dlg->lock );
+    vlc_cond_destroy( &p_dlg->cond );
+
+    return 1;
+}
+
+/** Show the dialog */
+static int vlclua_dialog_show( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+
+    extension_dialog_t **pp_dlg =
+            (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
+    if( !pp_dlg || !*pp_dlg )
+        return luaL_error( L, "Can't get pointer to dialog" );
+    extension_dialog_t *p_dlg = *pp_dlg;
+
+    p_dlg->b_hide = false;
+    SignalInterface( p_this, p_dlg );
+
+    return 1;
+}
+
+/** Hide the dialog */
+static int vlclua_dialog_hide( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+
+    extension_dialog_t **pp_dlg =
+            (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
+    if( !pp_dlg || !*pp_dlg )
+        return luaL_error( L, "Can't get pointer to dialog" );
+    extension_dialog_t *p_dlg = *pp_dlg;
+
+    p_dlg->b_hide = true;
+    SignalInterface( p_this, p_dlg );
+
+    return 1;
+}
+
+
+/**
+ * Create a button: add_button
+ * Arguments: text, function (as string)
+ * Qt: QPushButton
+ **/
+static int vlclua_dialog_add_button( lua_State *L )
+{
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) || !lua_isstring( L, 3 ) )
+        return luaL_error( L, "dialog:add_button usage: (text, func)" );
+
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_BUTTON;
+    p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+    p_widget->p_sys = strdup( luaL_checkstring( L, 3 ) );
+
+    return vlclua_create_widget_inner( L, 2, p_widget );
+}
+
+/**
+ * Create a text label: add_label
+ * Arguments: text
+ * Qt: QLabel
+ **/
+static int vlclua_dialog_add_label( lua_State *L )
+{
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) )
+        return luaL_error( L, "dialog:add_label usage: (text)" );
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_LABEL;
+    p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+
+    return vlclua_create_widget_inner( L, 1, p_widget );
+}
+
+/**
+ * Create a text area: add_html, add_text_input, add_password
+ * Arguments: text (may be nil)
+ * Qt: QLineEdit (Text/Password) or QTextArea (HTML)
+ **/
+static int vlclua_dialog_add_text_inner( lua_State *L, int i_type )
+{
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) && !lua_isnil( L, 2 ) )
+        return luaL_error( L, "dialog:add_text_input usage: (text = nil)" );
+
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = i_type;
+    if( !lua_isnil( L, 2 ) )
+        p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+
+    return vlclua_create_widget_inner( L, 1, p_widget );
+}
+
+/**
+ * Create a checkable box: add_check_box
+ * Arguments: text, checked (as bool)
+ * Qt: QCheckBox
+ **/
+static int vlclua_dialog_add_check_box( lua_State *L )
+{
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) )
+        return luaL_error( L, "dialog:add_check_box usage: (text, checked)" );
+
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_CHECK_BOX;
+    p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+    p_widget->b_checked = lua_toboolean( L, 3 );
+
+    return vlclua_create_widget_inner( L, 2, p_widget );
+}
+
+/**
+ * Create a drop-down list (non editable)
+ * Arguments: (none)
+ * Qt: QComboBox
+ * @todo make it editable?
+ **/
+static int vlclua_dialog_add_dropdown( lua_State *L )
+{
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_DROPDOWN;
+
+    return vlclua_create_widget_inner( L, 0, p_widget );
+}
+
+/**
+ * Create a list panel (multiple selection)
+ * Arguments: (none)
+ * Qt: QListWidget
+ **/
+static int vlclua_dialog_add_list( lua_State *L )
+{
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_LIST;
+
+    return vlclua_create_widget_inner( L, 0, p_widget );
+}
+
+/**
+ * Create an image label
+ * Arguments: url
+ * Qt: QLabel with setPixmap( QPixmap& )
+ **/
+static int vlclua_dialog_add_image( lua_State *L )
+{
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) )
+        return luaL_error( L, "dialog:add_image usage: (filename)" );
+
+    extension_widget_t *p_widget = calloc( 1, sizeof( extension_widget_t ) );
+    p_widget->type = EXTENSION_WIDGET_IMAGE;
+    p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+
+    return vlclua_create_widget_inner( L, 1, p_widget );
+}
+
+/**
+ * Internal helper to finalize the creation of a widget
+ * @param L Lua State
+ * @param i_args Number of arguments before "row" (0 or more)
+ * @param p_widget The widget to add
+ **/
+static int vlclua_create_widget_inner( lua_State *L, int i_args,
+                                       extension_widget_t *p_widget )
+{
+    int arg = i_args + 2;
+
+    /* Get dialog */
+    extension_dialog_t **pp_dlg =
+            (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
+    if( !pp_dlg || !*pp_dlg )
+        return luaL_error( L, "Can't get pointer to dialog" );
+    extension_dialog_t *p_dlg = *pp_dlg;
+
+    /* Set parent dialog */
+    p_widget->p_dialog = p_dlg;
+
+    /* Set common arguments: col, row, hspan, vspan, width, height */
+    p_widget->i_column = luaL_optint( L, arg++, 0 );
+    p_widget->i_row = luaL_optint( L, arg++, 0 );
+    p_widget->i_horiz_span = luaL_optint( L, arg++, 0 );
+    p_widget->i_vert_span = luaL_optint( L, arg++, 0 );
+    p_widget->i_width = luaL_optint( L, arg++, 0 );
+    p_widget->i_height = luaL_optint( L, arg, 0 );
+
+    vlc_mutex_lock( &p_dlg->lock );
+
+    /* Add the widget to the dialog descriptor */
+    if( AddWidget( p_dlg, p_widget ) != VLC_SUCCESS )
+    {
+        vlc_mutex_unlock( &p_dlg->lock );
+        lua_pushnil( L );
+        return 0;
+    }
+
+    vlc_mutex_unlock( &p_dlg->lock );
+
+    /* Create meta table */
+    extension_widget_t **pp_widget = lua_newuserdata( L, sizeof( void* ) );
+    *pp_widget = p_widget;
+    if( luaL_newmetatable( L, "widget" ) )
+    {
+        lua_newtable( L );
+        luaL_register( L, NULL, vlclua_widget_reg );
+        lua_setfield( L, -2, "__index" );
+    }
+    lua_setmetatable( L, -2 );
+
+    /* Signal interface */
+    vlc_object_t *p_this = vlclua_get_this( L );
+    int i_ret = SignalInterface( p_this, p_dlg );
+
+    return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
+}
+
+static int vlclua_widget_set_text( lua_State *L )
+{
+    /* Get dialog */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    /* Verify arguments */
+    if( !lua_isstring( L, 2 ) )
+        return luaL_error( L, "widget:set_text usage: (text)" );
+
+    /* Verify widget type */
+    switch( p_widget->type )
+    {
+        case EXTENSION_WIDGET_LABEL:
+        case EXTENSION_WIDGET_BUTTON:
+        case EXTENSION_WIDGET_HTML:
+        case EXTENSION_WIDGET_TEXT_FIELD:
+        case EXTENSION_WIDGET_PASSWORD:
+        case EXTENSION_WIDGET_DROPDOWN:
+        case EXTENSION_WIDGET_CHECK_BOX:
+            break;
+        case EXTENSION_WIDGET_LIST:
+        case EXTENSION_WIDGET_IMAGE:
+        default:
+            return luaL_error( L, "method set_text not valid for this widget" );
+    }
+
+    /* Update widget */
+    p_widget->b_update = true;
+    free( p_widget->psz_text );
+    p_widget->psz_text = strdup( luaL_checkstring( L, 2 ) );
+
+    /* Update dialog */
+    vlc_object_t *p_this = vlclua_get_this( L );
+    SignalInterface( p_this, p_widget->p_dialog );
+
+    return 1;
+}
+
+static int vlclua_widget_get_text( lua_State *L )
+{
+    /* Get dialog */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    /* Verify widget type */
+    switch( p_widget->type )
+    {
+        case EXTENSION_WIDGET_LABEL:
+        case EXTENSION_WIDGET_BUTTON:
+        case EXTENSION_WIDGET_HTML:
+        case EXTENSION_WIDGET_TEXT_FIELD:
+        case EXTENSION_WIDGET_PASSWORD:
+        case EXTENSION_WIDGET_DROPDOWN:
+        case EXTENSION_WIDGET_CHECK_BOX:
+            break;
+        case EXTENSION_WIDGET_LIST:
+        case EXTENSION_WIDGET_IMAGE:
+        default:
+            return luaL_error( L, "method get_text not valid for this widget" );
+    }
+
+    extension_dialog_t *p_dlg = p_widget->p_dialog;
+
+    char *psz_text = NULL;
+    vlc_mutex_lock( &p_dlg->lock );
+    if( p_widget->psz_text )
+        psz_text = strdup( p_widget->psz_text );
+    vlc_mutex_unlock( &p_dlg->lock );
+
+    lua_pushstring( L, psz_text );
+
+    free( psz_text );
+    return 1;
+}
+
+static int vlclua_widget_get_checked( lua_State *L )
+{
+    /* Get widget */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
+        return luaL_error( L, "method get_checked not valid for this widget" );
+
+    lua_pushboolean( L, p_widget->b_checked );
+    return 1;
+}
+
+static int vlclua_widget_add_value( lua_State *L )
+{
+    /* Get widget */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_DROPDOWN
+        && p_widget->type != EXTENSION_WIDGET_LIST )
+        return luaL_error( L, "method add_value not valid for this widget" );
+
+    if( !lua_isstring( L, 2 ) )
+        return luaL_error( L, "widget:add_value usage: (text, id = 0)" );
+
+    struct extension_widget_value_t *p_value,
+        *p_new_value = calloc( 1, sizeof( struct extension_widget_value_t ) );
+    p_new_value->psz_text = strdup( luaL_checkstring( L, 2 ) );
+    p_new_value->i_id = lua_tointeger( L, 3 );
+
+    if( !p_widget->p_values )
+    {
+        p_widget->p_values = p_new_value;
+    }
+    else
+    {
+        for( p_value = p_widget->p_values;
+             p_value->p_next != NULL;
+             p_value = p_value->p_next )
+        { /* Do nothing, iterate to find the end */ }
+        p_value->p_next = p_new_value;
+    }
+
+    p_widget->b_update = true;
+
+    vlc_object_t *p_this = vlclua_get_this( L );
+    SignalInterface( p_this, p_widget->p_dialog );
+
+    return 1;
+}
+
+static int vlclua_widget_get_value( lua_State *L )
+{
+    /* Get widget */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_DROPDOWN )
+        return luaL_error( L, "method get_value not valid for this widget" );
+
+    struct extension_widget_value_t *p_value;
+    for( p_value = p_widget->p_values;
+         p_value != NULL;
+         p_value = p_value->p_next )
+    {
+        if( p_value->b_selected )
+        {
+            lua_pushinteger( L, p_value->i_id );
+            lua_pushstring( L, p_value->psz_text );
+            return 2;
+        }
+    }
+
+    lua_pushinteger( L, -1 );
+    lua_pushnil( L );
+    return 2;
+}
+
+static int vlclua_widget_clear( lua_State *L )
+{
+    /* Get widget */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_DROPDOWN
+        && p_widget->type != EXTENSION_WIDGET_LIST )
+        return luaL_error( L, "method add_value not valid for this widget" );
+
+    struct extension_widget_value_t *p_value, *p_next;
+
+    for( p_value = p_widget->p_values;
+         p_value != NULL;
+         p_value = p_next )
+    {
+        p_next = p_value->p_next;
+        free( p_value->psz_text );
+        free( p_value );
+    }
+
+    p_widget->p_values = NULL;
+    p_widget->b_update = true;
+
+    vlc_object_t *p_this = vlclua_get_this( L );
+    SignalInterface( p_this, p_widget->p_dialog );
+
+    return 1;
+}
+
+static int vlclua_widget_get_selection( lua_State *L )
+{
+    /* Get widget */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_LIST )
+        return luaL_error( L, "method get_selection not valid for this widget" );
+
+    /* Create empty table */
+    lua_newtable( L );
+
+    struct extension_widget_value_t *p_value;
+    for( p_value = p_widget->p_values;
+         p_value != NULL;
+         p_value = p_value->p_next )
+    {
+        if( p_value->b_selected )
+        {
+            lua_pushinteger( L, p_value->i_id );
+            lua_pushstring( L, p_value->psz_text );
+            lua_settable( L, -3 );
+        }
+    }
+
+    return 1;
+}
+
+
+static int vlclua_widget_set_checked( lua_State *L )
+{
+    /* Get dialog */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 1, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    if( p_widget->type != EXTENSION_WIDGET_CHECK_BOX )
+        return luaL_error( L, "method set_checked not valid for this widget" );
+
+    /* Verify arguments */
+    if( !lua_isboolean( L, 2 ) )
+        return luaL_error( L, "widget:set_checked usage: (bool)" );
+
+    bool b_old_check = p_widget->b_checked;
+    p_widget->b_checked = lua_toboolean( L, 2 );
+
+    if( b_old_check != p_widget->b_checked )
+    {
+        /* Signal interface of the change */
+        p_widget->b_update = true;
+
+        vlc_object_t *p_this = vlclua_get_this( L );
+        SignalInterface( p_this, p_widget->p_dialog );
+    }
+
+    return 1;
+}
+
+/**
+ * Delete a widget from a dialog
+ * Remove it from the list once it has been safely destroyed by the interface
+ **/
+static int vlclua_dialog_delete_widget( lua_State *L )
+{
+    /* Get dialog */
+    extension_dialog_t **pp_dlg =
+            (extension_dialog_t**) luaL_checkudata( L, 1, "dialog" );
+    if( !pp_dlg || !*pp_dlg )
+        return luaL_error( L, "Can't get pointer to dialog" );
+    extension_dialog_t *p_dlg = *pp_dlg;
+
+    /* Get widget */
+    if( !lua_isuserdata( L, 2 ) )
+        return luaL_error( L, "Argument to del_widget is not a widget" );
+
+    /* Get dialog */
+    extension_widget_t **pp_widget =
+            (extension_widget_t **) luaL_checkudata( L, 2, "widget" );
+    if( !pp_widget || !*pp_widget )
+        return luaL_error( L, "Can't get pointer to widget" );
+    extension_widget_t *p_widget = *pp_widget;
+
+    /* Delete widget */
+    *pp_widget = NULL;
+
+    vlc_object_t *p_this = vlclua_get_this( L );
+
+    p_widget->b_kill = true;
+
+    int i_ret = SignalInterface( p_this, p_dlg );
+
+    if( i_ret != VLC_SUCCESS )
+    {
+        return luaL_error( L, "Could not delete widget" );
+    }
+
+    vlc_mutex_lock( &p_dlg->lock );
+
+    /* Same remarks as for dialog delete */
+    mtime_t abstime = mdate() + 2000000;
+    if( p_widget->p_sys_intf != NULL )
+        vlc_cond_timedwait( &p_dlg->cond, &p_dlg->lock, abstime );
+
+    if( p_widget->p_sys_intf == NULL )
+    {
+        i_ret = DeleteWidget( p_dlg, p_widget );
+
+        if( i_ret != VLC_SUCCESS )
+        {
+            vlc_mutex_unlock( &p_dlg->lock );
+            return luaL_error( L, "Could not remove widget from list" );
+        }
+    }
+    else
+    {
+        msg_Warn( p_this, "Could not delete a widget. Leaking its descriptor." );
+        i_ret = VLC_EGENERIC;
+    }
+
+    vlc_mutex_unlock( &p_dlg->lock );
+
+    return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
+}
+
+/*
+ * Below this line, no Lua specific code.
+ * Extension helpers.
+ */
+
+
+/**
+ * Add a widget to the widget list of a dialog
+ * @note Must be entered with lock on dialog
+ **/
+static int AddWidget( extension_dialog_t *p_dialog,
+                      extension_widget_t *p_widget )
+{
+    extension_widget_t **pp_widgets;
+    pp_widgets = realloc( p_dialog->pp_widgets,
+                          ( p_dialog->i_num_widgets + 1 ) * sizeof(void*) );
+    if( !pp_widgets )
+        return VLC_ENOMEM;
+    pp_widgets[p_dialog->i_num_widgets] = p_widget;
+    p_dialog->pp_widgets = pp_widgets;
+    p_dialog->i_num_widgets++;
+    return VLC_SUCCESS;
+}
+
+/**
+ * Remove a widget from the widget list of a dialog
+ * @note The widget MUST have been safely killed before
+ * @note Must be entered with lock on dialog
+ **/
+static int DeleteWidget( extension_dialog_t *p_dialog,
+                         extension_widget_t *p_widget )
+{
+    extension_widget_t **pp_iter = NULL;
+    int i_idx = 0;
+    for( int i = 0; i < p_dialog->i_num_widgets; i++ )
+    {
+        if( p_dialog->pp_widgets[i] == p_widget )
+        {
+            pp_iter = &( p_dialog->pp_widgets[i] );
+            i_idx = i;
+        }
+    }
+
+    if( !pp_iter )
+        return VLC_EGENERIC;
+
+    /* Delete entry in the array */
+    *pp_iter = NULL;
+
+    /* Resize array if it's easy to do */
+    bool b_downsize = true;
+    for( int j = i_idx + 1; j < p_dialog->i_num_widgets; j++ )
+    {
+        if( p_dialog->pp_widgets[j] != NULL )
+        {
+            b_downsize = false;
+            break;
+        }
+    }
+
+    if( b_downsize )
+    {
+        /* Downsize can't fail */
+        p_dialog->pp_widgets = realloc_down( p_dialog->pp_widgets,
+                                             i_idx * sizeof( void* ) );
+        p_dialog->i_num_widgets = i_idx;
+    }
+
+    /* Now free the data */
+    free( p_widget->p_sys );
+    struct extension_widget_value_t *p_value = p_widget->p_values;
+    while( p_value )
+    {
+        free( p_value->psz_text );
+        struct extension_widget_value_t *old = p_value;
+        p_value = p_value->p_next;
+        free( old );
+    }
+    free( p_widget->psz_text );
+    free( p_widget );
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * Add a widget to the widget list of a dialog
+ * @note Must be entered without lock
+ **/
+static int SignalInterface( vlc_object_t *p_this,
+                            extension_dialog_t *p_dialog )
+{
+    vlc_object_t *p_dp = extension_GetDialogProvider( p_this );
+    if( !p_dp )
+        return VLC_EGENERIC;
+    var_SetAddress( p_dp, "dialog-extension", p_dialog );
+    vlc_object_release( p_dp );
+    return VLC_SUCCESS;
+}
+
-- 
1.6.5.3






More information about the vlc-devel mailing list