LCOV - code coverage report
Current view: top level - engine/interface - console.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 29.6 % 673 199
Test Date: 2025-02-21 06:59:27 Functions: 56.0 % 75 42

            Line data    Source code
       1              : // console.cpp: the console buffer, its display, and command line control
       2              : 
       3              : #include "../libprimis-headers/cube.h"
       4              : #include "../../shared/stream.h"
       5              : 
       6              : #include "console.h"
       7              : #include "control.h"
       8              : #include "cs.h"
       9              : #include "ui.h"
      10              : #include "menus.h"
      11              : 
      12              : //input.h needs rendertext's objects
      13              : #include "render/rendertext.h"
      14              : #include "render/renderttf.h"
      15              : #include "input.h"
      16              : 
      17              : #include "world/octaedit.h"
      18              : 
      19              : int commandmillis = -1;
      20              : 
      21              : struct FilesKey
      22              : {
      23              :     const int type;
      24              :     const std::string dir,
      25              :                       ext;
      26              : 
      27            0 :     FilesKey(int type, const std::string &dir, const std::string &ext) : type(type), dir(dir), ext(ext) {}
      28              : 
      29            0 :     bool operator==(const FilesKey &y) const
      30              :     {
      31            0 :         return type == y.type && dir == y.dir && ext == y.ext;
      32              :     }
      33              : };
      34              : 
      35              : template<>
      36              : struct std::hash<FilesKey>
      37              : {
      38            0 :     size_t operator()(const FilesKey &key) const
      39              :     {
      40            0 :         size_t h = 5381;
      41            0 :         for(int i = 0, k; (k = key.dir[i]); i++)
      42              :         {
      43            0 :             h = ((h<<5)+h)^k;    // bernstein k=33 xor
      44              :         }
      45            0 :         return h;
      46              :     }
      47              : };
      48              : 
      49              : class CompletionFinder
      50              : {
      51              :     public:
      52              :         enum
      53              :         {
      54              :             Files_Directory = 0,
      55              :             Files_List,
      56              :         };
      57              : 
      58              :         void resetcomplete();
      59              :         void addfilecomplete(const char *command, char *dir, char *ext);
      60              :         void addlistcomplete(const char *command, char *list);
      61              : 
      62              :         void complete(char *s, size_t maxlen, const char *cmdprefix);
      63              : 
      64              :         //print to a stream f the listcompletions in the completions filesval
      65              :         void writecompletions(std::fstream& f) const;
      66              : 
      67              :     private:
      68              : 
      69              :         struct FilesVal
      70              :         {
      71              :             public:
      72              :                 int type;
      73              :                 std::string dir,
      74              :                             ext;
      75              :                 std::vector<char *> files;
      76              : 
      77              :                 FilesVal(int type, std::string dir, std::string ext);
      78              :                 ~FilesVal();
      79              : 
      80              :                 void update();
      81              : 
      82              :             private:
      83              :                 int millis;
      84              :         };
      85              : 
      86              :         friend std::hash<FilesKey>;
      87              : 
      88              :         std::unordered_map<FilesKey, FilesVal *> completefiles;
      89              :         std::unordered_map<const char *, FilesVal *> completions;
      90              : 
      91              :         int completesize = 0;
      92              :         char *lastcomplete = nullptr;
      93              : 
      94              :         void addcomplete(const char *command, int type, char *dir, char *ext);
      95              : 
      96              :         char *prependstring(char *d, const char *s, size_t len) const;
      97              : };
      98              : 
      99            0 : CompletionFinder::FilesVal::FilesVal(int type, std::string dir, std::string ext) : type(type), dir(dir), ext(ext[0] ? std::string(ext) : ""), millis(-1)
     100              : {
     101            0 : }
     102              : 
     103            0 : CompletionFinder::FilesVal::~FilesVal()
     104              : {
     105            0 :     for(char* i : files)
     106              :     {
     107            0 :         delete[] i;
     108              :     }
     109            0 : }
     110              : 
     111            0 : void CompletionFinder::FilesVal::update()
     112              : {
     113            0 :     if(type!=Files_Directory || millis >= commandmillis)
     114              :     {
     115            0 :         return;
     116              :     }
     117              :     //first delete old cached file vector
     118            0 :     for(char* i : files)
     119              :     {
     120            0 :         delete[] i;
     121              :     }
     122              :     //generate new one
     123            0 :     listfiles(dir.c_str(), ext.c_str(), files);
     124            0 :     std::sort(files.begin(), files.end());
     125            0 :     for(uint i = 0; i < files.size(); i++)
     126              :     {
     127            0 :         if(i && !std::strcmp(files[i], files[i-1]))
     128              :         {
     129            0 :             delete[] files.at(i);
     130            0 :             files.erase(files.begin() + i);
     131            0 :             i--; //we need to make up for the element we destroyed
     132              :         }
     133              :     }
     134            0 :     millis = totalmillis;
     135              : }
     136              : 
     137            0 : void CompletionFinder::resetcomplete()
     138              : {
     139            0 :     completesize = 0;
     140            0 : }
     141              : 
     142            1 : void CompletionFinder::addfilecomplete(const char *command, char *dir, char *ext)
     143              : {
     144            1 :     addcomplete(command, Files_Directory, dir, ext);
     145            1 : }
     146              : 
     147            1 : void CompletionFinder::addlistcomplete(const char *command, char *list)
     148              : {
     149            1 :     addcomplete(command, Files_List, list, nullptr);
     150            1 : }
     151              : 
     152            0 : void CompletionFinder::complete(char *s, size_t maxlen, const char *cmdprefix)
     153              : {
     154            0 :     size_t cmdlen = 0;
     155            0 :     if(cmdprefix)
     156              :     {
     157            0 :         cmdlen = std::strlen(cmdprefix);
     158            0 :         if(std::strncmp(s, cmdprefix, cmdlen))
     159              :         {
     160            0 :             prependstring(s, cmdprefix, maxlen);
     161              :         }
     162              :     }
     163            0 :     if(!s[cmdlen])
     164              :     {
     165            0 :         return;
     166              :     }
     167            0 :     if(!completesize)
     168              :     {
     169            0 :         completesize = static_cast<int>(std::strlen(&s[cmdlen]));
     170            0 :         delete[] lastcomplete;
     171            0 :         lastcomplete = nullptr;
     172              :     }
     173            0 :     FilesVal *f = nullptr;
     174            0 :     if(completesize)
     175              :     {
     176            0 :         char *end = std::strchr(&s[cmdlen], ' ');
     177            0 :         if(end)
     178              :         {
     179            0 :             f = completions[stringslice(&s[cmdlen], end).str];
     180              :         }
     181              :     }
     182            0 :     const char *nextcomplete = nullptr;
     183            0 :     if(f) // complete using filenames
     184              :     {
     185            0 :         int commandsize = std::strchr(&s[cmdlen], ' ')+1-s;
     186            0 :         f->update();
     187            0 :         for(const char * i : f->files)
     188              :         {
     189            0 :             if(std::strncmp(i, &s[commandsize], completesize+cmdlen-commandsize)==0 &&
     190            0 :                       (!lastcomplete || std::strcmp(i, lastcomplete) > 0) &&
     191            0 :                       (!nextcomplete || std::strcmp(i, nextcomplete) < 0))
     192              :             {
     193            0 :                 nextcomplete = i;
     194              :             }
     195              :         }
     196            0 :         cmdprefix = s;
     197            0 :         cmdlen = commandsize;
     198              :     }
     199              :     else // complete using command or var (ident) names
     200              :     {
     201            0 :         for(auto& [k, id] : idents)
     202              :         {
     203            0 :             if(std::strncmp(id.name, &s[cmdlen], completesize)==0 &&
     204            0 :                       (!lastcomplete || std::strcmp(id.name, lastcomplete) > 0) &&
     205            0 :                       (!nextcomplete || std::strcmp(id.name, nextcomplete) < 0))
     206              :             {
     207            0 :                 nextcomplete = id.name;
     208              :             }
     209              :         }
     210              :     }
     211              : 
     212            0 :     delete[] lastcomplete;
     213            0 :     lastcomplete = nullptr;
     214            0 :     if(nextcomplete)
     215              :     {
     216            0 :         cmdlen = std::min(cmdlen, maxlen-1);
     217            0 :         if(cmdlen)
     218              :         {
     219            0 :             std::memmove(s, cmdprefix, cmdlen);
     220              :         }
     221            0 :         copystring(&s[cmdlen], nextcomplete, maxlen-cmdlen);
     222            0 :         lastcomplete = newstring(nextcomplete);
     223              :     }
     224              : }
     225              : 
     226              : //print to a stream f the listcompletions in the completions filesval
     227            0 : void CompletionFinder::writecompletions(std::fstream& f) const
     228              : {
     229            0 :     std::vector<std::string> cmds;
     230            0 :     for(auto &[k, v] : completions)
     231              :     {
     232            0 :         if(v)
     233              :         {
     234            0 :             cmds.push_back(k);
     235              :         }
     236              :     }
     237            0 :     std::sort(cmds.begin(), cmds.end());
     238            0 :     for(std::string &k : cmds)
     239              :     {
     240            0 :         auto itr = completions.find(k.c_str());
     241            0 :         if(itr == completions.end())
     242              :         {
     243            0 :             conoutf("could not write completion");
     244            0 :             return;
     245              :         }
     246            0 :         const FilesVal *v = (*itr).second;
     247            0 :         if(v->type==Files_List)
     248              :         {
     249            0 :             if(validateblock(v->dir.c_str()))
     250              :             {
     251            0 :                 f << "listcomplete " << escapeid(k.c_str()) << " [" << v->dir << "]\n";
     252              :             }
     253              :             else
     254              :             {
     255            0 :                 f << "listcomplete " << escapeid(k.c_str()) << " " << escapestring(v->dir.c_str()) << std::endl;
     256              :             }
     257              :         }
     258              :         else
     259              :         {
     260            0 :             f << "complete " << escapeid(k.c_str()) << " " << escapestring(v->dir.c_str()) << " " << escapestring(v->ext.size() ? v->ext.c_str() : "*") << std::endl;
     261              :         }
     262              :     }
     263            0 : }
     264              : 
     265            2 : void CompletionFinder::addcomplete(const char *command, int type, char *dir, char *ext)
     266              : {
     267            2 :     if(identflags&Idf_Overridden)
     268              :     {
     269            0 :         conoutf(Console_Error, "cannot override complete %s", command);
     270            2 :         return;
     271              :     }
     272            2 :     if(!dir[0])
     273              :     {
     274            2 :         auto hasfilesitr = completions.find(command);
     275            2 :         if(hasfilesitr != completions.end())
     276              :         {
     277            0 :             (*hasfilesitr).second = nullptr;
     278              :         }
     279            2 :         return;
     280              :     }
     281            0 :     if(type==Files_Directory)
     282              :     {
     283            0 :         int dirlen = static_cast<int>(std::strlen(dir));
     284            0 :         while(dirlen > 0 && (dir[dirlen-1] == '/' || dir[dirlen-1] == '\\'))
     285              :         {
     286            0 :             dir[--dirlen] = '\0';
     287              :         }
     288            0 :         if(ext)
     289              :         {
     290            0 :             if(std::strchr(ext, '*'))
     291              :             {
     292            0 :                 ext[0] = '\0';
     293              :             }
     294            0 :             if(!ext[0])
     295              :             {
     296            0 :                 ext = nullptr;
     297              :             }
     298              :         }
     299              :     }
     300            0 :     FilesKey key(type, dir ? dir : "", dir ? dir : "");
     301            0 :     auto itr = completefiles.find(key);
     302            0 :     if(itr == completefiles.end())
     303              :     {
     304            0 :         FilesVal *f = new FilesVal(type, dir ? dir : "", ext ? ext : "");
     305            0 :         if(type==Files_List)
     306              :         {
     307            0 :             explodelist(dir, f->files);
     308              :         }
     309            0 :         FilesKey newfile = FilesKey(type, f->dir, f->ext);
     310            0 :         itr = completefiles.insert(std::pair<FilesKey, FilesVal *>(newfile, f)).first;
     311            0 :     }
     312            0 :     auto hasfilesitr = completions.find(std::string(command).c_str());
     313            0 :     if(hasfilesitr != completions.end())
     314              :     {
     315            0 :         (*hasfilesitr).second = (*itr).second;
     316              :     }
     317              :     else
     318              :     {
     319            0 :         FilesVal *v = (*itr).second;
     320            0 :         completions[newstring(command)] = v;
     321              :     }
     322            0 : }
     323              : 
     324            0 : char *CompletionFinder::prependstring(char *d, const char *s, size_t len) const
     325              : {
     326            0 :     size_t slen = std::min(std::strlen(s), len);
     327            0 :     std::memmove(&d[slen], d, std::min(len - slen, std::strlen(d) + 1));
     328            0 :     std::memcpy(d, s, slen);
     329            0 :     d[len-1] = 0;
     330            0 :     return d;
     331              : }
     332              : 
     333              : //internally relevant functionality
     334              : namespace
     335              : {
     336              :     constexpr int maxconsolelines = 1000;  //maximum length of conlines reverse queue
     337              : 
     338              :     struct cline
     339              :     {
     340              :         char *line;   //text contents of the line
     341              :         int type,     //one of the enum values Console_* in headers/consts.h
     342              :             outtime;  //timestamp when the console line was created
     343              :     };
     344              :     std::deque<cline> conlines; //global storage of console lines
     345              : 
     346              :     string commandbuf;
     347              :     char *commandaction = nullptr,
     348              :          *commandprompt = nullptr;
     349              :     enum CommandFlags
     350              :     {
     351              :         CmdFlags_Complete = 1<<0,
     352              :         CmdFlags_Execute  = 1<<1,
     353              :     };
     354              : 
     355              :     int commandflags = 0,
     356              :         commandpos = -1;
     357              : 
     358            0 :     VARFP(maxcon, 10, 200, maxconsolelines,
     359              :     {
     360              :         while(static_cast<int>(conlines.size()) > maxcon)
     361              :         {
     362              :             delete[] conlines.front().line;
     363              :             conlines.pop_back();
     364              :         }
     365              :     });
     366              : 
     367              :     constexpr int constrlen = 512;
     368              : 
     369              :     // tab-completion of all idents and base maps
     370              : 
     371              :     CompletionFinder cfinder;
     372              : 
     373          698 :     void conline(int type, const char *sf)        // add a line to the console buffer
     374              :     {
     375          698 :         char *buf = static_cast<int>(conlines.size()) >= maxcon ? conlines.back().line : newstring("", constrlen-1);
     376          698 :         if(static_cast<int>(conlines.size()) >= maxcon)
     377              :         {
     378          401 :             conlines.pop_back();
     379              :         }
     380              :         cline cl;
     381          698 :         cl.line = buf;
     382          698 :         cl.type = type;
     383          698 :         cl.outtime = totalmillis;                // for how long to keep line on screen
     384          698 :         copystring(cl.line, sf, constrlen);
     385          698 :         conlines.push_front(cl);
     386          698 :     }
     387              : 
     388            1 :     void fullconsole(int *val, int *numargs, ident *id)
     389              :     {
     390            1 :         if(*numargs > 0)
     391              :         {
     392            0 :             UI::holdui("fullconsole", *val!=0);
     393              :         }
     394              :         else
     395              :         {
     396            1 :             int vis = UI::uivisible("fullconsole") ? 1 : 0;
     397            1 :             if(*numargs < 0)
     398              :             {
     399            0 :                 intret(vis);
     400              :             }
     401              :             else
     402              :             {
     403            1 :                 printvar(id, vis);
     404              :             }
     405              :         }
     406            1 :     }
     407              : 
     408            1 :     void toggleconsole()
     409              :     {
     410            1 :         UI::toggleui("fullconsole");
     411            1 :     }
     412              : 
     413              :     VARP(miniconsize, 0, 5, 100);               //miniature console font size
     414              :     VARP(miniconwidth, 0, 40, 100);             //miniature console width
     415              :     VARP(confade, 0, 30, 60);                   //seconds before fading console
     416              :     VARP(miniconfade, 0, 30, 60);
     417              :     HVARP(confilter, 0, 0xFFFFFF, 0xFFFFFF);
     418              :     HVARP(fullconfilter, 0, 0xFFFFFF, 0xFFFFFF);
     419              :     HVARP(miniconfilter, 0, 0, 0xFFFFFF);
     420              : 
     421              :     int conskip     = 0,
     422              :         miniconskip = 0;
     423              : 
     424            2 :     void setconskip(int &skip, int filter, int n)
     425              :     {
     426            2 :         int offsetnum = std::abs(n),
     427            2 :             dir = n < 0 ? -1 : 1;
     428            2 :         skip = std::clamp(skip, 0, static_cast<int>(conlines.size()-1));
     429            2 :         while(offsetnum)
     430              :         {
     431            0 :             skip += dir;
     432            0 :             if(!(static_cast<int>(conlines.size()) > skip))
     433              :             {
     434            0 :                 skip = std::clamp(skip, 0, static_cast<int>(conlines.size()-1));
     435            0 :                 return;
     436              :             }
     437            0 :             if(skip < 0)
     438              :             {
     439            0 :                 skip = 0;
     440            0 :                 break;
     441              :             }
     442            0 :             if(conlines[skip].type&filter)
     443              :             {
     444            0 :                 --offsetnum;
     445              :             }
     446              :         }
     447              :     }
     448              : 
     449            1 :     void clearconsole()
     450              :     {
     451          201 :         while(conlines.size())
     452              :         {
     453          200 :             delete[] conlines.back().line;
     454          200 :             conlines.pop_back();
     455              :         }
     456            1 :     }
     457              : 
     458            0 :     float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int filter, float y = 0, int dir = 1)
     459              :     {
     460            0 :         int numl = conlines.size(),
     461            0 :             offsetlines = std::min(conskip, numl);
     462            0 :         if(confade)
     463              :         {
     464            0 :             if(!conskip)
     465              :             {
     466            0 :                 numl = 0;
     467            0 :                 for(int i = conlines.size(); --i >=0;) //note reverse iteration
     468              :                 {
     469            0 :                     if(totalmillis-conlines[i].outtime < confade*1000)
     470              :                     {
     471            0 :                         numl = i+1;
     472            0 :                         break;
     473              :                     }
     474              :                 }
     475              :             }
     476              :             else
     477              :             {
     478            0 :                 offsetlines--;
     479              :             }
     480              :         }
     481              : 
     482            0 :         int totalheight = 0;
     483            0 :         for(int i = 0; i < numl; ++i) //determine visible height
     484              :         {
     485              :             // shuffle backwards to fill if necessary
     486            0 :             int idx = offsetlines+i < numl ? offsetlines+i : --offsetlines;
     487            0 :             if(!(conlines[idx].type&filter))
     488              :             {
     489            0 :                 continue;
     490              :             }
     491            0 :             char *line = conlines[idx].line;
     492              :             float width, height;
     493            0 :             text_boundsf(line, width, height, conwidth);
     494            0 :             if(totalheight + height > conheight)
     495              :             {
     496            0 :                 numl = i;
     497            0 :                 if(offsetlines == idx)
     498              :                 {
     499            0 :                     ++offsetlines;
     500              :                 }
     501            0 :                 break;
     502              :             }
     503            0 :             totalheight += height;
     504              :         }
     505            0 :         if(dir > 0)
     506              :         {
     507            0 :             y = conoff;
     508              :         }
     509            0 :         for(int i = 0; i < numl; ++i)
     510              :         {
     511            0 :             int idx = offsetlines + (dir > 0 ? numl-i-1 : i);
     512            0 :             if(!(conlines[idx].type&filter))
     513              :             {
     514            0 :                 continue;
     515              :             }
     516            0 :             char *line = conlines[idx].line;
     517              :             float width, height;
     518            0 :             text_boundsf(line, width, height, conwidth);
     519            0 :             if(dir <= 0)
     520              :             {
     521            0 :                 y -= height;
     522              :             }
     523              :             //draw_text(line, conoff, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth);
     524            0 :             ttr.fontsize(50);
     525            0 :             ttr.renderttf(line, {0xFF, 0xFF, 0xFF, 0}, conoff, y);
     526            0 :             if(dir > 0)
     527              :             {
     528            0 :                 y += height;
     529              :             }
     530              :         }
     531            0 :         return y+conoff;
     532              :     }
     533              : 
     534              :     // keymap is defined externally in keymap.cfg
     535              : 
     536              :     /*
     537              :      * defines a mapping for a single key
     538              :      * multiple keymap objects are aggregated in keyms to create the entire bindings list
     539              :      */
     540              :     struct KeyMap
     541              :     {
     542              :         enum
     543              :         {
     544              :             Action_Default = 0,
     545              :             Action_Spectator,
     546              :             Action_Editing,
     547              :             Action_NumActions
     548              :         };
     549              : 
     550              :         int code;                           //unique bind code assigned to the key
     551              :         char *name;                         //name to use to access this key
     552              :         char *actions[Action_NumActions];   //array of strings to execute depending on what mode is being used
     553              :         bool pressed;                       //whether this key is currently depressed
     554              : 
     555            1 :         KeyMap() : code(-1), name(nullptr), pressed(false)
     556              :         {
     557            4 :             for(int i = 0; i < Action_NumActions; ++i)
     558              :             {
     559            3 :                 actions[i] = newstring("");
     560              :             }
     561            1 :         }
     562            1 :         ~KeyMap()
     563              :         {
     564            1 :             delete[] name;
     565            1 :             name = nullptr;
     566            4 :             for(int i = 0; i < Action_NumActions; ++i)
     567              :             {
     568            3 :                 delete[] actions[i];
     569            3 :                 actions[i] = nullptr;
     570              :             }
     571            1 :         }
     572              : 
     573              :         void clear(int type);
     574            0 :         void clear()
     575              :         {
     576            0 :             for(int i = 0; i < Action_NumActions; ++i)
     577              :             {
     578            0 :                 clear(i);
     579              :             }
     580            0 :         }
     581              :     };
     582              : 
     583              : 
     584              :     KeyMap *keypressed = nullptr;
     585              :     char *keyaction = nullptr;
     586              : 
     587            0 :     void KeyMap::clear(int type)
     588              :     {
     589            0 :         char *&binding = actions[type];
     590            0 :         if(binding[0])
     591              :         {
     592            0 :             if(!keypressed || keyaction!=binding)
     593              :             {
     594            0 :                 delete[] binding;
     595              :             }
     596            0 :             binding = newstring("");
     597              :         }
     598            0 :     }
     599              : 
     600              :     std::map<int, KeyMap> keyms;
     601              : 
     602            1 :     void keymap(int *code, char *key)
     603              :     {
     604            1 :         if(identflags&Idf_Overridden)
     605              :         {
     606            0 :             conoutf(Console_Error, "cannot override keymap %d", *code);
     607            0 :             return;
     608              :         }
     609            1 :         KeyMap &km = keyms[*code];
     610            1 :         km.code = *code;
     611            1 :         delete[] km.name;
     612            1 :         km.name = newstring(key);
     613              :     }
     614              : 
     615            3 :     void searchbinds(char *action, int type)
     616              :     {
     617            3 :         std::vector<char> names;
     618            3 :         for(auto &[k, km] : keyms)
     619              :         {
     620            0 :             if(!std::strcmp(km.actions[type], action))
     621              :             {
     622            0 :                 if(names.size())
     623              :                 {
     624            0 :                     names.push_back(' ');
     625              :                 }
     626            0 :                 for(uint i = 0; i < std::strlen(km.name); ++i)
     627              :                 {
     628            0 :                     names.push_back(km.name[i]);
     629              :                 }
     630              :             }
     631              :         }
     632            3 :         names.push_back('\0');
     633            3 :         result(names.data());
     634            3 :     }
     635              : 
     636            6 :     KeyMap *findbind(const char *key)
     637              :     {
     638            6 :         for(auto &[k, km] : keyms)
     639              :         {
     640            0 :             if(!strcasecmp(km.name, key))
     641              :             {
     642            0 :                 return &km;
     643              :             }
     644              :         }
     645            6 :         return nullptr;
     646              :     }
     647              : 
     648            3 :     void getbind(const char *key, int type)
     649              :     {
     650            3 :         KeyMap *km = findbind(key);
     651            3 :         result(km ? km->actions[type] : "");
     652            3 :     }
     653              : 
     654            3 :     void bindkey(const char *key, const char *action, int state, const char *cmd)
     655              :     {
     656            3 :         if(identflags&Idf_Overridden)
     657              :         {
     658            0 :             conoutf(Console_Error, "cannot override %s \"%s\"", cmd, key);
     659            0 :             return;
     660              :         }
     661            3 :         KeyMap *km = findbind(key);
     662            3 :         if(!km)
     663              :         {
     664            3 :             conoutf(Console_Error, "unknown key \"%s\"", key);
     665            3 :             return;
     666              :         }
     667            0 :         char *&binding = km->actions[state];
     668            0 :         if(!keypressed || keyaction!=binding)
     669              :         {
     670            0 :             delete[] binding;
     671              :         }
     672              :         // trim white-space to make searchbinds more reliable
     673            0 :         while(iscubespace(*action))
     674              :         {
     675            0 :             action++;
     676              :         }
     677            0 :         int len = std::strlen(action);
     678            0 :         while(len>0 && iscubespace(action[len-1]))
     679              :         {
     680            0 :             len--;
     681              :         }
     682            0 :         binding = newstring(action, len);
     683              :     }
     684              : 
     685            2 :     void inputcommand(char *init, char *action = nullptr, char *prompt = nullptr, char *flags = nullptr) // turns input to the command line on or off
     686              :     {
     687            2 :         commandmillis = init ? totalmillis : -1;
     688            2 :         textinput(commandmillis >= 0, TextInput_Console);
     689            2 :         keyrepeat(commandmillis >= 0, KeyRepeat_Console);
     690            2 :         copystring(commandbuf, init ? init : "");
     691              : 
     692            2 :         delete[] commandaction;
     693            2 :         delete[] commandprompt;
     694            2 :         commandaction = nullptr;
     695            2 :         commandprompt = nullptr;
     696              : 
     697            2 :         commandpos = -1;
     698            2 :         if(action && action[0])
     699              :         {
     700            0 :             commandaction = newstring(action);
     701              :         }
     702            2 :         if(prompt && prompt[0])
     703              :         {
     704            0 :             commandprompt = newstring(prompt);
     705              :         }
     706            2 :         commandflags = 0;
     707            2 :         if(flags)
     708              :         {
     709            1 :             while(*flags)
     710              :             {
     711            0 :                 switch(*flags++)
     712              :                 {
     713            0 :                     case 'c':
     714              :                     {
     715            0 :                         commandflags |= CmdFlags_Complete;
     716            0 :                         break;
     717              :                     }
     718            0 :                     case 'x':
     719              :                     {
     720            0 :                         commandflags |= CmdFlags_Execute;
     721            0 :                         break;
     722              :                     }
     723            0 :                     case 's':
     724              :                     {
     725            0 :                         commandflags |= CmdFlags_Complete|CmdFlags_Execute;
     726            0 :                         break;
     727              :                     }
     728              :                 }
     729              :             }
     730              :         }
     731            1 :         else if(init)
     732              :         {
     733            1 :             commandflags |= CmdFlags_Complete|CmdFlags_Execute;
     734              :         }
     735            2 :     }
     736              : 
     737            1 :     void saycommand(char *init)
     738              :     {
     739            1 :         inputcommand(init);
     740            1 :     }
     741              : 
     742            0 :     void pasteconsole()
     743              :     {
     744            0 :         if(!SDL_HasClipboardText())
     745              :         {
     746            0 :             return;
     747              :         }
     748            0 :         char *cb = SDL_GetClipboardText();
     749            0 :         if(!cb)
     750              :         {
     751            0 :             return;
     752              :         }
     753            0 :         size_t cblen = std::strlen(cb),
     754            0 :                commandlen = std::strlen(commandbuf);
     755            0 :         if(strlen(commandbuf) + cblen < 260)
     756              :         {
     757            0 :             std::memcpy(reinterpret_cast<uchar *>(&commandbuf[commandlen]), cb, cblen);
     758              :         }
     759            0 :         commandbuf[commandlen + cblen] = '\0';
     760            0 :         SDL_free(cb);
     761              :     }
     762              : 
     763              :     struct HLine
     764              :     {
     765              :         char *buf, *action, *prompt;
     766              :         int flags;
     767              : 
     768            0 :         HLine() : buf(nullptr), action(nullptr), prompt(nullptr), flags(0) {}
     769            0 :         ~HLine()
     770              :         {
     771            0 :             delete[] buf;
     772            0 :             delete[] action;
     773            0 :             delete[] prompt;
     774              : 
     775            0 :             buf = nullptr;
     776            0 :             action = nullptr;
     777            0 :             prompt = nullptr;
     778            0 :         }
     779              : 
     780            0 :         void restore() const
     781              :         {
     782            0 :             copystring(commandbuf, buf);
     783            0 :             if(commandpos >= static_cast<int>(std::strlen(commandbuf)))
     784              :             {
     785            0 :                 commandpos = -1;
     786              :             }
     787              : 
     788            0 :             delete[] commandaction;
     789            0 :             delete[] commandprompt;
     790              : 
     791            0 :             commandaction = nullptr;
     792            0 :             commandprompt = nullptr;
     793              : 
     794            0 :             if(action)
     795              :             {
     796            0 :                 commandaction = newstring(action);
     797              :             }
     798            0 :             if(prompt)
     799              :             {
     800            0 :                 commandprompt = newstring(prompt);
     801              :             }
     802            0 :             commandflags = flags;
     803            0 :         }
     804              : 
     805            0 :         bool shouldsave() const
     806              :         {
     807            0 :             return std::strcmp(commandbuf, buf) ||
     808            0 :                    (commandaction ? !action || std::strcmp(commandaction, action) : action!=nullptr) ||
     809            0 :                    (commandprompt ? !prompt || std::strcmp(commandprompt, prompt) : prompt!=nullptr) ||
     810            0 :                    commandflags != flags;
     811              :         }
     812              : 
     813            0 :         void save()
     814              :         {
     815            0 :             buf = newstring(commandbuf);
     816            0 :             if(commandaction)
     817              :             {
     818            0 :                 action = newstring(commandaction);
     819              :             }
     820            0 :             if(commandprompt)
     821              :             {
     822            0 :                 prompt = newstring(commandprompt);
     823              :             }
     824            0 :             flags = commandflags;
     825            0 :         }
     826              : 
     827            0 :         void run() const
     828              :         {
     829            0 :             if(flags&CmdFlags_Execute && buf[0]=='/')
     830              :             {
     831            0 :                 execute(buf+1);
     832              :             }
     833            0 :             else if(action)
     834              :             {
     835            0 :                 alias("commandbuf", buf);
     836            0 :                 execute(action);
     837              :             }
     838              :             else
     839              :             {
     840            0 :                 conoutf(Console_Info, "%s", buf);
     841              :             }
     842            0 :         }
     843              :     };
     844              :     std::vector<HLine *> history;
     845              :     int histpos = 0;
     846              : 
     847              :     VARP(maxhistory, 0, 1000, 10000);
     848              : 
     849            1 :     void historycmd(int *n)
     850              :     {
     851              :         static bool inhistory = false;
     852            1 :         if(!inhistory && static_cast<int>(history.size()) > *n)
     853              :         {
     854            0 :             inhistory = true;
     855            0 :             history[history.size()-*n-1]->run();
     856            0 :             inhistory = false;
     857              :         }
     858            1 :     }
     859              : 
     860              :     struct releaseaction
     861              :     {
     862              :         KeyMap *key;
     863              :         union
     864              :         {
     865              :             char *action;
     866              :             ident *id;
     867              :         };
     868              :         int numargs;
     869              :         tagval args[3];
     870              :     };
     871              :     std::vector<releaseaction> releaseactions;
     872              : 
     873            1 :     const char *addreleaseaction(char *s)
     874              :     {
     875            1 :         if(!keypressed)
     876              :         {
     877            1 :             delete[] s;
     878            1 :             return nullptr;
     879              :         }
     880            0 :         releaseactions.emplace_back();
     881            0 :         releaseaction &ra = releaseactions.back();
     882            0 :         ra.key = keypressed;
     883            0 :         ra.action = s;
     884            0 :         ra.numargs = -1;
     885            0 :         return keypressed->name;
     886              :     }
     887              : 
     888            1 :     void onrelease(const char *s)
     889              :     {
     890            1 :         addreleaseaction(newstring(s));
     891            1 :     }
     892              : 
     893            0 :     static void execbind(KeyMap &k, bool isdown, int map)
     894              :     {
     895            0 :         for(uint i = 0; i < releaseactions.size(); i++)
     896              :         {
     897            0 :             releaseaction &ra = releaseactions[i];
     898            0 :             if(ra.key==&k)
     899              :             {
     900            0 :                 if(ra.numargs < 0)
     901              :                 {
     902            0 :                     if(!isdown)
     903              :                     {
     904            0 :                         execute(ra.action);
     905              :                     }
     906            0 :                     delete[] ra.action;
     907              :                 }
     908              :                 else
     909              :                 {
     910            0 :                     execute(isdown ? nullptr : ra.id, ra.args, ra.numargs);
     911              :                 }
     912            0 :                 releaseactions.erase(releaseactions.begin() + i);
     913            0 :                 i--;
     914              :             }
     915              :         }
     916            0 :         if(isdown)
     917              :         {
     918            0 :             int state = KeyMap::Action_Default;
     919            0 :             if(!mainmenu)
     920              :             {
     921            0 :                 if(map == 1)
     922              :                 {
     923            0 :                     state = KeyMap::Action_Editing;
     924              :                 }
     925            0 :                 else if(map == 2)
     926              :                 {
     927            0 :                     state = KeyMap::Action_Spectator;
     928              :                 }
     929              :             }
     930            0 :             char *&action = k.actions[state][0] ? k.actions[state] : k.actions[KeyMap::Action_Default];
     931            0 :             keyaction = action;
     932            0 :             keypressed = &k;
     933            0 :             execute(keyaction);
     934            0 :             keypressed = nullptr;
     935            0 :             if(keyaction!=action)
     936              :             {
     937            0 :                 delete[] keyaction;
     938              :             }
     939              :         }
     940            0 :         k.pressed = isdown;
     941            0 :     }
     942              : 
     943            0 :     bool consoleinput(const char *str, int len)
     944              :     {
     945            0 :         if(commandmillis < 0)
     946              :         {
     947            0 :             return false;
     948              :         }
     949            0 :         ::cfinder.resetcomplete();
     950            0 :         int cmdlen = static_cast<int>(std::strlen(commandbuf)),
     951            0 :             cmdspace = static_cast<int>(sizeof(commandbuf)) - (cmdlen+1);
     952            0 :         len = std::min(len, cmdspace);
     953            0 :         if(commandpos<0)
     954              :         {
     955            0 :             std::memcpy(&commandbuf[cmdlen], str, len);
     956              :         }
     957              :         else
     958              :         {
     959            0 :             std::memmove(&commandbuf[commandpos+len], &commandbuf[commandpos], cmdlen - commandpos);
     960            0 :             std::memcpy(&commandbuf[commandpos], str, len);
     961            0 :             commandpos += len;
     962              :         }
     963            0 :         commandbuf[cmdlen + len] = '\0';
     964              : 
     965            0 :         return true;
     966              :     }
     967              : 
     968            0 :     bool consolekey(int code, bool isdown)
     969              :     {
     970            0 :         if(commandmillis < 0)
     971              :         {
     972            0 :             return false;
     973              :         }
     974            0 :         if(isdown)
     975              :         {
     976            0 :             switch(code)
     977              :             {
     978            0 :                 case SDLK_RETURN:
     979              :                 case SDLK_KP_ENTER:
     980              :                 {
     981            0 :                     break;
     982              :                 }
     983            0 :                 case SDLK_HOME:
     984              :                 {
     985            0 :                     if(std::strlen(commandbuf))
     986              :                     {
     987            0 :                         commandpos = 0;
     988              :                     }
     989            0 :                     break;
     990              :                 }
     991            0 :                 case SDLK_END:
     992              :                 {
     993            0 :                     commandpos = -1;
     994            0 :                     break;
     995              :                 }
     996            0 :                 case SDLK_DELETE:
     997              :                 {
     998            0 :                     size_t len = std::strlen(commandbuf);
     999            0 :                     if(commandpos<0)
    1000              :                     {
    1001            0 :                         break;
    1002              :                     }
    1003            0 :                     std::memmove(&commandbuf[commandpos], &commandbuf[commandpos+1], len - commandpos);
    1004            0 :                     ::cfinder.resetcomplete();
    1005            0 :                     if(commandpos >= static_cast<int>(len-1))
    1006              :                     {
    1007            0 :                         commandpos = -1;
    1008              :                     }
    1009            0 :                     break;
    1010              :                 }
    1011            0 :                 case SDLK_BACKSPACE:
    1012              :                 {
    1013            0 :                     size_t len = std::strlen(commandbuf);
    1014            0 :                     int i = commandpos>=0 ? commandpos : len;
    1015            0 :                     if(i<1)
    1016              :                     {
    1017            0 :                         break;
    1018              :                     }
    1019            0 :                     std::memmove(&commandbuf[i-1], &commandbuf[i], len - i + 1);
    1020            0 :                     ::cfinder.resetcomplete();
    1021            0 :                     if(commandpos>0)
    1022              :                     {
    1023            0 :                         commandpos--;
    1024              :                     }
    1025            0 :                     else if(!commandpos && len<=1)
    1026              :                     {
    1027            0 :                         commandpos = -1;
    1028              :                     }
    1029            0 :                     break;
    1030              :                 }
    1031            0 :                 case SDLK_LEFT:
    1032              :                 {
    1033            0 :                     if(commandpos>0)
    1034              :                     {
    1035            0 :                         commandpos--;
    1036              :                     }
    1037            0 :                     else if(commandpos<0)
    1038              :                     {
    1039            0 :                         commandpos = static_cast<int>(std::strlen(commandbuf))-1;
    1040              :                     }
    1041            0 :                     break;
    1042              :                 }
    1043            0 :                 case SDLK_RIGHT:
    1044              :                 {
    1045            0 :                     if(commandpos>=0 && ++commandpos >= static_cast<int>(std::strlen(commandbuf)))
    1046              :                     {
    1047            0 :                         commandpos = -1;
    1048              :                     }
    1049            0 :                     break;
    1050              :                 }
    1051            0 :                 case SDLK_UP:
    1052              :                 {
    1053            0 :                     if(histpos > static_cast<int>(history.size()))
    1054              :                     {
    1055            0 :                         histpos = history.size();
    1056              :                     }
    1057            0 :                     if(histpos > 0)
    1058              :                     {
    1059            0 :                         history[--histpos]->restore();
    1060              :                     }
    1061            0 :                     break;
    1062              :                 }
    1063            0 :                 case SDLK_DOWN:
    1064              :                 {
    1065            0 :                     if(histpos + 1 < static_cast<int>(history.size()))
    1066              :                     {
    1067            0 :                         history[++histpos]->restore();
    1068              :                     }
    1069            0 :                     break;
    1070              :                 }
    1071            0 :                 case SDLK_TAB:
    1072              :                 {
    1073            0 :                     if(commandflags&CmdFlags_Complete)
    1074              :                     {
    1075            0 :                         ::cfinder.complete(commandbuf, sizeof(commandbuf), commandflags&CmdFlags_Execute ? "/" : nullptr);
    1076            0 :                         if(commandpos>=0 && commandpos >= static_cast<int>(std::strlen(commandbuf)))
    1077              :                         {
    1078            0 :                             commandpos = -1;
    1079              :                         }
    1080              :                     }
    1081            0 :                     break;
    1082              :                 }
    1083            0 :                 case SDLK_v:
    1084              :                 {
    1085            0 :                     if(SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL))//mod keys
    1086              :                     {
    1087            0 :                         pasteconsole();
    1088              :                     }
    1089            0 :                     break;
    1090              :                 }
    1091              :             }
    1092              :         }
    1093              :         else
    1094              :         {
    1095            0 :             if(code==SDLK_RETURN || code==SDLK_KP_ENTER)
    1096              :             {
    1097            0 :                 HLine *h = nullptr;
    1098            0 :                 if(commandbuf[0])
    1099              :                 {
    1100            0 :                     if(history.empty() || history.back()->shouldsave())
    1101              :                     {
    1102            0 :                         if(maxhistory && static_cast<int>(history.size()) >= maxhistory)
    1103              :                         {
    1104            0 :                             for(uint i = 0; i < (history.size()-maxhistory+1); ++i)
    1105              :                             {
    1106            0 :                                 delete history[i];
    1107              :                             }
    1108            0 :                             history.erase(history.begin(), history.begin() + history.size()-maxhistory+1);
    1109              :                         }
    1110            0 :                         history.emplace_back(h = new HLine)->save();
    1111              :                     }
    1112              :                     else
    1113              :                     {
    1114            0 :                         h = history.back();
    1115              :                     }
    1116              :                 }
    1117            0 :                 histpos = history.size();
    1118            0 :                 inputcommand(nullptr);
    1119            0 :                 if(h)
    1120              :                 {
    1121            0 :                     h->run();
    1122              :                 }
    1123            0 :             }
    1124            0 :             else if(code==SDLK_ESCAPE)
    1125              :             {
    1126            0 :                 histpos = history.size();
    1127            0 :                 inputcommand(nullptr);
    1128              :             }
    1129              :         }
    1130              : 
    1131            0 :         return true;
    1132              :     }
    1133              : }
    1134              : 
    1135              : //iengine.h
    1136            0 : void clear_console()
    1137              : {
    1138            0 :     keyms.clear();
    1139            0 : }
    1140              : 
    1141              : //console.h
    1142            0 : void processtextinput(const char *str, int len)
    1143              : {
    1144            0 :     if(!UI::textinput(str, len))
    1145              :     {
    1146            0 :         consoleinput(str, len);
    1147              :     }
    1148            0 : }
    1149              : 
    1150            0 : void processkey(int code, bool isdown, int map)
    1151              : {
    1152            0 :     auto itr = keyms.find(code);
    1153            0 :     if(itr != keyms.end() && (*itr).second.pressed)
    1154              :     {
    1155            0 :         execbind((*itr).second, isdown, map); // allow pressed keys to release
    1156              :     }
    1157            0 :     else if(!UI::keypress(code, isdown)) // UI key intercept
    1158              :     {
    1159            0 :         if(!consolekey(code, isdown))
    1160              :         {
    1161            0 :             if(itr != keyms.end())
    1162              :             {
    1163            0 :                 execbind((*itr).second, isdown, map);
    1164              :             }
    1165              :         }
    1166              :     }
    1167            0 : }
    1168              : 
    1169            0 : float rendercommand(float x, float y, float w)
    1170              : {
    1171            0 :     if(commandmillis < 0)
    1172              :     {
    1173            0 :         return 0;
    1174              :     }
    1175              :     char buf[constrlen];
    1176            0 :     const char *prompt = commandprompt ? commandprompt : ">";
    1177            0 :     formatstring(buf, "%s %s", prompt, commandbuf);
    1178              :     float width, height;
    1179            0 :     text_boundsf(buf, width, height, w);
    1180            0 :     y -= height;
    1181            0 :     ttr.fontsize(50);
    1182            0 :     ttr.renderttf(buf, {0xFF, 0xFF, 0xFF, 0}, x, y);
    1183              :     //draw_text(buf, x, y, 0xFF, 0xFF, 0xFF, 0xFF, commandpos>=0 ? commandpos+1 + std::strlen(prompt) : std::strlen(buf), w);
    1184            0 :     return height;
    1185              : }
    1186              : 
    1187            0 : float renderfullconsole(float w, float h)
    1188              : {
    1189            0 :     float conpad = FONTH/2,
    1190            0 :           conheight = h - 2*conpad,
    1191            0 :           conwidth = w - 2*conpad;
    1192            0 :     drawconlines(conskip, 0, conwidth, conheight, conpad, fullconfilter);
    1193            0 :     return conheight + 2*conpad;
    1194              : }
    1195              : 
    1196            0 : float renderconsole(float w, float h, float abovehud)
    1197              : {
    1198            0 :     static VARP(consize, 0, 5, 100);                   //font size of the console text
    1199            0 :     float conpad = FONTH/2,
    1200            0 :           conheight = std::min(static_cast<float>(FONTH*consize), h - 2*conpad),
    1201            0 :           conwidth = w - 2*conpad,
    1202            0 :           y = drawconlines(conskip, confade, conwidth, conheight, conpad, confilter);
    1203            0 :     if(miniconsize && miniconwidth)
    1204              :     {
    1205            0 :         drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, std::min(static_cast<float>(FONTH*miniconsize), abovehud - y), conpad, miniconfilter, abovehud, -1);
    1206              :     }
    1207            0 :     return y;
    1208              : }
    1209              : 
    1210          698 : void conoutfv(int type, const char *fmt, va_list args)
    1211              : {
    1212              :     static char buf[constrlen];
    1213          698 :     vformatstring(buf, fmt, args, sizeof(buf));
    1214          698 :     conline(type, buf);
    1215          698 :     logoutf("%s", buf);
    1216          698 : }
    1217              : 
    1218          575 : void conoutf(const char *fmt, ...)
    1219              : {
    1220              :     va_list args;
    1221          575 :     va_start(args, fmt);
    1222          575 :     conoutfv(Console_Info, fmt, args);
    1223          575 :     va_end(args);
    1224          575 : }
    1225              : 
    1226          117 : void conoutf(int type, const char *fmt, ...)
    1227              : {
    1228              :     va_list args;
    1229          117 :     va_start(args, fmt);
    1230          117 :     conoutfv(type, fmt, args);
    1231          117 :     va_end(args);
    1232          117 : }
    1233              : 
    1234            0 : const char *getkeyname(int code)
    1235              : {
    1236            0 :     auto itr = keyms.find(code);
    1237            0 :     return itr != keyms.end() ? (*itr).second.name : nullptr;
    1238              : }
    1239              : 
    1240            1 : tagval *addreleaseaction(ident *id, int numargs)
    1241              : {
    1242            1 :     if(!keypressed || numargs > 3)
    1243              :     {
    1244            1 :         return nullptr;
    1245              :     }
    1246            0 :     releaseactions.emplace_back();
    1247            0 :     releaseaction &ra = releaseactions.back();
    1248            0 :     ra.key = keypressed;
    1249            0 :     ra.id = id;
    1250            0 :     ra.numargs = numargs;
    1251            0 :     return ra.args;
    1252              : }
    1253              : 
    1254              : //print to a stream f the binds in the binds vector
    1255            0 : void writebinds(std::fstream& f)
    1256              : {
    1257              :     static const char * const cmds[3] = { "bind", "specbind", "editbind" };
    1258            0 :     std::vector<KeyMap *> binds;
    1259            0 :     for(auto &[k, km] : keyms)
    1260              :     {
    1261            0 :         binds.push_back(&km);
    1262              :     }
    1263            0 :     std::sort(binds.begin(), binds.end());
    1264            0 :     for(int j = 0; j < 3; ++j)
    1265              :     {
    1266            0 :         for(KeyMap *&km : binds)
    1267              :         {
    1268            0 :             if(*(km->actions[j]))
    1269              :             {
    1270            0 :                 if(validateblock(km->actions[j]))
    1271              :                 {
    1272            0 :                     f << cmds[j] << " " << escapestring(km->name) << " [" << km->actions[j] << "]\n";
    1273              :                 }
    1274              :                 else
    1275              :                 {
    1276            0 :                     f << cmds[j] << " " << escapestring(km->name) << " " << escapestring(km->actions[j]) << std::endl;
    1277              :                 }
    1278              :             }
    1279              :         }
    1280              :     }
    1281            0 : }
    1282              : 
    1283            0 : extern void writecompletions(std::fstream& f)
    1284              : {
    1285            0 :     ::cfinder.writecompletions(f);
    1286            0 : }
    1287              : 
    1288            1 : void initconsolecmds()
    1289              : {
    1290            1 :     addcommand("fullconsole", reinterpret_cast<identfun>(fullconsole), "iN$", Id_Command);
    1291            1 :     addcommand("toggleconsole", reinterpret_cast<identfun>(toggleconsole), "", Id_Command);
    1292              : 
    1293            1 :     static auto conskipcmd = [] (int *n)
    1294              :     {
    1295            1 :         setconskip(conskip, UI::uivisible("fullconsole") ? fullconfilter : confilter, *n);
    1296            1 :     };
    1297            1 :     addcommand("conskip", reinterpret_cast<identfun>(+conskipcmd), "i", Id_Command);
    1298              : 
    1299              : 
    1300            1 :     static auto miniconskipcmd = [] (int *n)
    1301              :     {
    1302            1 :         setconskip(miniconskip, miniconfilter, *n);
    1303            1 :     };
    1304            1 :     addcommand("miniconskip", reinterpret_cast<identfun>(+miniconskipcmd), "i", Id_Command);
    1305              : 
    1306            1 :     addcommand("clearconsole", reinterpret_cast<identfun>(clearconsole), "", Id_Command);
    1307            1 :     addcommand("keymap", reinterpret_cast<identfun>(keymap), "is", Id_Command);
    1308              : 
    1309            1 :     static auto bind = [] (char *key, char *action)
    1310              :     {
    1311            1 :         bindkey(key, action, KeyMap::Action_Default, "bind");
    1312            1 :     };
    1313            1 :     addcommand("bind", reinterpret_cast<identfun>(+bind), "ss", Id_Command);
    1314              : 
    1315            1 :     static auto specbind = [] (char *key, char *action)
    1316              :     {
    1317            1 :         bindkey(key, action, KeyMap::Action_Spectator, "specbind");
    1318            1 :     };
    1319            1 :     addcommand("specbind", reinterpret_cast<identfun>(+specbind), "ss", Id_Command);
    1320              : 
    1321            1 :     static auto editbind = [] (char *key, char *action)
    1322              :     {
    1323            1 :         bindkey(key, action, KeyMap::Action_Editing, "editbind");
    1324            1 :     };
    1325            1 :     addcommand("editbind", reinterpret_cast<identfun>(+editbind), "ss", Id_Command);
    1326              : 
    1327            1 :     static auto getbindcmd = [] (char *key)
    1328              :     {
    1329            1 :         getbind(key, KeyMap::Action_Default);
    1330            1 :     };
    1331            1 :     addcommand("getbind", reinterpret_cast<identfun>(+getbindcmd), "s", Id_Command);
    1332              : 
    1333            1 :     static auto getspecbind = [] (char *key)
    1334              :     {
    1335            1 :         getbind(key, KeyMap::Action_Spectator);
    1336            1 :     };
    1337            1 :     addcommand("getspecbind", reinterpret_cast<identfun>(+getspecbind), "s", Id_Command);
    1338              : 
    1339            1 :     static auto geteditbind = [] (char *key)
    1340              :     {
    1341            1 :         getbind(key, KeyMap::Action_Editing);
    1342            1 :     };
    1343            1 :     addcommand("geteditbind", reinterpret_cast<identfun>(+geteditbind), "s", Id_Command);
    1344              : 
    1345            1 :     static auto searchbindscmd = [] (char *action)
    1346              :     {
    1347            1 :         searchbinds(action, KeyMap::Action_Default);
    1348            1 :     };
    1349            1 :     addcommand("searchbinds", reinterpret_cast<identfun>(+searchbindscmd), "s", Id_Command);
    1350              : 
    1351            1 :     static auto searchspecbinds = [] (char *action)
    1352              :     {
    1353            1 :         searchbinds(action, KeyMap::Action_Spectator);
    1354            1 :     };
    1355            1 :     addcommand("searchspecbinds", reinterpret_cast<identfun>(+searchspecbinds), "s", Id_Command);
    1356              : 
    1357            1 :     static auto searcheditbinds = [] (char *action)
    1358              :     {
    1359            1 :         searchbinds(action, KeyMap::Action_Editing);
    1360            1 :     };
    1361            1 :     addcommand("searcheditbinds", reinterpret_cast<identfun>(+searcheditbinds), "s", Id_Command);
    1362              : 
    1363            1 :     static auto clearbinds = [] ()
    1364              :     {
    1365            1 :         for(auto &[k, km] : keyms)
    1366              :         {
    1367            0 :             km.clear(KeyMap::Action_Default);
    1368              :         }
    1369            1 :     };
    1370            1 :     addcommand("clearbinds", reinterpret_cast<identfun>(+clearbinds), "", Id_Command);
    1371              : 
    1372            1 :     static auto clearspecbinds = [] ()
    1373              :     {
    1374            1 :         for(auto &[k, km] : keyms)
    1375              :         {
    1376            0 :             km.clear(KeyMap::Action_Spectator);
    1377              :         }
    1378            1 :     };
    1379            1 :     addcommand("clearspecbinds", reinterpret_cast<identfun>(+clearspecbinds), "", Id_Command);
    1380              : 
    1381            1 :     static auto cleareditbinds = [] ()
    1382              :     {
    1383            1 :         for(auto &[k, km] : keyms)
    1384              :         {
    1385            0 :             km.clear(KeyMap::Action_Editing);
    1386              :         }
    1387            1 :     };
    1388            1 :     addcommand("cleareditbinds", reinterpret_cast<identfun>(+cleareditbinds), "", Id_Command);
    1389              : 
    1390            1 :     static auto clearallbinds = [] ()
    1391              :     {
    1392            1 :         for(auto &[k, km] : keyms)
    1393              :         {
    1394            0 :             km.clear();
    1395              :         }
    1396            1 :     };
    1397            1 :     addcommand("clearallbinds", reinterpret_cast<identfun>(+clearallbinds), "", Id_Command);
    1398            1 :     addcommand("inputcommand", reinterpret_cast<identfun>(inputcommand), "ssss", Id_Command);
    1399            1 :     addcommand("saycommand", reinterpret_cast<identfun>(saycommand), "C", Id_Command);
    1400            1 :     addcommand("history", reinterpret_cast<identfun>(historycmd), "i", Id_Command);
    1401            1 :     addcommand("onrelease", reinterpret_cast<identfun>(onrelease), "s", Id_Command);
    1402            2 :     addcommand("complete", reinterpret_cast<identfun>(+[] (char *command, char *dir, char *ext) {::cfinder.addfilecomplete(command, dir, ext);}), "sss", Id_Command);
    1403            2 :     addcommand("listcomplete", reinterpret_cast<identfun>(+[] (char *command, char *list) {::cfinder.addlistcomplete(command, list);}), "ss", Id_Command);
    1404            1 : }
        

Generated by: LCOV version 2.0-1