2006-05-29 04:59:36 -04:00
|
|
|
/* HACK HACK. HACK.
|
|
|
|
* This is just a quick hack to see if a 3D type video cube
|
|
|
|
* would be possible with clutter. It needs many hacks cleaning.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <clutter/clutter.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
|
|
|
|
#include <math.h> /* for M_PI */
|
|
|
|
|
|
|
|
#define WINWIDTH 800
|
|
|
|
#define WINHEIGHT 600
|
|
|
|
|
|
|
|
/* lazy globals */
|
|
|
|
static float xrot, yrot, zrot;
|
|
|
|
|
|
|
|
/* Avoid needing GLUT perspective call */
|
|
|
|
static void
|
|
|
|
frustum (GLfloat left,
|
|
|
|
GLfloat right,
|
|
|
|
GLfloat bottom,
|
|
|
|
GLfloat top,
|
|
|
|
GLfloat nearval,
|
|
|
|
GLfloat farval)
|
|
|
|
{
|
|
|
|
GLfloat x, y, a, b, c, d;
|
|
|
|
GLfloat m[16];
|
|
|
|
|
|
|
|
x = (2.0 * nearval) / (right - left);
|
|
|
|
y = (2.0 * nearval) / (top - bottom);
|
|
|
|
a = (right + left) / (right - left);
|
|
|
|
b = (top + bottom) / (top - bottom);
|
|
|
|
c = -(farval + nearval) / ( farval - nearval);
|
|
|
|
d = -(2.0 * farval * nearval) / (farval - nearval);
|
|
|
|
|
|
|
|
#define M(row,col) m[col*4+row]
|
|
|
|
M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F;
|
|
|
|
M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F;
|
|
|
|
M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d;
|
|
|
|
M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F;
|
|
|
|
#undef M
|
|
|
|
|
|
|
|
glMultMatrixf (m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
perspective (GLfloat fovy,
|
|
|
|
GLfloat aspect,
|
|
|
|
GLfloat zNear,
|
|
|
|
GLfloat zFar)
|
|
|
|
{
|
|
|
|
GLfloat xmin, xmax, ymin, ymax;
|
|
|
|
|
|
|
|
ymax = zNear * tan (fovy * M_PI / 360.0);
|
|
|
|
ymin = -ymax;
|
|
|
|
xmin = ymin * aspect;
|
|
|
|
xmax = ymax * aspect;
|
|
|
|
|
|
|
|
frustum (xmin, xmax, ymin, ymax, zNear, zFar);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* video texture subclass */
|
|
|
|
|
|
|
|
#define CLUTTER_TYPE_VIDEO_TEXTURE_CUBE clutter_video_texture_cube_get_type()
|
|
|
|
|
|
|
|
#define CLUTTER_VIDEO_TEXTURE_CUBE(obj) \
|
|
|
|
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
|
|
|
CLUTTER_TYPE_VIDEO_TEXTURE_CUBE, ClutterVideoTextureCube))
|
|
|
|
|
|
|
|
#define CLUTTER_VIDEO_TEXTURE_CUBE_CLASS(klass) \
|
|
|
|
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
|
|
|
CLUTTER_TYPE_VIDEO_TEXTURE_CUBE, ClutterVideoTextureCubeClass))
|
|
|
|
|
|
|
|
#define CLUTTER_IS_VIDEO_TEXTURE_CUBE(obj) \
|
|
|
|
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
|
|
|
CLUTTER_TYPE_VIDEO_TEXTURE_CUBE))
|
|
|
|
|
|
|
|
#define CLUTTER_IS_VIDEO_TEXTURE_CUBE_CLASS(klass) \
|
|
|
|
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
|
|
|
CLUTTER_TYPE_VIDEO_TEXTURE_CUBE))
|
|
|
|
|
|
|
|
#define CLUTTER_VIDEO_TEXTURE_CUBE_GET_CLASS(obj) \
|
|
|
|
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
|
|
|
CLUTTER_TYPE_VIDEO_TEXTURE_CUBE, ClutterVideoTextureCubeClass))
|
|
|
|
|
|
|
|
typedef struct ClutterVideoTextureCubePrivate ClutterVideoTextureCubePrivate ;
|
|
|
|
|
|
|
|
typedef struct ClutterVideoTextureCube
|
|
|
|
{
|
|
|
|
ClutterVideoTexture parent;
|
|
|
|
ClutterVideoTextureCubePrivate *priv;
|
|
|
|
|
|
|
|
}
|
|
|
|
ClutterVideoTextureCube;
|
|
|
|
|
|
|
|
typedef struct ClutterVideoTextureCubeClass
|
|
|
|
{
|
|
|
|
ClutterVideoTextureClass parent_class;
|
|
|
|
}
|
|
|
|
ClutterVideoTextureCubeClass;
|
|
|
|
|
|
|
|
GType clutter_video_texture_cube_get_type (void);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (ClutterVideoTextureCube, clutter_video_texture_cube, CLUTTER_TYPE_VIDEO_TEXTURE);
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_video_texture_cube_paint (ClutterElement *self)
|
|
|
|
{
|
|
|
|
if (clutter_texture_get_pixbuf (CLUTTER_TEXTURE(self)) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!CLUTTER_ELEMENT_IS_REALIZED (CLUTTER_ELEMENT(self)))
|
|
|
|
clutter_element_realize (CLUTTER_ELEMENT(self));
|
|
|
|
|
|
|
|
if (!clutter_texture_has_generated_tiles (CLUTTER_TEXTURE(self)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* HACK: sets up a 3D tranform matrix other than regular 2D one */
|
|
|
|
/* FIXME: figure out how to nicely combine both within clutter */
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
perspective (45.0f,
|
|
|
|
(GLfloat)WINWIDTH/(GLfloat)WINHEIGHT,
|
|
|
|
0.1f,
|
|
|
|
100.0f);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
/* back camera out a little */
|
|
|
|
glTranslatef(0.0f,0.0f,-3.0f);
|
|
|
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glShadeModel(GL_SMOOTH);
|
|
|
|
glClearDepth(1.0f);
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
|
|
|
|
|
|
glRotatef(xrot,1.0f,0.0f,0.0f);
|
|
|
|
glRotatef(yrot,0.0f,1.0f,0.0f);
|
|
|
|
glRotatef(zrot,0.0f,0.0f,1.0f);
|
|
|
|
|
|
|
|
/* HACK: Cheat as just bind to first tiled as squared */
|
|
|
|
clutter_texture_bind_tile (CLUTTER_TEXTURE(self), 0);
|
|
|
|
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
// Front Face
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
|
|
|
|
// Back Face
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
|
|
|
|
// Top Face
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
|
|
|
|
// Bottom Face
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
|
|
|
|
// Right face
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
|
|
|
|
// Left Face
|
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
|
|
|
|
/* HACK: reset to regular transform */
|
|
|
|
#if 0
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
|
|
glLoadIdentity ();
|
|
|
|
glOrtho (0, WINWIDTH, WINHEIGHT, 0, -1, 1);
|
|
|
|
glMatrixMode (GL_MODELVIEW);
|
|
|
|
glLoadIdentity ();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* rotate */
|
|
|
|
xrot+=1.0f;
|
|
|
|
yrot+=1.0f;
|
|
|
|
zrot+=1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_video_texture_cube_class_init (ClutterVideoTextureCubeClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
ClutterElementClass *element_class;
|
|
|
|
|
|
|
|
gobject_class = (GObjectClass*)klass;
|
|
|
|
element_class = (ClutterElementClass*)klass;
|
|
|
|
|
|
|
|
element_class->paint = clutter_video_texture_cube_paint;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_video_texture_cube_init (ClutterVideoTextureCube *self)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ClutterElement*
|
|
|
|
clutter_video_texture_cube_new (GError **err)
|
|
|
|
{
|
|
|
|
return CLUTTER_ELEMENT(g_object_new (CLUTTER_TYPE_VIDEO_TEXTURE_CUBE,
|
|
|
|
/* "tiled", FALSE, */
|
|
|
|
NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
ClutterElement *label, *texture, *vtexture;
|
2006-06-05 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-color.h:
* clutter-color.c: Reimplement ClutterColor as a boxed type;
add convenience API for color handling, like: add, subtract,
shade, HSL color-space conversion, packing and unpacking.
* clutter-private.h: Update ClutterMainContext, and export the
main context pointer here.
* clutter-rectangle.h:
* clutter-rectangle.c: Update the color-related code; make
clutter_rectangle_new() and empty constructor and provide
clutter_rectangle_new_with_color(); provide color setter
and getter API.
* clutter-label.h:
* clutter-label.c: Rename the "font" property to "font-name";
update the color-related code to the new ClutterColor object;
rename clutter_label_new() to clutter_label_new_with_text(),
and add setters and getters for the properties.
* clutter-marshal.list: Add VOID:OBJECT and VOID:BOXED marshallers
generators.
* clutter-stage.h:
* clutter-stage.c: Rework the API: provide a default constructor
for a singleton object, named clutter_stage_get_default(), which
supercedes the clutter_stage() function in clutter-main; provide
new events: button-press-event, button-release-event,
key-press-event and key-release-event; update the color-related
code;
(clutter_stage_snapshot): Allow negative width and height when
taking a snapshot (meaning: use full width/height).
(clutter_stage_get_element_at_pos): Rename clutter_stage_pick().
* clutter-element.c (clutter_element_paint): Clean up the
stage and color related code.
* clutter-event.h:
* clutter-event.c: Add generic ClutterAnyEvent type; add
clutter_event_new(), clutter_event_copy() and clutter_event_free();
make ClutterEvent a boxed type.
* clutter-main.h:
* clutter-main.c: Remove clutter_stage(); add clutter_main_quit(),
for cleanly quitting from clutter_main(); add multiple mainloops
support; allocate the ClutterCntx instead of adding it to the
stack; re-work the ClutterEvent dispatching.
* clutter-group.c (clutter_group_add), (clutter_group_remove): Keep
a reference on the element when added to a ClutterGroup.
* examples/rects.py
* examples/test.c:
* examples/test-text.c:
* examples/video-cube.c:
* examples/super-oh.c:
* examples/test-video.c: Update.
2006-06-05 09:38:31 -04:00
|
|
|
ClutterElement *stage = clutter_stage_get_default ();
|
2006-05-29 04:59:36 -04:00
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
GError *err = NULL;
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
g_error("%s <video file>", argv[0]);
|
|
|
|
|
|
|
|
clutter_init (&argc, &argv);
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file ("clutter-logo-800x600.png", NULL);
|
|
|
|
|
|
|
|
if (!pixbuf)
|
|
|
|
g_error("pixbuf load failed");
|
|
|
|
|
2006-06-05 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-color.h:
* clutter-color.c: Reimplement ClutterColor as a boxed type;
add convenience API for color handling, like: add, subtract,
shade, HSL color-space conversion, packing and unpacking.
* clutter-private.h: Update ClutterMainContext, and export the
main context pointer here.
* clutter-rectangle.h:
* clutter-rectangle.c: Update the color-related code; make
clutter_rectangle_new() and empty constructor and provide
clutter_rectangle_new_with_color(); provide color setter
and getter API.
* clutter-label.h:
* clutter-label.c: Rename the "font" property to "font-name";
update the color-related code to the new ClutterColor object;
rename clutter_label_new() to clutter_label_new_with_text(),
and add setters and getters for the properties.
* clutter-marshal.list: Add VOID:OBJECT and VOID:BOXED marshallers
generators.
* clutter-stage.h:
* clutter-stage.c: Rework the API: provide a default constructor
for a singleton object, named clutter_stage_get_default(), which
supercedes the clutter_stage() function in clutter-main; provide
new events: button-press-event, button-release-event,
key-press-event and key-release-event; update the color-related
code;
(clutter_stage_snapshot): Allow negative width and height when
taking a snapshot (meaning: use full width/height).
(clutter_stage_get_element_at_pos): Rename clutter_stage_pick().
* clutter-element.c (clutter_element_paint): Clean up the
stage and color related code.
* clutter-event.h:
* clutter-event.c: Add generic ClutterAnyEvent type; add
clutter_event_new(), clutter_event_copy() and clutter_event_free();
make ClutterEvent a boxed type.
* clutter-main.h:
* clutter-main.c: Remove clutter_stage(); add clutter_main_quit(),
for cleanly quitting from clutter_main(); add multiple mainloops
support; allocate the ClutterCntx instead of adding it to the
stack; re-work the ClutterEvent dispatching.
* clutter-group.c (clutter_group_add), (clutter_group_remove): Keep
a reference on the element when added to a ClutterGroup.
* examples/rects.py
* examples/test.c:
* examples/test-text.c:
* examples/video-cube.c:
* examples/super-oh.c:
* examples/test-video.c: Update.
2006-06-05 09:38:31 -04:00
|
|
|
clutter_element_set_size (stage,
|
2006-05-29 04:59:36 -04:00
|
|
|
WINWIDTH, WINHEIGHT);
|
|
|
|
|
|
|
|
texture = clutter_texture_new_from_pixbuf (pixbuf);
|
|
|
|
|
|
|
|
vtexture = clutter_video_texture_cube_new (&err);
|
|
|
|
|
|
|
|
if (vtexture == NULL || err != NULL)
|
|
|
|
{
|
|
|
|
g_error("failed to create vtexture, err: %s", err->message);
|
|
|
|
}
|
|
|
|
|
|
|
|
clutter_video_texture_open(CLUTTER_VIDEO_TEXTURE(vtexture),
|
|
|
|
argv[1],
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
2006-06-05 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-color.h:
* clutter-color.c: Reimplement ClutterColor as a boxed type;
add convenience API for color handling, like: add, subtract,
shade, HSL color-space conversion, packing and unpacking.
* clutter-private.h: Update ClutterMainContext, and export the
main context pointer here.
* clutter-rectangle.h:
* clutter-rectangle.c: Update the color-related code; make
clutter_rectangle_new() and empty constructor and provide
clutter_rectangle_new_with_color(); provide color setter
and getter API.
* clutter-label.h:
* clutter-label.c: Rename the "font" property to "font-name";
update the color-related code to the new ClutterColor object;
rename clutter_label_new() to clutter_label_new_with_text(),
and add setters and getters for the properties.
* clutter-marshal.list: Add VOID:OBJECT and VOID:BOXED marshallers
generators.
* clutter-stage.h:
* clutter-stage.c: Rework the API: provide a default constructor
for a singleton object, named clutter_stage_get_default(), which
supercedes the clutter_stage() function in clutter-main; provide
new events: button-press-event, button-release-event,
key-press-event and key-release-event; update the color-related
code;
(clutter_stage_snapshot): Allow negative width and height when
taking a snapshot (meaning: use full width/height).
(clutter_stage_get_element_at_pos): Rename clutter_stage_pick().
* clutter-element.c (clutter_element_paint): Clean up the
stage and color related code.
* clutter-event.h:
* clutter-event.c: Add generic ClutterAnyEvent type; add
clutter_event_new(), clutter_event_copy() and clutter_event_free();
make ClutterEvent a boxed type.
* clutter-main.h:
* clutter-main.c: Remove clutter_stage(); add clutter_main_quit(),
for cleanly quitting from clutter_main(); add multiple mainloops
support; allocate the ClutterCntx instead of adding it to the
stack; re-work the ClutterEvent dispatching.
* clutter-group.c (clutter_group_add), (clutter_group_remove): Keep
a reference on the element when added to a ClutterGroup.
* examples/rects.py
* examples/test.c:
* examples/test-text.c:
* examples/video-cube.c:
* examples/super-oh.c:
* examples/test-video.c: Update.
2006-06-05 09:38:31 -04:00
|
|
|
clutter_group_add (CLUTTER_GROUP (stage), texture);
|
|
|
|
clutter_group_add (CLUTTER_GROUP (stage), vtexture);
|
|
|
|
clutter_group_show_all (CLUTTER_GROUP (stage));
|
2006-05-29 04:59:36 -04:00
|
|
|
|
|
|
|
if (!clutter_video_texture_play(CLUTTER_VIDEO_TEXTURE(vtexture), NULL))
|
|
|
|
g_error("failed to play vtexture");
|
|
|
|
|
|
|
|
clutter_main();
|
|
|
|
|
2006-06-05 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-color.h:
* clutter-color.c: Reimplement ClutterColor as a boxed type;
add convenience API for color handling, like: add, subtract,
shade, HSL color-space conversion, packing and unpacking.
* clutter-private.h: Update ClutterMainContext, and export the
main context pointer here.
* clutter-rectangle.h:
* clutter-rectangle.c: Update the color-related code; make
clutter_rectangle_new() and empty constructor and provide
clutter_rectangle_new_with_color(); provide color setter
and getter API.
* clutter-label.h:
* clutter-label.c: Rename the "font" property to "font-name";
update the color-related code to the new ClutterColor object;
rename clutter_label_new() to clutter_label_new_with_text(),
and add setters and getters for the properties.
* clutter-marshal.list: Add VOID:OBJECT and VOID:BOXED marshallers
generators.
* clutter-stage.h:
* clutter-stage.c: Rework the API: provide a default constructor
for a singleton object, named clutter_stage_get_default(), which
supercedes the clutter_stage() function in clutter-main; provide
new events: button-press-event, button-release-event,
key-press-event and key-release-event; update the color-related
code;
(clutter_stage_snapshot): Allow negative width and height when
taking a snapshot (meaning: use full width/height).
(clutter_stage_get_element_at_pos): Rename clutter_stage_pick().
* clutter-element.c (clutter_element_paint): Clean up the
stage and color related code.
* clutter-event.h:
* clutter-event.c: Add generic ClutterAnyEvent type; add
clutter_event_new(), clutter_event_copy() and clutter_event_free();
make ClutterEvent a boxed type.
* clutter-main.h:
* clutter-main.c: Remove clutter_stage(); add clutter_main_quit(),
for cleanly quitting from clutter_main(); add multiple mainloops
support; allocate the ClutterCntx instead of adding it to the
stack; re-work the ClutterEvent dispatching.
* clutter-group.c (clutter_group_add), (clutter_group_remove): Keep
a reference on the element when added to a ClutterGroup.
* examples/rects.py
* examples/test.c:
* examples/test-text.c:
* examples/video-cube.c:
* examples/super-oh.c:
* examples/test-video.c: Update.
2006-06-05 09:38:31 -04:00
|
|
|
g_object_unref (stage);
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|