Commit f1175e71 authored by Sam Hocevar's avatar Sam Hocevar

 * Tried to fix the BeOS interface and video output. Any BeOS developers
   listening ? I can't even test that code, it probably wants additional
   fixing.
 * Added a workaround in modules.c for broken libc's which happen to truncate
   filenames in dentry structures when using 64bits offsets. The kind of
   stuff you only see in RedHats <g>.
 * Made the SDL error messages a bit more useful.
 * The Gnome menu is now created when mouse is released, not when it
   is pressed. It may sound stupid, but it really makes a difference.
parent 566ca287
/*****************************************************************************
* aout_beos.cpp: BeOS audio output
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: aout_beos.cpp,v 1.10 2001/01/13 12:57:19 sam Exp $
* Copyright (C) 1999, 2000, 2001 VideoLAN
* $Id: aout_beos.cpp,v 1.11 2001/02/17 08:48:56 sam Exp $
*
* Authors:
* Samuel Hocevar <sam@via.ecp.fr>
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.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
......
/*****************************************************************************
* beos.cpp : BeOS plugin for vlc
*****************************************************************************
* Copyright (C) 2000 VideoLAN
* Copyright (C) 2000, 2001 VideoLAN
*
* Authors:
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.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
......@@ -35,12 +36,6 @@ extern "C"
#include "common.h" /* boolean_t, byte_t */
#include "threads.h"
#include "mtime.h"
#include "plugins.h"
#include "interface.h"
#include "audio_output.h"
#include "video.h"
#include "video_output.h"
#include "modules.h"
#include "modules_inner.h"
......@@ -57,6 +52,8 @@ MODULE_CONFIG_END
* Capabilities defined in the other files.
*****************************************************************************/
extern void aout_getfunctions( function_list_t * p_function_list );
extern void vout_getfunctions( function_list_t * p_function_list );
extern void intf_getfunctions( function_list_t * p_function_list );
/*****************************************************************************
* InitModule: get the module structure and configuration.
......@@ -73,7 +70,9 @@ int InitModule( module_t * p_module )
p_module->psz_version = VERSION;
p_module->i_capabilities = MODULE_CAPABILITY_NULL
| MODULE_CAPABILITY_AOUT;
| MODULE_CAPABILITY_AOUT
| MODULE_CAPABILITY_VOUT
| MODULE_CAPABILITY_INTF;
return( 0 );
}
......@@ -95,6 +94,8 @@ int ActivateModule( module_t * p_module )
}
aout_getfunctions( &p_module->p_functions->aout );
vout_getfunctions( &p_module->p_functions->vout );
intf_getfunctions( &p_module->p_functions->intf );
p_module->p_config = p_config;
......@@ -115,71 +116,4 @@ int DeactivateModule( module_t * p_module )
return( 0 );
}
/* OLD MODULE STRUCTURE -- soon to be removed */
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static void vout_GetPlugin( p_vout_thread_t p_vout );
static void intf_GetPlugin( p_intf_thread_t p_intf );
/* Video output */
int vout_BeCreate ( vout_thread_t *p_vout, char *psz_display,
int i_root_window, void *p_data );
int vout_BeInit ( p_vout_thread_t p_vout );
void vout_BeEnd ( p_vout_thread_t p_vout );
void vout_BeDestroy ( p_vout_thread_t p_vout );
int vout_BeManage ( p_vout_thread_t p_vout );
void vout_BeDisplay ( p_vout_thread_t p_vout );
void vout_BeSetPalette ( p_vout_thread_t p_vout,
u16 *red, u16 *green, u16 *blue, u16 *transp );
/* Interface */
int intf_BeCreate ( p_intf_thread_t p_intf );
void intf_BeDestroy ( p_intf_thread_t p_intf );
void intf_BeManage ( p_intf_thread_t p_intf );
/*****************************************************************************
* GetConfig: get the plugin structure and configuration
*****************************************************************************/
plugin_info_t * GetConfig( void )
{
plugin_info_t * p_info = (plugin_info_t *) malloc( sizeof(plugin_info_t) );
p_info->psz_name = "BeOS";
p_info->psz_version = VERSION;
p_info->psz_author = "the VideoLAN team <vlc@videolan.org>";
p_info->aout_GetPlugin = NULL;
p_info->vout_GetPlugin = vout_GetPlugin;
p_info->intf_GetPlugin = intf_GetPlugin;
p_info->yuv_GetPlugin = NULL;
/* the beos plugin always works under BeOS :) */
p_info->i_score = 0x800;
return( p_info );
}
/*****************************************************************************
* Following functions are only called through the p_info structure
*****************************************************************************/
static void vout_GetPlugin( p_vout_thread_t p_vout )
{
p_vout->p_sys_create = vout_BeCreate;
p_vout->p_sys_init = vout_BeInit;
p_vout->p_sys_end = vout_BeEnd;
p_vout->p_sys_destroy = vout_BeDestroy;
p_vout->p_sys_manage = vout_BeManage;
p_vout->p_sys_display = vout_BeDisplay;
}
static void intf_GetPlugin( p_intf_thread_t p_intf )
{
p_intf->p_sys_create = intf_BeCreate;
p_intf->p_sys_destroy = intf_BeDestroy;
p_intf->p_sys_manage = intf_BeManage;
}
} /* extern "C" */
/*****************************************************************************
* intf_beos.cpp: beos interface
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: intf_beos.cpp,v 1.6 2001/01/05 18:46:43 massiot Exp $
* Copyright (C) 1999, 2000, 2001 VideoLAN
* $Id: intf_beos.cpp,v 1.7 2001/02/17 08:48:56 sam Exp $
*
* Authors:
* Jean-Marc Dressler
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.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
......@@ -44,11 +44,8 @@ extern "C"
#include "common.h"
#include "threads.h"
#include "mtime.h"
#include "plugins.h"
#include "input.h"
#include "video.h"
#include "video_output.h"
#include "tests.h"
#include "modules.h"
#include "intf_msg.h"
#include "interface.h"
......@@ -124,9 +121,45 @@ extern "C"
{
/*****************************************************************************
* intf_BeCreate: initialize dummy interface
* Local prototypes.
*****************************************************************************/
int intf_BeCreate( intf_thread_t *p_intf )
static int intf_Probe ( probedata_t *p_data );
static int intf_Open ( intf_thread_t *p_intf );
static void intf_Close ( intf_thread_t *p_intf );
static void intf_Run ( intf_thread_t *p_intf );
/*****************************************************************************
* Functions exported as capabilities. They are declared as static so that
* we don't pollute the namespace too much.
*****************************************************************************/
void intf_getfunctions( function_list_t * p_function_list )
{
p_function_list->pf_probe = intf_Probe;
p_function_list->functions.intf.pf_open = intf_Open;
p_function_list->functions.intf.pf_close = intf_Close;
p_function_list->functions.intf.pf_run = intf_Run;
}
/*****************************************************************************
* intf_Probe: probe the interface and return a score
*****************************************************************************
* This function tries to initialize Gnome and returns a score to the
* plugin manager so that it can select the best plugin.
*****************************************************************************/
static int intf_Probe( probedata_t *p_data )
{
if( TestMethod( INTF_METHOD_VAR, "beos" ) )
{
return( 999 );
}
return( 1 );
}
/*****************************************************************************
* intf_Create: initialize dummy interface
*****************************************************************************/
static int intf_Create( intf_thread_t *p_intf )
{
/* Allocate instance and initialize some members */
p_intf->p_sys = (intf_sys_t*) malloc( sizeof( intf_sys_t ) );
......@@ -139,7 +172,8 @@ int intf_BeCreate( intf_thread_t *p_intf )
/* Create the interface window */
p_intf->p_sys->p_window =
new InterfaceWindow( BRect( 100, 100, 200, 200 ), "Interface :)", p_intf );
new InterfaceWindow( BRect( 100, 100, 200, 200 ),
"Interface :)", p_intf );
if( p_intf->p_sys->p_window == 0 )
{
free( p_intf->p_sys );
......@@ -147,17 +181,6 @@ int intf_BeCreate( intf_thread_t *p_intf )
return( 1 );
}
/* Spawn video output thread */
if( p_main->b_video )
{
p_intf->p_vout = vout_CreateThread( NULL, 0, 0, 0, NULL, 0, NULL );
if( p_intf->p_vout == NULL ) /* error */
{
intf_ErrMsg("intf error: can't create output thread" );
return( 1 );
}
}
/* Bind normal keys. */
intf_AssignNormalKeys( p_intf );
......@@ -165,22 +188,10 @@ int intf_BeCreate( intf_thread_t *p_intf )
}
/*****************************************************************************
* intf_BeDestroy: destroy dummy interface
* intf_Destroy: destroy dummy interface
*****************************************************************************/
void intf_BeDestroy( intf_thread_t *p_intf )
static void intf_Destroy( intf_thread_t *p_intf )
{
/* Close input thread, if any (blocking) */
if( p_intf->p_input )
{
input_DestroyThread( p_intf->p_input, NULL );
}
/* Close video output thread, if any (blocking) */
if( p_intf->p_vout )
{
vout_DestroyThread( p_intf->p_vout, NULL );
}
/* Destroy the interface window */
p_intf->p_sys->p_window->Lock();
p_intf->p_sys->p_window->Quit();
......@@ -191,14 +202,24 @@ void intf_BeDestroy( intf_thread_t *p_intf )
/*****************************************************************************
* intf_BeManage: event loop
* intf_Run: event loop
*****************************************************************************/
void intf_BeManage( intf_thread_t *p_intf )
static void intf_Run( intf_thread_t *p_intf )
{
if( p_intf->p_sys->i_key != -1 )
while( !p_intf->b_die )
{
intf_ProcessKey( p_intf, p_intf->p_sys->i_key );
p_intf->p_sys->i_key = -1;
/* Manage core vlc functions through the callback */
p_intf->pf_manage( p_intf );
/* Manage keys */
if( p_intf->p_sys->i_key != -1 )
{
intf_ProcessKey( p_intf, p_intf->p_sys->i_key );
p_intf->p_sys->i_key = -1;
}
/* Wait a bit */
msleep( INTF_IDLE_SLEEP );
}
}
......
/*****************************************************************************
* vout_beos.cpp: beos video output display method
*****************************************************************************
* Copyright (C) 2000 VideoLAN
* Copyright (C) 2000, 2001 VideoLAN
*
* Authors:
* Jean-Marc Dressler
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.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
......@@ -44,13 +44,14 @@ extern "C"
#include "common.h"
#include "threads.h"
#include "mtime.h"
#include "plugins.h"
#include "tests.h"
#include "modules.h"
#include "video.h"
#include "video_output.h"
#include "intf_msg.h"
#include "interface.h" /* XXX maybe to remove if beos_window.h is splitted */
#include "intf_msg.h"
#include "main.h"
}
......@@ -336,16 +337,55 @@ extern "C"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int BeosOpenDisplay ( vout_thread_t *p_vout );
static void BeosCloseDisplay ( vout_thread_t *p_vout );
static int vout_Probe ( probedata_t *p_data );
static int vout_Create ( struct vout_thread_s * );
static int vout_Init ( struct vout_thread_s * );
static void vout_End ( struct vout_thread_s * );
static void vout_Destroy ( struct vout_thread_s * );
static int vout_Manage ( struct vout_thread_s * );
static void vout_Display ( struct vout_thread_s * );
static int BeosOpenDisplay ( vout_thread_t *p_vout );
static void BeosCloseDisplay( vout_thread_t *p_vout );
/*****************************************************************************
* Functions exported as capabilities. They are declared as static so that
* we don't pollute the namespace too much.
*****************************************************************************/
void vout_getfunctions( function_list_t * p_function_list )
{
p_function_list->pf_probe = vout_Probe;
p_function_list->functions.vout.pf_create = vout_Create;
p_function_list->functions.vout.pf_init = vout_Init;
p_function_list->functions.vout.pf_end = vout_End;
p_function_list->functions.vout.pf_destroy = vout_Destroy;
p_function_list->functions.vout.pf_manage = vout_Manage;
p_function_list->functions.vout.pf_display = vout_Display;
p_function_list->functions.vout.pf_setpalette = NULL;
}
/*****************************************************************************
* vout_Probe: probe the video driver and return a score
*****************************************************************************
* This function tries to initialize SDL and returns a score to the
* plugin manager so that it can select the best plugin.
*****************************************************************************/
static int vout_Probe( probedata_t *p_data )
{
if( TestMethod( VOUT_METHOD_VAR, "beos" ) )
{
return( 999 );
}
return( 100 );
}
/*****************************************************************************
* vout_BeCreate: allocates dummy video thread output method
* vout_Create: allocates dummy video thread output method
*****************************************************************************
* This function allocates and initializes a dummy vout method.
*****************************************************************************/
int vout_BeCreate( vout_thread_t *p_vout, char *psz_display,
int i_root_window, void *p_data )
int vout_Create( vout_thread_t *p_vout )
{
/* Allocate structure */
p_vout->p_sys = (vout_sys_t*) malloc( sizeof( vout_sys_t ) );
......@@ -356,8 +396,10 @@ int vout_BeCreate( vout_thread_t *p_vout, char *psz_display,
}
/* Set video window's size */
p_vout->i_width = main_GetIntVariable( VOUT_WIDTH_VAR, VOUT_WIDTH_DEFAULT );
p_vout->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR, VOUT_HEIGHT_DEFAULT );
p_vout->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
VOUT_WIDTH_DEFAULT );
p_vout->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
VOUT_HEIGHT_DEFAULT );
/* Open and initialize device */
if( BeosOpenDisplay( p_vout ) )
......@@ -371,9 +413,9 @@ int vout_BeCreate( vout_thread_t *p_vout, char *psz_display,
}
/*****************************************************************************
* vout_BeInit: initialize dummy video thread output method
* vout_Init: initialize dummy video thread output method
*****************************************************************************/
int vout_BeInit( vout_thread_t *p_vout )
int vout_Init( vout_thread_t *p_vout )
{
VideoWindow * p_win = p_vout->p_sys->p_window;
u32 i_page_size;
......@@ -406,9 +448,9 @@ int vout_BeInit( vout_thread_t *p_vout )
}
/*****************************************************************************
* vout_BeEnd: terminate dummy video thread output method
* vout_End: terminate dummy video thread output method
*****************************************************************************/
void vout_BeEnd( vout_thread_t *p_vout )
void vout_End( vout_thread_t *p_vout )
{
VideoWindow * p_win = p_vout->p_sys->p_window;
......@@ -422,11 +464,11 @@ void vout_BeEnd( vout_thread_t *p_vout )
}
/*****************************************************************************
* vout_BeDestroy: destroy dummy video thread output method
* vout_Destroy: destroy dummy video thread output method
*****************************************************************************
* Terminate an output method created by DummyCreateOutputMethod
*****************************************************************************/
void vout_BeDestroy( vout_thread_t *p_vout )
void vout_Destroy( vout_thread_t *p_vout )
{
BeosCloseDisplay( p_vout );
......@@ -434,46 +476,48 @@ void vout_BeDestroy( vout_thread_t *p_vout )
}
/*****************************************************************************
* vout_BeManage: handle dummy events
* vout_Manage: handle dummy events
*****************************************************************************
* This function should be called regularly by video output thread. It manages
* console events. It returns a non null value on error.
*****************************************************************************/
int vout_BeManage( vout_thread_t *p_vout )
int vout_Manage( vout_thread_t *p_vout )
{
if( p_vout->i_changes & VOUT_SIZE_CHANGE )
{
intf_DbgMsg("resizing window");
intf_DbgMsg( "resizing window" );
p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
/* Resize window */
p_vout->p_sys->p_window->ResizeTo( p_vout->i_width, p_vout->i_height );
/* Destroy XImages to change their size */
vout_BeEnd( p_vout );
vout_End( p_vout );
/* Recreate XImages. If SysInit failed, the thread can't go on. */
if( vout_BeInit( p_vout ) )
if( vout_Init( p_vout ) )
{
intf_ErrMsg("error: can't resize display");
intf_ErrMsg( "error: can't resize display" );
return( 1 );
}
/* Tell the video output thread that it will need to rebuild YUV
* tables. This is needed since convertion buffer size may have changed */
* tables. This is needed since convertion buffer size may have
* changed */
p_vout->i_changes |= VOUT_YUV_CHANGE;
intf_Msg("vout: video display resized (%dx%d)", p_vout->i_width, p_vout->i_height);
intf_Msg( "vout: video display resized (%dx%d)",
p_vout->i_width, p_vout->i_height );
}
return( 0 );
}
/*****************************************************************************
* vout_BeDisplay: displays previously rendered output
* vout_Display: displays previously rendered output
*****************************************************************************
* This function send the currently rendered image to dummy image, waits until
* it is displayed and switch the two rendering buffers, preparing next frame.
*****************************************************************************/
void vout_BeDisplay( vout_thread_t *p_vout )
void vout_Display( vout_thread_t *p_vout )
{
VideoWindow * p_win = p_vout->p_sys->p_window;
......
......@@ -175,7 +175,7 @@ static int vout_Create( vout_thread_t *p_vout )
if( SDLOpenDisplay(p_vout) )
{
intf_ErrMsg( "vout error: can't initialize SDL (%s)", SDL_GetError() );
intf_ErrMsg( "vout error: can't set up SDL (%s)", SDL_GetError() );
free( p_vout->p_sys );
return( 1 );
}
......@@ -262,16 +262,21 @@ static int vout_Manage( vout_thread_t *p_vout )
p_vout->i_changes |= VOUT_SIZE_CHANGE;
break;
case SDL_MOUSEBUTTONUP:
switch( event.button.button )
{
case SDL_BUTTON_RIGHT:
p_main->p_intf->b_menu_change = 1;
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
switch( event.button.button )
{
case SDL_BUTTON_MIDDLE:
p_vout->i_changes |= VOUT_CURSOR_CHANGE;
break;
case SDL_BUTTON_RIGHT:
p_main->p_intf->b_menu_change = 1;
break;
}
break;
......@@ -337,7 +342,7 @@ static int vout_Manage( vout_thread_t *p_vout )
SDLCloseDisplay( p_vout );
if( SDLOpenDisplay( p_vout ) )
{
intf_ErrMsg( "error: can't open DISPLAY default display" );
intf_ErrMsg( "vout error: can't reopen display after resize" );
return( 1 );
}
p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
......@@ -354,7 +359,7 @@ static int vout_Manage( vout_thread_t *p_vout )
SDLCloseDisplay( p_vout );
if( SDLOpenDisplay( p_vout ) )
{
intf_ErrMsg( "error: can't open DISPLAY default display" );
intf_ErrMsg( "error: can't reopen display after YUV change" );
return( 1 );
}
p_vout->i_changes &= ~VOUT_YUV_CHANGE;
......@@ -533,13 +538,13 @@ static int SDLOpenDisplay( vout_thread_t *p_vout )
else
flags |= SDL_SWSURFACE; /* save video memory */
bpp = SDL_VideoModeOK(p_vout->p_sys->i_width,
p_vout->p_sys->i_height,
p_vout->i_screen_depth, flags);
bpp = SDL_VideoModeOK( p_vout->p_sys->i_width,
p_vout->p_sys->i_height,
p_vout->i_screen_depth, flags );
if(bpp == 0)
{
intf_ErrMsg( "error: can't open DISPLAY default display" );
intf_ErrMsg( "vout error: no video mode available" );
return( 1 );
}
......@@ -549,7 +554,7 @@ static int SDLOpenDisplay( vout_thread_t *p_vout )
if( p_vout->p_sys->p_display == NULL )
{
intf_ErrMsg( "error: can't open DISPLAY default display" );
intf_ErrMsg( "vout error: cannot set video mode" );
return( 1 );
}
......@@ -563,7 +568,6 @@ static int SDLOpenDisplay( vout_thread_t *p_vout )
SDL_WM_SetCaption( VOUT_TITLE " (SDL output)",
VOUT_TITLE " (SDL output)" );
SDL_EventState(SDL_KEYUP , SDL_IGNORE); /* ignore keys up */
SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
if( p_vout->b_need_render )
{
......
......@@ -2,7 +2,7 @@
* vout_x11.c: X11 video output display method
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
* $Id: vout_x11.c,v 1.13 2001/02/16 06:37:09 sam Exp $
* $Id: vout_x11.c,v 1.14 2001/02/17 08:48:56 sam Exp $
*
* Authors: Vincent Seguin <seguin@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
......@@ -114,8 +114,7 @@ static int vout_Manage ( struct vout_thread_s * );
static void vout_Display ( struct vout_thread_s * );
static void vout_SetPalette( struct vout_thread_s *, u16*, u16*, u16*, u16* );
static int X11CreateWindow ( vout_thread_t *p_vout );
static int X11CreateWindow ( vout_thread_t *p_vout );
static int X11InitDisplay ( vout_thread_t *p_vout, char *psz_display );
static int X11CreateImage ( vout_thread_t *p_vout, XImage **pp_ximage );
......@@ -125,7 +124,6 @@ static int X11CreateShmImage ( vout_thread_t *p_vout, XImage **pp_ximage,
static void X11DestroyShmImage ( vout_thread_t *p_vout, XImage *p_ximage,
XShmSegmentInfo *p_shm_info );
/* local prototypes */
static void X11TogglePointer ( vout_thread_t *p_vout );
static void X11EnableScreenSaver ( vout_thread_t *p_vout );
static void X11DisableScreenSaver ( vout_thread_t *p_vout );
......@@ -354,7 +352,8 @@ static int vout_Manage( vout_thread_t *p_vout )
b_resized = 0;
while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
StructureNotifyMask | KeyPressMask |
ButtonPressMask, &xevent ) == True )
ButtonPressMask | ButtonReleaseMask, &xevent )
== True )
{
/* ConfigureNotify event: prepare */
if( (xevent.type == ConfigureNotify)
......@@ -412,7 +411,13 @@ static int vout_Manage( vout_thread_t *p_vout )
case Button2:
X11TogglePointer( p_vout );
break;
}
}
/* Mouse release */
else if( xevent.type == ButtonRelease )
{
switch( ((XButtonEvent *)&xevent)->button )
{
case Button3:
/* FIXME: need locking ! */
p_main->p_intf->b_menu_change = 1;
......@@ -678,7 +683,8 @@ static int X11CreateWindow( vout_thread_t *p_vout )
} while( !( b_expose && b_configure_notify && b_map_notify ) );
XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
StructureNotifyMask | KeyPressMask | ButtonPressMask );
StructureNotifyMask | KeyPressMask |
ButtonPressMask | ButtonReleaseMask );
if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
{
......
......@@ -24,6 +24,12 @@
#include "config.h"
/* Some faulty libcs have a broken struct dirent when _FILE_OFFSET_BITS
* is set to 64. Don't try to be cleverer. */
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS
#endif
#include <stdlib.h> /* free(), strtol() */
#include <stdio.h> /* sprintf() */
#include <string.h> /* strdup() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment