[vlc-devel] [PATCH 02/10] Extensions: Lua implementation for dialogs

Jean-Philippe André jpeg at videolan.org
Thu Nov 12 00:08:02 CET 2009


New vlc.dialog library, that allows users to create scriptable dialogs.
Works only when used with extensions (see following commits).
---
 modules/misc/lua/libs/dialog.c |  958 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 958 insertions(+), 0 deletions(-)
 create mode 100644 modules/misc/lua/libs/dialog.c

diff --git a/modules/misc/lua/libs/dialog.c b/modules/misc/lua/libs/dialog.c
new file mode 100644
index 0000000..ec27844
--- /dev/null
+++ b/modules/misc/lua/libs/dialog.c
@@ -0,0 +1,958 @@
+/*****************************************************************************
+ * dialog.c: Functions to create interface dialogs from Lua extensions
+ *****************************************************************************
+ * Copyright (C) 2007-2008 the VideoLAN team
+ * $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 );
+
+/* Interactions */
+static int vlclua_dialog_event_callback( extensions_manager_t *p_mgr,
+                                         extension_dialog_event_e type,
+                                         extension_dialog_t *p_dialog,
+                                         extension_widget_t *p_widget );
+
+/* 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( extensions_manager_t *p_mgr,
+                            extension_dialog_t *p_dialog );
+
+// Note: other scripts assume this can't fail. Btw this protection isn't enough
+#define GET_THIS( a ) \
+    extensions_manager_t *a = (extensions_manager_t*) vlclua_get_this( L ); \
+    if( !a ) \
+    { \
+        printf( "Something very bad happened with the script.\n" \
+                "Object 'this' could not be found! (%s:%d)\n", \
+                __FILE__, __LINE__ ); \
+        return VLC_EGENERIC; \
+    }
+
+/*
+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_LIST:
+case EXTENSION_WIDGET_CHECK_BOX:
+case EXTENSION_WIDGET_IMAGE:
+*/
+
+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 }
+};
+
+void luaopen_dialog( lua_State *L )
+{
+    lua_getglobal( L, "vlc" );
+    lua_pushcfunction( L, vlclua_dialog_create );
+    lua_setfield( L, -2, "dialog" );
+}
+
+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 );
+
+    GET_THIS( p_mgr );
+
+    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->psz_title = strdup( psz_title );
+    p_dlg->p_ext = vlclua_extension_get( L );
+    p_dlg->b_kill = false;
+    p_dlg->pf_event = vlclua_dialog_event_callback;
+
+    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_mgr, "Creating dialog '%s'", psz_title);
+
+    int i_ret = SignalInterface( p_mgr, p_dlg );
+
+    return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
+}
+
+static int vlclua_dialog_delete( lua_State *L )
+{
+    GET_THIS( p_mgr );
+
+    /* 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_mgr, "Deleting dialog '%s'", p_dlg->psz_title );
+    p_dlg->b_kill = true;
+    SignalInterface( p_mgr, p_dlg );
+
+    /* SignalInterface must take the lock asap and the UI signals us
+       when it's done deleting the dialog */
+    int i_retries = 0;
+    while( p_dlg->p_sys_intf != NULL )
+    {
+        if( ++i_retries > 20 )
+        {
+            /* 2s after SignalIntf, the dialog is still open. In order to avoid
+               unnecessary crashes, I prefer to leak the dialog. */
+            msg_Dbg( p_mgr, "Could not delete the dialog properly in time. "
+                     "Leaking its descriptor." );
+            return 1;
+        }
+        mtime_t abstime = mdate() + 100000;
+        vlc_mutex_lock( &p_dlg->lock );
+        vlc_cond_timedwait( &p_dlg->cond, &p_dlg->lock, abstime );
+        vlc_mutex_unlock( &p_dlg->lock );
+    }
+
+    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 )
+{
+    GET_THIS( p_mgr );
+
+    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_mgr, p_dlg );
+
+    return 1;
+}
+
+/** Hide the dialog */
+static int vlclua_dialog_hide( lua_State *L )
+{
+    GET_THIS( p_mgr );
+
+    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_mgr, 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 ) );
+
+    widget_sys_t *p_sys = calloc( 1, sizeof( widget_sys_t ) );
+    p_sys->psz_function = strdup( luaL_checkstring( L, 3 ) );
+    p_widget->p_sys = p_sys;
+
+    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 */
+    if( lua_isnumber( L, arg ) )
+        p_widget->i_column = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+    if( lua_isnumber( L, ++arg ) )
+        p_widget->i_row = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+    if( lua_isnumber( L, ++arg ) )
+        p_widget->i_horiz_span = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+    if( lua_isnumber( L, ++arg ) )
+        p_widget->i_vert_span = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+    if( lua_isnumber( L, ++arg ) )
+        p_widget->i_width = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+    if( lua_isnumber( L, ++arg ) )
+        p_widget->i_height = luaL_checkinteger( L, arg );
+    else goto end_of_args;
+
+end_of_args:
+    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 */
+    GET_THIS( p_mgr );
+    int i_ret = SignalInterface( p_mgr, 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 */
+    GET_THIS( p_mgr );
+    SignalInterface( p_mgr, 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;
+
+    GET_THIS( p_mgr );
+    SignalInterface( p_mgr, 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;
+
+    GET_THIS( p_mgr );
+    SignalInterface( p_mgr, 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;
+
+        GET_THIS( p_mgr );
+        SignalInterface( p_mgr, 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;
+
+    GET_THIS( p_mgr );
+
+    p_widget->b_kill = true;
+
+    int i_ret = SignalInterface( p_mgr, p_dlg );
+
+    if( i_ret != VLC_SUCCESS )
+    {
+        return luaL_error( L, "Could not delete widget" );
+    }
+
+    vlc_mutex_lock( &p_dlg->lock );
+    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" );
+    }
+
+    vlc_mutex_unlock( &p_dlg->lock );
+
+    return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
+}
+
+/**
+ * Handle an event coming from the UI
+ * @param type Type of interaction: click, close...
+ * @param p_widget can be NULL
+ **/
+static int vlclua_dialog_event_callback( extensions_manager_t *p_mgr,
+                                         extension_dialog_event_e type,
+                                         extension_dialog_t *p_dialog,
+                                         extension_widget_t *p_widget )
+{
+    assert( p_dialog );
+    assert( p_mgr );
+
+    switch( type )
+    {
+        case EXTENSION_EVENT_CLICK:
+            assert( p_widget );
+            PushCommand( p_dialog->p_ext, CMD_CLICK, p_widget );
+            break;
+        case EXTENSION_EVENT_CLOSE:
+            PushCommand( p_dialog->p_ext, CMD_CLOSE );
+            break;
+        default:
+            msg_Dbg(p_mgr, "Received unknown UI event %d, discarded", type);
+            break;
+    }
+
+    return 1;
+}
+
+
+/*
+ * 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;
+    }
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * Add a widget to the widget list of a dialog
+ * @note Must be entered without lock
+ **/
+static int SignalInterface( extensions_manager_t *p_mgr,
+                            extension_dialog_t *p_dialog )
+{
+    assert( p_mgr );
+    vlc_object_t *p_dp = extension_GetDialogProvider( p_mgr );
+    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.2




More information about the vlc-devel mailing list