variables.c 11.3 KB
Newer Older
1
/*****************************************************************************
2
 * variables.c: Generic lua<->vlc variables interface
3
 *****************************************************************************
4
 * Copyright (C) 2007-2010 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea at videolan tod 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

31 32 33 34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35 36
#include <math.h>

37
#include <vlc_common.h>
38

39 40 41 42 43 44 45 46
#include "../vlc.h"
#include "../libs.h"
#include "variables.h"
#include "objects.h"

/*****************************************************************************
 * Variables handling
 *****************************************************************************/
47
static int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val )
48
{
Rémi Duraffort's avatar
Rémi Duraffort committed
49
    switch( i_type & VLC_VAR_CLASS )
50 51 52 53 54
    {
        case VLC_VAR_BOOL:
            lua_pushboolean( L, val.b_bool );
            break;
        case VLC_VAR_INTEGER:
55 56 57 58 59 60 61 62 63 64 65 66 67 68
            /* Lua may only support 32-bit integers. If so, and the
             * value requires a higher range, push it as a float. We
             * lose some precision, but object variables are not a
             * recommended API for lua scripts: functionality requiring
             * high precision should be provided with a dedicated lua
             * binding instead of object variables.
             */
            // TODO: check using LUA_MININTEGER and LUA_MAXINTEGER macros
            // if and when we require lua >= 5.3
            if( sizeof( lua_Integer ) < sizeof( val.i_int ) &&
                ( val.i_int < INT32_MIN || INT32_MAX < val.i_int ) )
                lua_pushnumber( L, (lua_Number)val.i_int );
            else
                lua_pushinteger( L, val.i_int );
69 70 71 72 73 74 75 76 77 78
            break;
        case VLC_VAR_STRING:
            lua_pushstring( L, val.psz_string );
            break;
        case VLC_VAR_FLOAT:
            lua_pushnumber( L, val.f_float );
            break;
        case VLC_VAR_ADDRESS:
            vlclua_error( L );
            break;
79
        case VLC_VAR_VOID:
80 81 82 83 84 85
        default:
            vlclua_error( L );
    }
    return 1;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
86
static int vlclua_pushlist( lua_State *L, vlc_list_t *p_list )
87
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
88
    int i_count = p_list->i_count;
89 90 91 92 93

    lua_createtable( L, i_count, 0 );
    for( int i = 0; i < i_count; i++ )
    {
        lua_pushinteger( L, i+1 );
94
        if( !vlclua_pushvalue( L, p_list->i_type, p_list->p_values[i] ) )
95 96 97 98 99 100
             lua_pushnil( L );
        lua_settable( L, -3 );
    }
    return 1;
}

101 102
static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
{
103
    switch( i_type & VLC_VAR_CLASS )
104 105 106 107 108 109 110
    {
        case VLC_VAR_VOID:
            break;
        case VLC_VAR_BOOL:
            val->b_bool = luaL_checkboolean( L, -1 );
            break;
        case VLC_VAR_INTEGER:
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
            /* Lua may only support 32-bit integers. If so, we need to
             * get the value as a float instead so we can even know if
             * there would be an overflow.
             */
            // TODO: check using LUA_MININTEGER and LUA_MAXINTEGER macros
            // if and when we require lua >= 5.3
            if( sizeof( lua_Integer ) < sizeof( val->i_int ) )
            {
                lua_Number f = luaL_checknumber( L, -1 );
                // Calling vlc.var.set() on integer object variables with
                // an out-of-range float value is not handled.
                val->i_int = (int64_t)llround( f );
                if( INT32_MIN < val->i_int && val->i_int < INT32_MAX )
                    val->i_int = luaL_checkinteger( L, -1 );
            }
            else
                val->i_int = luaL_checkinteger( L, -1 );
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
            break;
        case VLC_VAR_STRING:
            val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
            break;
        case VLC_VAR_FLOAT:
            val->f_float = luaL_checknumber( L, -1 );
            break;
        case VLC_VAR_ADDRESS:
            vlclua_error( L );
            break;
        default:
            vlclua_error( L );
    }
    return 1;
}

