VISH  0.2
WebVish.cpp

Demonstration how to implement a http protocol using the VISH socket callback mechanisms.

#include <ocean/plankton/VModules.hpp>
#include <ocean/plankton/VCreator.hpp>
#include <ocean/streams/VSocketAction.hpp>

#include <ocean/shrimp/ColorTypes.hpp>
#include <ocean/streams/VStream.hpp>
#include <ocean/Anemonia/VFrameBuffer.hpp>

#include <ocean/Anemonia/Viewer.hpp>
#include <ocean/plankton/VTime.hpp>

#include <memcore/stringutil.hpp>

using namespace Wizt;

extern bool WriteJpegStream(int sockid,
                            const Wizt::rgb_t*data,
                            int   width, int height);

struct  CreateObjectHtml : Wizt::VManagedObjectIterator
{
        string  html;

        override bool apply(int priority,
                            const RefPtr<VManagedObject>&VC, const std::string&ModulePath)
        {
                if (RefPtr<VCreatorBase> VB = VC)
                {
                        html += "<FORM ACTION=/create><INPUT TYPE=SUBMIT VALUE=\"" + VB->Name() + "\"></FORM>"
                                + "\n";
                }
                return true;
        }
};

struct  NetworkObjectHtml : Wizt::VManagedObjectIterator
{
        string  html;

        struct  ObjectInput : VObjectInputIterator
        {
                string  who;
                string  &html;

                ObjectInput(const string&Who, string&Html)
                : who(Who), html(Html)
                {}

                override bool apply(const RefPtr<VSlot>&S, int lv)
                {
                        if (!S)
                                return true; 

                        if (!S->getParameter() )
                                return true;


                        // local context not supported yet
                RefPtr<ValuePool> Context; 
                RefPtr<VValueBase> Val = S->getParameter()->getValue(Context, "");
                        if (Val)
                        {
//                              fprintf(stderr, "WEBGET: %s\n", S->name.c_str() ); 
//                              fprintf(stderr, "WEBGET P: %s\n", S->param->Name().c_str() ); 
//                              fprintf(stderr, "WEBGET V: %s\n", Val->Text().c_str() );

                        string What = Val -> Text() ; 
                                html += "<TR><TD><FORM ACTION=\"/objects/" + who + "\" METHOD=GET>"
                                        "<INPUT VALUE=\"" + S->SlotName() + "\" TYPE=SUBMIT>\n"
                                        "<INPUT VALUE=\""+What+"\" NAME=\"" + S->SlotName() + "\"></FORM></TD></TR>\n";
                        }


                        //              html += "</TR>\n"; 
                        return true;
                }
        };

        override bool apply(int priority,
                            const RefPtr<VManagedObject>&V, const std::string&ModulePath)
        {
        RefPtr<VObject> VObj = V; 
                if (!VObj)
                        return true; 

                html += "<TABLE BORDER=2><TR><TH COLSPAN=2>" + VObj->Name() + "</TH></TR>\n";
        ObjectInput OI(VObj->Name(), html);
                VObj->iterateInputs(OI);

                html += "</TABLE>\n"; 
                return true;
        }
};



class   WebVish : public VObject
{
public:
        TypedSlot<VSocketAction>  Network;

        TypedSlot<VFrameBuffer> RenderSource;


