intf.c 8.79 KB
Newer Older
1
/*****************************************************************************
2
 * intf.c: Generic lua interface functions
3
 *****************************************************************************
4
 * Copyright (C) 2007-2008 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
#include <vlc_common.h>
36 37 38 39 40 41 42 43 44 45 46 47
#include <vlc_meta.h>
#include <vlc_charset.h>

#include <vlc_interface.h>
#include <vlc_playlist.h>
#include <vlc_aout.h>

#include <lua.h>        /* Low level lua C API */
#include <lauxlib.h>    /* Higher level C API */
#include <lualib.h>     /* Lua libs */

#include "vlc.h"
48
#include "libs.h"
49

50 51 52
/*****************************************************************************
 * Prototypes
 *****************************************************************************/
53
static void *Run( void * );
54 55

/*****************************************************************************
56
 *
57
 *****************************************************************************/
58
static char *FindFile( const char *psz_name )
59 60 61
{
    char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
    char **ppsz_dir;
62
    vlclua_dir_list( "intf", ppsz_dir_list );
63 64 65 66 67 68 69
    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
    {
        char *psz_filename;
        FILE *fp;
        if( asprintf( &psz_filename, "%s"DIR_SEP"%s.lua", *ppsz_dir,
                      psz_name ) < 0 )
        {
70
            vlclua_dir_list_free( ppsz_dir_list );
71 72 73 74 75 76
            return NULL;
        }
        fp = fopen( psz_filename, "r" );
        if( fp )
        {
            fclose( fp );
77
            vlclua_dir_list_free( ppsz_dir_list );
78 79 80 81
            return psz_filename;
        }
        free( psz_filename );
    }
82
    vlclua_dir_list_free( ppsz_dir_list );
83 84 85 86 87 88 89 90 91 92 93
    return NULL;
}

static inline void luaL_register_submodule( lua_State *L, const char *psz_name,
                                            const luaL_Reg *l )
{
    lua_newtable( L );
    luaL_register( L, NULL, l );
    lua_setfield( L, -2, psz_name );
}

94
static const struct
95 96 97 98 99 100 101 102 103 104
{
    const char *psz_shortcut;
    const char *psz_name;
} pp_shortcuts[] = {
    { "luarc", "rc" },
    /* { "rc", "rc" }, */
    { "luahotkeys", "hotkeys" },
    /* { "hotkeys", "hotkeys" }, */
    { "luatelnet", "telnet" },
    /* { "telnet", "telnet" }, */
dionoea's avatar
dionoea committed
105 106
    { "luahttp", "http" },
    /* { "http", "http" }, */
107 108
    { NULL, NULL } };

109
static bool WordInList( const char *psz_list, const char *psz_word )
110 111 112 113 114 115 116 117 118
{
    const char *psz_str = strstr( psz_list, psz_word );
    int i_len = strlen( psz_word );
    while( psz_str )
    {
        if( (psz_str == psz_list || *(psz_str-1) == ',' )
         /* it doesn't start in middle of a word */
         /* it doest end in middle of a word */
         && ( psz_str[i_len] == '\0' || psz_str[i_len] == ',' ) )
119
            return true;
120 121
        psz_str = strstr( psz_str, psz_word );
    }
122
    return false;
123 124
}

ivoire's avatar
ivoire committed
125
static char *GetModuleName( intf_thread_t *p_intf )
126 127 128 129 130 131 132 133 134 135
{
    int i;
    const char *psz_intf;
    if( *p_intf->psz_intf == '$' )
        psz_intf = var_GetString( p_intf, p_intf->psz_intf+1 );
    else
        psz_intf = p_intf->psz_intf;
    for( i = 0; pp_shortcuts[i].psz_name; i++ )
    {
        if( WordInList( psz_intf, pp_shortcuts[i].psz_shortcut ) )
ivoire's avatar
ivoire committed
136
            return strdup( pp_shortcuts[i].psz_name );
137 138 139 140 141
    }

    return config_GetPsz( p_intf, "lua-intf" );
}

142
static const luaL_Reg p_reg[] = { { NULL, NULL } };
143

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
144
int Open_LuaIntf( vlc_object_t *p_this )
145 146 147 148 149
{
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
    intf_sys_t *p_sys;
    lua_State *L;

ivoire's avatar
ivoire committed
150
    char *psz_name = GetModuleName( p_intf );
151
    const char *psz_config;
152
    bool b_config_set = false;
ivoire's avatar
ivoire committed
153
    if( !psz_name ) psz_name = strdup( "dummy" );
154 155 156 157

    p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t*) );
    if( !p_intf->p_sys )
    {
ivoire's avatar
ivoire committed
158
        free( psz_name );
159 160 161
        return VLC_ENOMEM;
    }
    p_sys = p_intf->p_sys;
162
    p_sys->psz_filename = FindFile( psz_name );