Pierre Ynard's avatar
Pierre Ynard committed
144 145 146 147 148 149 150 151 152 153 154 155 156
static int vlclua_var_inherit( lua_State *L )
{
    vlc_value_t val;
    vlc_object_t *p_obj;
    if( lua_type( L, 1 ) == LUA_TNIL )
        p_obj = vlclua_get_this( L );
    else
    {
        vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
        p_obj = *pp_obj;
    }
    const char *psz_var = luaL_checkstring( L, 2 );

157
    int i_type = config_GetType( psz_var );
Pierre Ynard's avatar
Pierre Ynard committed
158 159 160 161
    if( var_Inherit( p_obj, psz_var, i_type, &val ) != VLC_SUCCESS )
        return 0;

    lua_pop( L, 2 );
162 163 164 165
    vlclua_pushvalue( L, i_type, val );
    if( i_type == VLC_VAR_STRING )
        free( val.psz_string );
    return 1;
Pierre Ynard's avatar
Pierre Ynard committed
166 167
}

168 169 170 171 172
static int vlclua_var_get( lua_State *L )
{
    vlc_value_t val;
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
173 174

    int i_type = var_Type( *pp_obj, psz_var );
175 176
    if( var_Get( *pp_obj, psz_var, &val ) != VLC_SUCCESS )
        return 0;
177

178
    lua_pop( L, 2 );
179 180 181 182
    vlclua_pushvalue( L, i_type, val );
    if( i_type == VLC_VAR_STRING )
        free( val.psz_string );
    return 1;
183 184 185 186 187 188 189
}

static int vlclua_var_set( lua_State *L )
{
    vlc_value_t val;
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
190 191

    int i_type = var_Type( *pp_obj, psz_var );
192
    vlclua_tovalue( L, i_type, &val );
193 194 195

    int i_ret = var_Set( *pp_obj, psz_var, val );

196 197 198 199
    lua_pop( L, 3 );
    return vlclua_push_ret( L, i_ret );
}

200 201
static int vlclua_var_create( lua_State *L )
{
202
    int i_type, i_ret;
203 204
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
205

206 207 208 209 210 211 212 213 214 215 216
    switch( lua_type( L, 3 ) )
    {
        case LUA_TNUMBER:
            i_type = VLC_VAR_FLOAT;
            break;
        case LUA_TBOOLEAN:
            i_type = VLC_VAR_BOOL;
            break;
        case LUA_TSTRING:
            i_type = VLC_VAR_STRING;
            break;
217 218 219
        case LUA_TNIL:
            i_type = VLC_VAR_VOID;
            break;
220 221 222 223
        default:
            return 0;
    }

224
    if( ( i_ret = var_Create( *pp_obj, psz_var, i_type ) ) != VLC_SUCCESS )
225
        return vlclua_push_ret( L, i_ret );
226 227 228 229 230

    // Special case for void variables
    if( i_type == VLC_VAR_VOID )
        return 0;

231 232 233 234 235
    vlc_value_t val;
    vlclua_tovalue( L, i_type, &val );
    return vlclua_push_ret( L, var_Set( *pp_obj, psz_var, val ) );
}

236 237 238 239 240 241
static int vlclua_var_get_list( lua_State *L )
{
    vlc_value_t val;
    vlc_value_t text;
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
242

243
    int i_ret = var_Change( *pp_obj, psz_var, VLC_VAR_GETCHOICES, &val, &text );
244 245 246
    if( i_ret < 0 )
        return vlclua_push_ret( L, i_ret );

247 248
    vlclua_pushlist( L, val.p_list );
    vlclua_pushlist( L, text.p_list );
249

250
    var_FreeList( &val, &text );
251 252 253 254 255 256 257
    return 2;
}

