/* -*- C++ -*- * Copyright ©2004 Hugo Mills * * This software is distributed under the terms of the GNU GPL v3 * For more information on the GPL, see the file COPYING or * visit http://www.gnu.org/ * * This software is distributed without warranty */ #include "magellan/configuration.h" #include #include #include #include "config.h" #include "magellan/configlexer.h" #include "plugins.h" #include "magellan/xfsphere.h" #include "magellan/parsertools.h" #include "magellan/options.h" #include "rendercontext.h" #include "rendercontextstacked.h" enum RS_Type { RS_NONE, RS_NAMED, RS_TYPED, RS_NAMED_TYPED }; /* Inserter object: * Used by readsection to add an object to a map * Also adds to a list of plugins */ template class Inserter_Map { public: Inserter_Map(std::map& mp, std::list& ps) : target(&mp), plugins(&ps) { } virtual ~Inserter_Map() { } void insert(const std::string& key, C* value) { if(target->find(key) != target->end()) std::cerr << "Section '" << key << "' exists already" << std::endl; else { value->name = key; (*target)[key] = value; plugins->push_back(value); if(opts->debug_parse() >= 4 || opts->debug_render() >= 2) std::cerr << " " << key << " created at " << value << std::endl; } } private: std::map* target; std::list* plugins; }; /* Inserter object: * Used by readsection to add an object to a simple pointer, * overwriting it if it was already initialised. e.g. we only expect * one Output object. */ template class Inserter_Ptr { public: Inserter_Ptr(C& mp, std::list& ps) : target(&mp), plugins(&ps) { } virtual ~Inserter_Ptr() { } void insert(const std::string& key, C value) const { if(*target != NULL) std::cerr << "Section '" << key << "' exists already" << std::endl; else { value->name = key; *target = value; plugins->push_back(value); if(opts->debug_parse() >= 4 || opts->debug_render() >= 2) std::cerr << " " << key << " created at " << value << std::endl; } } private: C* target; std::list* plugins; }; /* Inserter object: * Used by readsection to add an object to a set. e.g. in the case of * MDisplay objects, where we don't need to look them up, but do need * to be able to iterate over them. */ template class Inserter_List { public: Inserter_List(std::list& mp, std::list& ps) : target(&mp), plugins(&ps) { } virtual ~Inserter_List() { } void insert(const std::string& key, C* value) { value->name = key; target->push_back(value); plugins->push_back(value); if(opts->debug_parse() >= 4 || opts->debug_render() >= 2) std::cerr << " " << key << " created at " << value << std::endl; } private: std::list* target; std::list* plugins; }; /* Iterates over a list of plugins, and tests to see whether they will * accept a configuration section of the given type/subtype. */ template typename F::handled_class* run_list(ConfigLexer* lex, const std::string& type, const std::string& subtype, const std::list& pluginlist) { try { for(typename std::list::const_iterator plug = pluginlist.begin(); plug != pluginlist.end(); plug++) { typename F::handled_class* m = (*plug)->parser(lex, type, subtype); if(m) { if(opts->debug_parse() >= 2) std::cerr << " Accepted by: " << (*plug)->name << std::endl; lex->next(); return m; } } } catch(...) { std::cerr << "Caught error: recovering to next section" << std::endl; lex->section_next(); std::cerr << "Recovered." << std::endl; return NULL; } std::cerr << "No plugin accepted config '" << type << "', '" << subtype << "'. Skipping." << std::endl; return NULL; } /* Validates the current input token as the beginning of a section of * the given type */ bool check_type(ConfigLexer* lex, const char* sectype) { if(lex->type() != TOKEN) { std::cerr << "Syntax error -- not a token: '" << lex->value() << "' (parser_sections.h " << __LINE__ << ")" << std::endl; // Syntax error -- not a token return false; } if(lex->value() != sectype) return false; lex->next(); return true; } std::string get_name(ConfigLexer* lex) { if(lex->type() != STRING) { // Error std::cerr << "FAILED" << std::endl; return ""; } std::string rv = lex->value(); lex->next(); return rv; } bool get_types(ConfigLexer* lex, std::string& type, std::string& subtype) { try { if(!readkeyedvalue(lex, "type", &type)) { std::cerr << "Type not specified" << std::endl; return false; } if(!readkeyedvalue(lex, "subtype", &subtype)) subtype = ""; } catch(NotAToken natex) { } return true; } /* Read the first bit of a config section, including obtaining the * type information, if present and necessary. * Then find a plugin of the given type which is willing to handle that * configuration section. */ template bool readsection(ConfigLexer* lex, enum RS_Type rst, const char* sectype, const std::list& pluginlist, Inst& inserter) { if(!check_type(lex, sectype)) return false; if(opts->debug_parse() >= 1) std::cerr << "Reading section of class " << sectype << " (line " << lex->lineno() << ")" << std::endl; // Read the name of the section, if it's a named section std::string name = ""; if(rst == RS_NAMED || rst == RS_NAMED_TYPED) { name = get_name(lex); if(opts->debug_parse() >= 2) std::cerr << " Named '" << name << "'" << std::endl; } // Check the opening brace if(lex->type() != CHAR || lex->value() != "{") { // Bitch return false; } lex->next(); // Read the types, if it's a typed section std::string type, subtype; if(rst == RS_TYPED || rst == RS_NAMED_TYPED) if(!get_types(lex, type, subtype)) return false; // Look for a plugin of the right type that's willing to handle // this configuration typename F::handled_class* obj = run_list(lex, type, subtype, pluginlist); if(obj == NULL) return false; // Insert the resulting config object into the relevant list inserter.insert(name, obj); return true; } Configuration::Configuration(ConfigLexer* lex) : _output(NULL) { max_threads = 4; // Kick off by moving to the first token lex->next(); // Temporary list of all plugins std::list plist; _output = NULL; while(lex->type() != END) { if(lex->type() != TOKEN) { std::cerr << "Expecting a token: '" << lex->value() << "' is unrecognised" << std::endl; lex->next(); continue; } Inserter_Ptr ipbm = Inserter_Ptr(_output, plist); Inserter_Map imo = Inserter_Map(orbits, plist); Inserter_Map imm = Inserter_Map(maps, plist); Inserter_Map imcf = Inserter_Map(content, plist); Inserter_Map imv = Inserter_Map(viewports, plist); Inserter_List imdd = Inserter_List(displays, plist); if(readsection(lex, RS_TYPED, "output", output_plugins, ipbm) || readsection(lex, RS_NAMED_TYPED, "orbit", orbit_plugins, imo) || readsection(lex, RS_NAMED_TYPED, "projection", map_plugins, imm) || readsection(lex, RS_NAMED, "content", content_plugins, imcf) || readsection(lex, RS_NAMED, "viewport", viewport_plugins, imv) || readsection(lex, RS_NONE, "display", display_plugins, imdd) ) { // All is OK } else { // Syntax error -- unknown section std::cerr << "Unknown section / illegal keyword: '" << lex->value() << "' (parser_config.cc " << __LINE__ << ")" << std::endl; lex->next(); } } // Go through all the configured objects, and attempt to bind them bindall(plist.begin(), plist.end()); #ifdef HAVE_BOOST_THREAD // Now create the rendering contexts and process them into a // rendering menu generatecontexts(); splitcontexts(); mergecontexts(); #endif } template void Configuration::bindall(Iter begin, Iter end) { bool changed; bool remainingunbound; do { changed = false; remainingunbound = false; for(Iter it = begin; it != end; ++it) { if(!(*it)->isbound()) { Plugin* bp = (*it)->bind(this); if(bp) { changed = true; *it = bp; } } if(!(*it)->isbound()) remainingunbound = true; } } while(changed); if(remainingunbound) { std::cerr << "Some plugins remain unbound. Aborting." << std::endl; exit(1); } } #ifdef HAVE_BOOST_THREAD void Configuration::generatecontexts(void) { // Go through all of the MDisplays in order, and generate a single // render context for each one. int i = 0; for(std::list::const_iterator it = displays.begin(); it != displays.end(); it++) { RenderContext* ctx = new RenderContext(i++, (*it)); contexts.push_back(ctx); } } typedef std::list::iterator Iter; void Configuration::splitcontexts(void) { bool done = false; if(opts->debug_render() >= 2) { for(Iter it = contexts.begin(); it != contexts.end(); it++) { std::cerr << "Context " << *it << " " << **it << std::endl; } } while(!done) { done = true; int A = -1; int B; // For each pair of contexts for(Iter ctxA = contexts.begin(); ctxA != contexts.end(); ctxA++) { A++; B = -1; for(Iter ctxB = contexts.begin(); ctxB != contexts.end(); ctxB++) { B++; //std::cerr << "Examining " << A << " " << B << std::endl; if(ctxA == ctxB) continue; if( ! (*ctxA)->overlaps(*ctxB) ) continue; if( (*ctxB)->contains(*ctxA) ) continue; done = false; RenderContext* leftsplit = NULL; RenderContext* rightsplit = NULL; if( ! (*ctxA)->bestsplit(&leftsplit, &rightsplit, *ctxB) ) continue; if(opts->debug_render() >= 2) { std::cerr << "Splitting " << **ctxA << std::endl << " Into: " << *leftsplit << std::endl << " " << *rightsplit << std::endl; } // Replace the entry at *ctxA with the entries in // {left,right}split. This implementation ensures that // ctxA is always valid. (Since ctxB is never equal to // ctxA, it's always valid, too). contexts.insert(ctxA, leftsplit); contexts.insert(ctxA, rightsplit); delete *ctxA; // Clean up the contained object ctxA = contexts.erase(ctxA); // Returns the next item ctxA--; // Go back one, to account for the automatic // increment in the containing loop if(opts->debug_render() >= 3) { for(Iter it = contexts.begin(); it != contexts.end(); it++) { std::cerr << "Context " << *it << " " << **it << std::endl; } } } } } if(opts->debug_render() >= 2) { std::cerr << "After splitting contexts:" << std::endl; for(Iter it = contexts.begin(); it != contexts.end(); it++) { std::cerr << "Context " << *it << " " << **it << std::endl; } } } class RenderContextPointerOrder_Position : public std::binary_function { public: virtual bool operator()(RenderContext* a, RenderContext* b) { return *a < *b; } }; class RenderContextPointerOrder_Size : public std::binary_function { public: virtual bool operator()(RenderContext* a, RenderContext* b) { return a->size() > b->size(); } }; void Configuration::mergecontexts(void) { contexts.sort(RenderContextPointerOrder_Position()); if(opts->debug_render() >= 2) { std::cerr << "After sorting contexts:" << std::endl; for(Iter it = contexts.begin(); it != contexts.end(); it++) { std::cerr << "Context " << *it << " " << **it << std::endl; } } // Merge contexts covering the same area RenderContextStacked* group = NULL; for(Iter it = contexts.begin(); it != contexts.end(); it++) { if(opts->debug_render() >= 3) { std::cerr << "Examining context " << **it << std::endl; } if(group != NULL) { // If we have a group in progress, check this context to // see if it matches the group if(opts->debug_render() >= 4) { std::cerr << "Testing overlap between " << **it << std::endl << " and " << *group << std::endl; } if( (*it)->overlaps(group) ) { if(opts->debug_render() >= 3) { std::cerr << "Merging " << **it << " into current group" << std::endl; } // It matches, so add to the group and remove it from // the list group->add(*it); it = contexts.erase(it); it--; // Go back to the previous element (to handle // the automatic advance of the for loop) } else { if(opts->debug_render() >= 3) { std::cerr << "Closing context group." << std::endl; } // The current context doesn't match the group, so // close off the group group = NULL; } } if(group == NULL) { // If we don't have a group already (or just closed one), // peek ahead to the next context to see if it matches // this one in size/position Iter next = it; next++; if(next != contexts.end()) { if(opts->debug_render() >= 4) { std::cerr << "Testing overlap between " << **it << std::endl << " and " << **next << std::endl; } if( (*it)->overlaps(*next) ) { if(opts->debug_render() >= 3) { std::cerr << "Starting new context group with " << **it << std::endl; } // It does match, so create a new group and add // the current context to it group = new RenderContextStacked(*it); *it = group; } } } } // Finally, order contexts by size contexts.sort(RenderContextPointerOrder_Size()); if(opts->debug_render() >= 1) { std::cerr << "Rendering contexts in use:" << std::endl; for(Iter it = contexts.begin(); it != contexts.end(); it++) { std::cerr << "Context " << *it << " " << **it << std::endl; } } } #endif