        void    writeSnapshot(socket_t id)
        {
                if (!RenderSource->getSource() )
                {
                rgb_t   data[100]; 
                        for(int i=0; i<100; i++)
                        {
                                data[i][0] = 255; 
                                data[i][1] = 128; 
                                data[i][2] =   0;
                        }
                        WriteJpegStream(id, data, 10, 10); 
                        VStream::close( id );
                        return;
                } 

        RefPtr<Viewer> MyViewer = VObject::find("Viewer1"); 
                if (!MyViewer)
                {
                        puts("UGH. No viewer. aborting screenshot");
                        return;
                }

        RefPtr<ValuePool> Context = MyViewer->myValuePool; 
                if (Context)
                {
                        puts("yay, got context");
                } 
                else
                        puts("nope, no context");

        VFrameBuffer    FB;
                RenderSource << Context >> FB; 
                if (!FB.MyRenderer)
                {
                rgb_t   data[100];
                        for(int i=0; i<100; i++)
                        {
                                data[i][0] = 255;
                                data[i][1] =   0;
                                data[i][2] =   0;
                        }
                        WriteJpegStream(id, data, 10, 10);
                        VStream::close( id );
                        return;
                }

                struct  GetAndSendFrame : VFrameBuffer::Grab
                {
                        socket_t id;
                        VFrameBuffer::Size ImgSize;

                        GetAndSendFrame(socket_t fd)
                        : id(fd)
                        , ImgSize(0,0)
                        {}

                        override string setUrl(const string&url,const string&,const VFrameBuffer::MetaInfo&)
                        {
                                return "";
                        }
                        
                        override VFrameBuffer::format preferredFormat() const
                        {
                                return VFrameBuffer::rgb;
                        }
                        
                        override const type_info&pixeltype() const
                        {
                                return typeid(unsigned char);
                        } 

                        override const type_info&depthtype() const
                        {
                                return typeid(void);
                        }

                        override bool openLayer(const VFrameBuffer::Size&FullSize,
                                                const VFrameBuffer::Size&SizeOfThisTile,
                                                const Layer&)
                        {
                                ImgSize = FullSize; 
                                return true;
                        }

                        // Note: tiled retrieval is NOT supported here
                        override bool retrieve(const ImageTile&ImageData)
                        {
                                if (RefPtr<Chunk<rgb_t> > RGBdata = ImageData.ColorData)
                                        WriteJpegStream( id , (*RGBdata)(), ImgSize.x, ImgSize.y); 

                                return true;
                        }

                        override bool closeLayer()
                        {
                                return true;
                        }
                }; 

        VRequest        Source(false);
        GetAndSendFrame GASF(id); 
                FB.MyRenderer->grab(Source, GASF, VFrameBuffer::Size(1024, 768 ), NullPtr() ); 
                VStream::close( id );
        }


static  void    ZoomInOut(double howmuch)
        {
        RefPtr<Viewer> MyViewer = VObject::find("Viewer1"); 
                if (!MyViewer)
                {
                        puts("UGH. No viewer. aborting screenshot");
                        return;
                }

        RefPtr<ValuePool> Context = MyViewer->myValuePool; 
                if (!Context)
                        puts("nope, no context"); 

                // 
                // Find a parent object of the Viewer thatimplements a type VCamera. 
                // This is the one that will control the Viewer, and we will have 
                // to modify this one then based on the given zoom request.
                //
                struct FB : VObjectIterator
                {
                        RefPtr<VSlot> ViewerCameraInput;

                        override bool apply(const RefPtr<VObject>&vobj,
                                            const string&name,
                                            const type_info&type)
                        {
                                ViewerCameraInput = vobj->getImplementationSlot( typeid(VCamera), "",""); 
                                if (ViewerCameraInput)
                                {
                                        return false; // terminate iteration
                                } 
                                else
                                        puts("nope, no camera providing input object");

                                return true;
                        }       
                }; 
        FB fb; 
                MyViewer->iterateParents(fb);

                if (!fb.ViewerCameraInput)
                {
                        puts("empty viewer"); 
                        return;
                } 

        RefPtr<VValueParameter<VCamera> > ViewerControlCameraParameter = fb.ViewerCameraInput->getParameter();
                if (!ViewerControlCameraParameter)
                {
                        puts("nope, no camera available"); 
                        return;
                } 

                // 
                // Okee, got the actual camera parameter, now modify it. 
                //

        VCamera TheCamera; 
                ViewerControlCameraParameter->getValue(TheCamera, Context, "");

        VCamera::tvector3 viewline = TheCamera.Observer - TheCamera.LookAt; 

                TheCamera.Observer += howmuch*viewline;
                TheCamera.LookAt   += howmuch*viewline; 

                ViewerControlCameraParameter->setValue(TheCamera, Context, "", false, NullPtr() );
        }

static  void    ChangeTime(double howmuch)
        {
        RefPtr<VParameter> TimeParameter = VObject::findUniqueOutputObject( typeid(VTime) ); 
        RefPtr<VValueParameter<VTime> > TheTimes = TimeParameter; 

                if (!TheTimes) return; 


        RefPtr<VValue<double> > TMin = TheTimes->getProperty( VPROPERTY_VTIME_MIN ),
                                TMax = TheTimes->getProperty( VPROPERTY_VTIME_MAX ); 

        double  Tmin = 0, Tmax = 1.0; 
                if (TMin) TMin->getValue(Tmin); 
                if (TMax) TMax->getValue(Tmax);

        RefPtr<ValuePool> Context;

        VTime   Tnow; 
                TheTimes->getValue(Tnow, Context, ""); 

        double  t = Tnow(); 
                t += howmuch*(Tmax - Tmin); 
                if (t<Tmin) t = Tmin; 
                if (t>Tmax) t = Tmax;

                Tnow() = t; 

                TheTimes->setValue(Tnow, Context, "", false, NullPtr() );
        }

        struct  Client : VSocketAction::Receiver
        {
                WeakPtr<WebVish> WV;

                Client(const WeakPtr<WebVish>&wv)
                : WV(wv)
                {}

                ~Client()
                {
                        puts("WebVish:: ~Client()");
                }