static int vlclua_libvlc_command( lua_State *L )
{
    vlc_object_t * p_this = vlclua_get_this( L );
    vlc_value_t val_arg;
258 259

    const char *psz_cmd = luaL_checkstring( L, 1 );
260
    val_arg.psz_string = (char*)luaL_optstring( L, 2, "" );
261

262
    int i_type = var_Type( p_this->obj.libvlc, psz_cmd );
263
    if( ! (i_type & VLC_VAR_ISCOMMAND) )
264 265 266 267 268
    {
        return luaL_error( L, "libvlc's \"%s\" is not a command",
                           psz_cmd );
    }

269
    int i_ret = var_Set( p_this->obj.libvlc, psz_cmd, val_arg );
270 271 272
    lua_pop( L, 2 );

    return vlclua_push_ret( L, i_ret );
273 274
}

275 276 277
#undef vlclua_var_toggle_or_set
int vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
                              const char *psz_name )
278 279 280 281 282
{
    bool b_bool;
    if( lua_gettop( L ) > 1 ) return vlclua_error( L );

    if( lua_gettop( L ) == 0 )
Rafaël Carré's avatar
Rafaël Carré committed
283
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
284
        b_bool = var_ToggleBool( p_obj, psz_name );
Rafaël Carré's avatar
Rafaël Carré committed
285 286 287 288 289 290 291 292 293 294 295 296
        goto end;
    }

    /* lua_gettop( L ) == 1 */
    const char *s = luaL_checkstring( L, -1 );
    lua_pop( L, 1 );

    if( s && !strcmp(s, "on") )
        b_bool = true;
    else if( s && !strcmp(s, "off") )
        b_bool = false;
    else
297
    {
Rafaël Carré's avatar
Rafaël Carré committed
298 299
        b_bool = var_GetBool( p_obj, psz_name );
        goto end;
300 301
    }

Rafaël Carré's avatar
Rafaël Carré committed
302 303 304 305
    if( b_bool != var_GetBool( p_obj, psz_name ) )
        var_SetBool( p_obj, psz_name, b_bool );

end:
306 307 308 309
    lua_pushboolean( L, b_bool );
    return 1;
}

310 311 312 313 314
static int vlclua_trigger_callback( lua_State *L )
{
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );

315 316
    var_TriggerCallback( *pp_obj, psz_var );
    return vlclua_push_ret( L, 0 );
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
static int vlclua_inc_integer( lua_State *L )
{
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
    int64_t i_val = var_IncInteger( *pp_obj, psz_var );

    lua_pushinteger( L, i_val );
    return 1;
}

static int vlclua_dec_integer( lua_State *L )
{
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
    int64_t i_val = var_DecInteger( *pp_obj, psz_var );

    lua_pushinteger( L, i_val );
    return 1;
}

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
static int vlclua_countchoices( lua_State *L )
{
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
    int i_count = var_CountChoices( *pp_obj, psz_var );

    lua_pushinteger( L, i_count );
    return 1;
}

static int vlclua_togglebool( lua_State *L )
{
    vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
    const char *psz_var = luaL_checkstring( L, 2 );
    bool b_val = var_ToggleBool( *pp_obj, psz_var );

    lua_pushboolean( L, b_val );
    return 1;
}

359 360 361 362
/*****************************************************************************
 *
 *****************************************************************************/
static const luaL_Reg vlclua_var_reg[] = {
Pierre Ynard's avatar
Pierre Ynard committed
363
    { "inherit", vlclua_var_inherit },
364 365 366
    { "get", vlclua_var_get },
    { "get_list", vlclua_var_get_list },
    { "set", vlclua_var_set },
367
    { "create", vlclua_var_create },
368
    { "trigger_callback", vlclua_trigger_callback },
369
    { "libvlc_command", vlclua_libvlc_command },
370 371
    { "inc_integer", vlclua_inc_integer },
    { "dec_integer", vlclua_dec_integer },
372 373
    { "count_choices", vlclua_countchoices },
    { "toggle_bool", vlclua_togglebool },
374 375 376 377 378 379 380 381 382
    { NULL, NULL }
};

void luaopen_variables( lua_State *L )
{
    lua_newtable( L );
    luaL_register( L, NULL, vlclua_var_reg );
    lua_setfield( L, -2, "var" );
}