|
- /*
- * Copyright (c) 2012 Mark McCurry
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
- /**
- * @file ports.h
- * Collection of functions for ports.
- * This includes dispatchin, reading metadata etc.
- */
-
- #ifndef RTOSC_PORTS
- #define RTOSC_PORTS
-
- #include <vector>
- #include <functional>
- #include <initializer_list>
- #include <rtosc/rtosc.h>
- #include <rtosc/rtosc-version.h>
- #include <cstring>
- #include <cctype>
- #include <cstdlib>
- #include <cstdio>
- #include <string>
-
- namespace rtosc {
-
- //First define all types
- typedef const char *msg_t;
-
- struct Port;
- struct Ports;
-
- //! data object for the dispatch routine
- struct RtData
- {
- RtData(void);
-
- /**
- * Location of where the dispatch routine is currently being called.
- * If non-NULL, the dispatch routine will update the port name here while
- * walking through the Ports tree
- */
- char *loc;
- size_t loc_size;
- void *obj; //!< runtime object to dispatch this object to
- int matches; //!< number of matches returned from dispatch routine
- const Port *port; //!< dispatch will write the matching port's pointer here
- //! @brief Will be set to point to the full OSC message in case of
- //! a base dispatch
- const char *message;
-
- int idx[16];
- void push_index(int ind);
- void pop_index(void);
-
- virtual void replyArray(const char *path, const char *args,
- rtosc_arg_t *vals);
- virtual void reply(const char *path, const char *args, ...);
- //!Reply if information has been requested
- virtual void reply(const char *msg);
- virtual void chain(const char *path, const char *args, ...);
- //!Bypass message to some kind of backend if the message can not be handled
- virtual void chain(const char *msg);
- virtual void chainArray(const char *path, const char *args,
- rtosc_arg_t *vals);
- //!Transmit initialization/change of a value to all listeners
- virtual void broadcast(const char *path, const char *args, ...);
- virtual void broadcast(const char *msg);
- virtual void broadcastArray(const char *path, const char *args,
- rtosc_arg_t *vals);
-
- virtual void forward(const char *rational=NULL);
- };
-
-
- /**
- * Port in rtosc dispatching hierarchy
- */
- struct Port {
- const char *name; //!< Pattern for messages to match
- const char *metadata;//!< Statically accessable data about port
- const Ports *ports; //!< Pointer to further ports
- std::function<void(msg_t, RtData&)> cb;//!< Callback for matching functions
-
- class MetaIterator
- {
- public:
- MetaIterator(const char *str);
-
- //A bit odd to return yourself, but it seems to work for this
- //context
- const MetaIterator& operator*(void) const {return *this;}
- const MetaIterator* operator->(void) const {return this;}
- bool operator==(MetaIterator a) {return title == a.title;}
- bool operator!=(MetaIterator a) {return title != a.title;}
- MetaIterator& operator++(void);
- operator bool() const;
-
- const char *title;
- const char *value;
- };
-
- class MetaContainer
- {
- public:
- MetaContainer(const char *str_);
-
- MetaIterator begin(void) const;
- MetaIterator end(void) const;
-
- MetaIterator find(const char *str) const;
- size_t length(void) const;
- //!Return the key to the value @p str, or NULL if the key is
- //!invalid or if there's no value for that key.
- const char *operator[](const char *str) const;
-
- const char *str_ptr;
- };
-
- MetaContainer meta(void) const
- {
- if(metadata && *metadata == ':')
- return MetaContainer(metadata+1);
- else
- return MetaContainer(metadata);
- }
-
- };
-
- /**
- * Ports - a dispatchable collection of Port entries
- *
- * This structure makes it somewhat easier to perform actions on collections of
- * port entries and it is responsible for the dispatching of OSC messages to
- * their respective ports.
- * That said, it is a very simple structure, which uses a stl container to store
- * all data in a simple dispatch table.
- * All methods post-initialization are RT safe (assuming callbacks are RT safe)
- */
- struct Ports
- {
- std::vector<Port> ports;
- std::function<void(msg_t, RtData&)> default_handler;
-
- typedef std::vector<Port>::const_iterator itr_t;
-
- /**Forwards to builtin container*/
- itr_t begin() const {return ports.begin();}
-
- /**Forwards to builtin container*/
- itr_t end() const {return ports.end();}
-
- /**Forwards to builtin container*/
- size_t size() const {return ports.size();}
-
- /**Forwards to builtin container*/
- const Port &operator[](unsigned i) const {return ports[i];}
-
- Ports(std::initializer_list<Port> l);
- ~Ports(void);
-
- Ports(const Ports&) = delete;
-
- /**
- * Dispatches message to all matching ports.
- * This uses simple pattern matching available in rtosc::match.
- *
- * @param m A valid OSC message. Note that the address part shall not
- * contain any type specifier.
- * @param d The RtData object shall contain a path buffer (or null), the length of
- * the buffer, a pointer to data. It must also contain the location
- * if an answer is being expected.
- * @param base_dispatch Whether the OSC path is to be interpreted as a full
- * OSC path beginning at the root
- */
- void dispatch(const char *m, RtData &d, bool base_dispatch=false) const;
-
- /**
- * Retrieve local port by name
- * TODO implement full matching
- */
- const Port *operator[](const char *name) const;
-
-
- /**
- * Find the best match for a given path
- *
- * @parameter path partial OSC path
- * @returns first path prefixed by the argument
- *
- * Example usage:
- * @code
- * Ports p = {{"foo",0,0,dummy_method},
- * {"flam",0,0,dummy_method},
- * {"bar",0,0,dummy_method}};
- * p.apropos("/b")->name;//bar
- * p.apropos("/f")->name;//foo
- * p.apropos("/fl")->name;//flam
- * p.apropos("/gg");//NULL
- * @endcode
- */
- const Port *apropos(const char *path) const;
-
- /**
- * Collapse path with parent path identifiers "/.."
- *
- * e.g. /foo/bar/../baz => /foo/baz
- */
- static char *collapsePath(char *p);
-
- protected:
- void refreshMagic(void);
- private:
- //Performance hacks
- class Port_Matcher *impl;
- unsigned elms;
- };
-
- struct ClonePort
- {
- const char *name;
- std::function<void(msg_t, RtData&)> cb;
- };
-
- struct ClonePorts:public Ports
- {
- ClonePorts(const Ports &p,
- std::initializer_list<ClonePort> c);
- };
-
- struct MergePorts:public Ports
- {
- MergePorts(std::initializer_list<const Ports*> c);
- };
-
- /**
- * Return a port's default value
- *
- * Returns the default value of a given port, if any exists, as a string.
- * For the parameters, see the overloaded function.
- * @note The recursive parameter should never be specified.
- * @return The default value(s), pretty-printed, or NULL if there is no
- * valid default annotation
- */
- const char* get_default_value(const char* port_name, const Ports& ports,
- void* runtime, const Port* port_hint = NULL,
- int32_t idx = -1, int recursive = 1);
-
- /**
- * Return a port's default value
- *
- * Returns the default value of a given port, if any exists, as an array of
- * rtosc_arg_vals . The values in the resulting array are being canonicalized,
- * i.e. mapped values are being converted to integers; see
- * canonicalize_arg_vals() .
- *
- * @param port_name the port's OSC path.
- * @param port_args the port's arguments, e.g. '::i:c:S'
- * @param ports the ports where @a portname is to be searched
- * @param runtime object holding @a ports . Optional. Helps finding
- * default values dependent on others, such as presets.
- * @param port_hint The port itself corresponding to portname (including
- * the args). If not specified, will be found using @p portname .
- * @param idx If the port is an array (using the '#' notation), this specifies
- * the index required for the default value
- * @param n Size of the output parameter @res . This size can not be known,
- * so you should provide a large enough array.
- * @param res The output parameter for the argument values.
- * @param strbuf String buffer for storing pretty printed strings and blobs.
- * @param strbufsize Size of @p strbuf
- * @return The actual number of aruments written to @p res (can be smaller
- * than @p n) or -1 if there is no valid default annotation
- */
- int get_default_value(const char* port_name, const char *port_args,
- const Ports& ports,
- void* runtime, const Port* port_hint,
- int32_t idx,
- size_t n, rtosc_arg_val_t* res,
- char *strbuf, size_t strbufsize);
-
-
- /**
- * Return a string list of all changed values
- *
- * Return a human readable list of the value that changed
- * corresponding to the rDefault macro
- * @param ports The static ports structure
- * @param runtime The runtime object
- * @return The list of ports and their changed values, linewise
- */
- std::string get_changed_values(const Ports& ports, void* runtime);
-
- //! Class to modify messages loaded from savefiles.
- //! Objects of this class shall be passed to savefile loading routines. You can
- //! inherit to change the behaviour, e.g. to modify or discard such messages.
- class savefile_dispatcher_t
- {
- const Ports* ports;
- void* runtime;
- char loc[1024];
-
- protected:
- enum proceed {
- abort = -2, //!< the message shall lead to abort the savefile loading
- discard = -1 //!< the message shall not be dispatched
- };
-
- enum dependency_t {
- no_dependencies, //! default values don't depend on others
- has_dependencies, //! default values do depend on others
- not_specified //! it's not know which of the other enum values fit
- };
-
- rtosc_version rtosc_filever, //!< rtosc versinon savefile was written with
- rtosc_curver, //!< rtosc version of this library
- app_filever, //!< app version savefile was written with
- app_curver; //!< current app version
-
- //! call this to dispatch a message
- void operator()(const char* msg);
-
- static int default_response(size_t nargs, bool first_round,
- dependency_t dependency);
-
- private:
- //! callback for when a message shall be dispatched
- //! implement this if you need to change a message
- virtual int on_dispatch(size_t portname_max, char* portname,
- size_t maxargs, size_t nargs,
- rtosc_arg_val_t* args,
- bool round2, dependency_t dependency);
-
- friend int dispatch_printed_messages(const char* messages,
- const Ports& ports, void* runtime,
- savefile_dispatcher_t *dispatcher);
-
- friend int load_from_file(const char* file_content,
- const Ports& ports, void* runtime,
- const char* appname,
- rtosc_version appver,
- savefile_dispatcher_t* dispatcher);
- };
-
- /**
- * Scan OSC messages from human readable format and dispatch them.
- * @param messages The OSC messages, whitespace-separated
- * @param ports The static ports structure
- * @param runtime The runtime object
- * @param dispatcher Object to modify messages prior to dispatching, or NULL.
- * You can overwrite its virtual functions, and you should specify any of the
- * version structs if needed. All other members shall not be initialized.
- * @return The number of messages read, or, if there was a read error,
- * the number of bytes read until the read error occured minus one
- */
- int dispatch_printed_messages(const char* messages,
- const Ports& ports, void* runtime,
- savefile_dispatcher_t *dispatcher = NULL);
-
- /**
- * Return a savefile containing all values that differ from the default values.
- * @param ports The static ports structure
- * @param runtime The runtime object
- * @param appname Name of the application calling this function
- * @param appver Version of the application calling this function
- * @return The resulting savefile as an std::sting
- */
- std::string save_to_file(const Ports& ports, void* runtime,
- const char* appname, rtosc_version appver);
-
- /**
- * Read save file and dispatch contained parameters.
- * @param file_content The file as a C string
- * @param ports The static ports structure
- * @param runtime The runtime object
- * @param appname Name of the application calling this function; must
- * match the file's application name
- * @param appver Version of the application calling this function
- * @param dispatcher Modifier for the messages; NULL if no modifiers are needed
- * @return The number of messages read, or, if there was a read error,
- * the negated number of bytes read until the read error occured minus one
- */
- int load_from_file(const char* file_content,
- const Ports& ports, void* runtime,
- const char* appname,
- rtosc_version appver,
- savefile_dispatcher_t* dispatcher = NULL);
-
- /**
- * Convert given argument values to their canonical representation.
- *
- * The ports first (or-wise) argument types are defined as canonical.
- * E.g. if passing two 'S' argument values, the
- * port could be `portname::ii:cc:SS` or `portname::ii:t`.
- *
- * @param av The input and output argument values
- * @param n The size of @p av
- * @param port_args The port arguments string, e.g. `::i:c:s`. The first
- * non-colon letter sequence marks the canonical types
- * @param meta The port's metadata container
- * @return The number of argument values that should have need conversion,
- * but failed, e.g. because of values missing in rMap.
- */
- int canonicalize_arg_vals(rtosc_arg_val_t* av, size_t n,
- const char* port_args, Port::MetaContainer meta);
-
- /**
- * Convert each of the given arguments to their mapped symbol, if possible.
- * @param av The input and output argument values
- * @param n The size of @p av
- * @param meta The port's metadata container
- */
- void map_arg_vals(rtosc_arg_val_t* av, size_t n,
- Port::MetaContainer meta);
-
- /*********************
- * Port walking code *
- *********************/
- //typedef std::function<void(const Port*,const char*)> port_walker_t;
- /**
- * Function pointer type for port walking.
- *
- * accepts:
- * - the currently walked port
- * - the port's absolute location
- * - the part of the location which makes up the port; this is usually the
- * location's substring after the last slash, but it can also contain multiple
- * slashes
- * - the port's base, i.e. it's parent Ports struct
- * - the custom data supplied to walk_ports
- * - the runtime object (which may be NULL if not known)
- */
- typedef void(*port_walker_t)(const Port*,const char*,const char*,
- const Ports&,void*,void*);
-
- /**
- * Call a function on all ports and subports.
- * @param base The base port of traversing
- * @param name_buffer Buffer which will be filled with the port name; must be
- * reset to zero over the full length!
- * @param buffer_size Size of name_buffer
- * @param data Data that should be available in the callback
- * @param walker Callback function
- */
- void walk_ports(const Ports *base,
- char *name_buffer,
- size_t buffer_size,
- void *data,
- port_walker_t walker,
- void *runtime = NULL);
-
- /**
- * Return the index with value @p value from the metadata's enumeration.
- * @param meta The metadata
- * @param value The value to search the key for
- * @return The first key holding value, or `std::numeric_limits<int>::min()`
- * if none was found
- */
- int enum_key(Port::MetaContainer meta, const char* value);
-
- /*********************
- * Port Dumping code *
- *********************/
-
- struct OscDocFormatter
- {
- const Ports *p;
- std::string prog_name;
- std::string uri;
- std::string doc_origin;
- std::string author_first;
- std::string author_last;
- //TODO extend this some more
- };
-
- std::ostream &operator<<(std::ostream &o, OscDocFormatter &formatter);
- };
- #endif
|