                override bool receive(socket_t id)
                {
                fprintf(stderr, "override bool WebVish::Client ->receive(socket_t id)\n"); fflush(stderr);

                string  gotit = VStream::recv(id);
                fprintf(stderr, "override bool WebVish::Client ->receive(socket_t id) - GOT data\n"); fflush(stderr);
/*
GET / HTTP/1.1
Host: localhost:7000
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.8) Gecko/20071008 Firefox/2.0.0.8
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,* / *;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0
 */
                vector< string > HTTP; 
                        split(gotit, HTTP, ' ', false, '\n'); 

                        if (HTTP.size()>1)
                        {
                                printf("HTTP COMMAND %s\n", HTTP[0].c_str() ); 
                                printf("HTTP QUERY [%s]\n", HTTP[1].c_str() ); 

                                if (HTTP[0] == "GET")
                                {
                                        if (HTTP[1] == "/Viewer1.jpg")
                                        {
                                                puts("sending snapshot...");
                                                WV->writeSnapshot(id); 
                                                return false;
                                        }
                                        else if (HTTP[1] == "/Viewer1?moveCamera=ZoomOut")
                                        {
                                                ZoomInOut( 0.5);
                                        }
                                        else if (HTTP[1] == "/Viewer1?moveCamera=ZoomIn")
                                        {
                                                ZoomInOut(-0.5);
                                        }
                                        else if (HTTP[1] == "/Viewer1?time=past")
                                        {
                                                ChangeTime(-0.01);
                                        }
                                        else if (HTTP[1] == "/Viewer1?time=future")
                                        {
                                                ChangeTime( 0.01);
                                        } 
                                        else
                                                puts("Unknown HTTP path");
                                }
                        }
                CreateObjectHtml CreationHTML; 
                        VCreatorBase::traverse( CreationHTML ); 
                
                NetworkObjectHtml NetHTML;
                        VObject::traverse( NetHTML );

                        VStream::send( id,
                                       string(
                                               "<HTML>"
                                               "<TITLE>VISH Control Site</TITLE>"
                                               "<BODY>\n")
                                       +
                                       "<H2 ALIGN=CENTER><A HREF=/>WebVISH Control Suite</A></H2>\n"
                                       "Click left image area for navigation into past, right for navigation into future,"
                                       "Upper central area for zooming out, lower central area for zooming in.<BR>"

                                       "<MAP NAME=\"Viewer1\">"
                                       "<AREA HREF=\"/Viewer1?time=past\"          SHAPE=RECT COORDS=\"0,0,256,768\">"
                                       "<AREA HREF=\"/Viewer1?moveCamera=ZoomOut\" SHAPE=RECT COORDS=\"256,0,768,384\">"
                                       "<AREA HREF=\"/Viewer1?moveCamera=ZoomIn\"  SHAPE=RECT COORDS=\"0,384,768,768\">"
                                       "<AREA HREF=\"/Viewer1?time=future\"        SHAPE=RECT COORDS=\"768,0,1024,768\">"
                                       "</MAP>"
                                       +
                                       "<TABLE BORDER=4><TR> <TD> <IMG SRC=\"/Viewer1.jpg\" USEMAP=\"#Viewer1\" ISMAP></TD></TR></TABLE><BR>\n"
                                       +
                                               NetHTML.html
                                       +
                                       "<P><HR><P>"
                                       +
                                               CreationHTML.html
                                       +
                                       "</BODY>\n"
                                       "</HTML>"
                                );

                        VStream::close( id );

                        return false;
                }
        };

        struct  AcceptConnections : VSocketAction::Connector
        {
                WeakPtr<WebVish> WV;

                AcceptConnections(const WeakPtr<WebVish>&W)
                : WV(W)
                {}

                ~AcceptConnections()
                {
                        fprintf(stderr, "~AcceptConnections()\n"); fflush(stderr);
                }


                bool accept(socket_t fd)
                {
/*
  Could always send a greeting her, but is not good for HTTP stuff.
*/
#if 0
                        VStream::send( fd, "HTTP/1.1 200 OK\r\n"
                                       // "Date: Wed, 24 Oct 2007 02:59:46 GMT"
                                       "Server: VISH\r\n"
                                       "Connection: close\r\n"
                                       "Content-Type: text/html; charset=UTF-8\r\n"
                                       "\r\n"
                                       );
#endif

                        if (RefPtr<VValue<VSocketAction> > N = **(WV->Network) )
                        {
                                N->add(fd, VStream::getPeername(fd).hostname, new Client(WV) );
                        }

                        return true;
                }
        };

        RefPtr<AcceptConnections> AC;

        WebVish(const string&name, int p, const RefPtr<VCreationPreferences>&VP)
        : VObject(name, p, VP)
        , Network(this, "webserver", VP, 0 )
        , RenderSource(this, "viewer", VFrameBuffer() )
        {
                attachUniqueObject( RenderSource, NullPtr(), false);

                AC = new AcceptConnections( self() ); 

                if (RefPtr<VValue<VSocketAction> > N = **Network)
                {
                        N->listen( 7007, AC ); 
                        puts("WebVish on http://localhost:7007");
                }
                else
                {
                        puts("\n*** WEBVISH ERROR: No Network Implementation available! ***\n");
                }
        }

        override bool update(VRequest&R, double precision)
        {
                return true;
        }
};

static  Ref<VCreator<WebVish> > myCreator("Create/Webserver", ObjectQuality::BETA);

VISH_DEFAULT_INIT