163 164 165 166
    if( !p_sys->psz_filename )
    {
        msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
                 psz_name );
ivoire's avatar
ivoire committed
167
        free( psz_name );
ivoire's avatar
ivoire committed
168
        free( p_sys );
169 170 171 172 173 174 175 176
        return VLC_EGENERIC;
    }
    msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );

    L = luaL_newstate();
    if( !L )
    {
        msg_Err( p_intf, "Could not create new Lua State" );
ivoire's avatar
ivoire committed
177
        free( psz_name );
178 179 180 181
        free( p_sys );
        return VLC_EGENERIC;
    }

182
    luaL_openlibs( L );
183 184 185

    /* register our functions */
    luaL_register( L, "vlc", p_reg );
186 187

    /* store a pointer to p_intf (FIXME: user could overwrite this) */
188 189
    lua_pushlightuserdata( L, p_intf );
    lua_setfield( L, -2, "private" );
190

191
    /* register submodules */
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    luaopen_acl( L );
    luaopen_config( L );
    luaopen_volume( L );
    luaopen_httpd( L );
    luaopen_input( L );
    luaopen_msg( L );
    luaopen_misc( L );
    luaopen_net( L );
    luaopen_object( L );
    luaopen_osd( L );
    luaopen_playlist( L );
    luaopen_sd( L );
    luaopen_stream( L );
    luaopen_strings( L );
    luaopen_variables( L );
    luaopen_video( L );
    luaopen_vlm( L );
    luaopen_volume( L );

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    /* clean up */
    lua_pop( L, 1 );

    /* <gruik> */
    /* Setup the module search path */
    {
    char *psz_command;
    char *psz_char = strrchr(p_sys->psz_filename,DIR_SEP_CHAR);
    *psz_char = '\0';
    /* FIXME: don't use luaL_dostring */
    if( asprintf( &psz_command,
                  "package.path = \"%s"DIR_SEP"modules"DIR_SEP"?.lua;\"..package.path",
                  p_sys->psz_filename ) < 0 )
        return VLC_EGENERIC;
    *psz_char = DIR_SEP_CHAR;
    if( luaL_dostring( L, psz_command ) )
        return VLC_EGENERIC;
    }
    /* </gruik> */

    psz_config = config_GetPsz( p_intf, "lua-config" );
    if( psz_config && *psz_config )
    {
        char *psz_buffer;
        if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
        {
            printf("%s\n", psz_buffer);
            if( luaL_dostring( L, psz_buffer ) == 1 )
                msg_Err( p_intf, "Error while parsing \"lua-config\"." );
            free( psz_buffer );
            lua_getglobal( L, "config" );
            if( lua_istable( L, -1 ) )
            {
                lua_getfield( L, -1, psz_name );
                if( lua_istable( L, -1 ) )
                {
                    lua_setglobal( L, "config" );
248
                    b_config_set = true;
249 250 251 252
                }
            }
        }
    }
253
    if( b_config_set == false )
254 255 256 257 258 259 260
    {
        lua_newtable( L );
        lua_setglobal( L, "config" );
    }

    p_sys->L = L;

261 262 263 264 265 266 267 268 269 270 271 272 273
    p_intf->psz_header = psz_name;
    /* ^^ Do I need to clean that up myself in Close_LuaIntf? */

    vlc_mutex_init( &p_sys->lock );
    vlc_cond_init( &p_sys->wait );
    p_sys->exiting = false;

    if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
    {
        p_sys->exiting = true;
        Close_LuaIntf( p_this );
        return VLC_ENOMEM;
    }
274 275 276 277

    return VLC_SUCCESS;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
278
void Close_LuaIntf( vlc_object_t *p_this )
279 280
{
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
281 282 283 284 285 286 287 288 289 290 291 292
    intf_sys_t *p_sys = p_intf->p_sys;

    if( !p_sys->exiting ) /* <- Read-only here and in thread: no locking */
    {
        vlc_mutex_lock( &p_sys->lock );
        p_sys->exiting = true;
        vlc_cond_signal( &p_sys->wait );
        vlc_mutex_unlock( &p_sys->lock );
        vlc_join( p_sys->thread, NULL );
    }
    vlc_cond_destroy( &p_sys->wait );
    vlc_mutex_destroy( &p_sys->lock );
293

294 295
    lua_close( p_sys->L );
    free( p_sys );
296 297
}

298
static void *Run( void *data )
299
{
300 301 302
    intf_thread_t *p_intf = data;
    intf_sys_t *p_sys = p_intf->p_sys;
    lua_State *L = p_sys->L;
303

304
    if( luaL_dofile( L, p_sys->psz_filename ) )
305
    {
306
        msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
307 308 309
                 lua_tostring( L, lua_gettop( L ) ) );
        lua_pop( L, 1 );
    }
310
    return NULL;
311
}