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: 2026-08-20 06:51:03 Functions: 56.0 % 75 42

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

Generated by: LCOV version 2.0-1