LCOV - code coverage report
Current view: top level - src - autoinput.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 179 208 86.1 %
Date: 2022-06-15 20:16:21 Functions: 12 14 85.7 %
Branches: 105 186 56.5 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : #endif
       4                 :            : /**@file autoinput.cc
       5                 :            :  * @brief This module provides the same functionality as INPUTTER (https://bisqwit.iki.fi/source/inputter.html)
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <mutex>
       9                 :            : #include <thread>
      10                 :            : #include <string_view>
      11                 :            : #include <random>
      12                 :            : #include <list>
      13                 :            : 
      14                 :            : #include "autoinput.hh"
      15                 :            : #include "ctype.hh"
      16                 :            : #include "clock.hh"
      17                 :            : #include "ui.hh"
      18                 :            : 
      19                 :            : //#define TURBOHACK
      20                 :            : 
      21                 :            : namespace
      22                 :            : {
      23                 :            :     std::mutex lock;
      24                 :            :     std::list<AutoInputResponse> data;
      25                 :            :     bool       terminate = false;
      26                 :            :     bool       finished  = true;
      27                 :            : }
      28                 :            : 
      29                 :            : static constexpr unsigned char Delay_CharClassTable[] =
      30                 :            : {
      31                 :            : //Delay_CharClassTable:
      32                 :            : //        ; D, H, I, J, M are fast ctrls
      33                 :            : //        ; K is CtrlK
      34                 :            : //        ; A,B,E,F,W,Y, Z are other ctrls
      35                 :            : //
      36                 :            : // ; Ctrl keys:
      37                 :            : //;  @A   BC   DE   FG   HI   JK   LM   NO   PQ   RS   TU   VW   XY   Z[   \]   ^_
      38                 :            : // ; Normal keys
      39                 :            : //;   !   "#   $%   &'   ()   *+   ,-   ./   01   23   45   67   89   :;   <=   >?
      40                 :            : //;  @A   BC   DE   FG   HI   JK   LM   NO   PQ   RS   TU   VW   XY   Z[   \]   ^_
      41                 :            : //;  `a   bc   de   fg   hi   jk   lm   no   pq   rs   tu   vw   xy   z{   |}   ~<del>
      42                 :            :     0x0C,0xC0,0x2C,0xC0,0x22,0x14,0x01,0x00,0x00,0x00,0x00,0x02,0x02,0x22,0x88,0x88,//00h
      43                 :            :     0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x66,0x66,0x66,0x66,0x66,0x88,0x88,0x88,//20h
      44                 :            :     0x86,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x68,0x88,0x88,//40h
      45                 :            :     0x86,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x68,0x88,0xA8 //60h
      46                 :            : };
      47                 :            : 
      48                 :            : #include <iostream>
      49                 :          1 : void AutoInputProvider(std::u32string& s)
      50                 :            : {
      51                 :          1 :     std::mt19937 rnd;
      52                 :      24878 :     auto random = [&rnd](unsigned size){ return std::uniform_int_distribution<>(0, (size)-1)(rnd); };
      53                 :            : 
      54                 :          1 :     unsigned cur_speed = 16;
      55                 :          1 :     int      modifier  = 5;
      56                 :          1 :     int      repeats   = 0;
      57                 :          1 :     char32_t prev_key  = 0;
      58                 :          1 :     bool     had_ctrlk = false;
      59                 :            : 
      60                 :         16 :     auto ResetSeed = [&]()
      61                 :            :     {
      62                 :         15 :         rnd.seed(rnd.default_seed);
      63                 :         15 :         modifier  = 5;
      64                 :         15 :         repeats   = 0;
      65                 :         15 :         prev_key  = 0;
      66                 :         15 :         had_ctrlk = false;
      67                 :         16 :     };
      68                 :            : 
      69                 :          1 :     ResetSeed();
      70                 :            : 
      71                 :            : #ifdef TURBOHACK
      72                 :            :     bool TimeHackActive = true;
      73                 :            : #endif
      74                 :            : 
      75                 :          3 :     auto Do_Delay_Resize = [&](std::pair<unsigned,unsigned> found_delay,
      76                 :            :                                unsigned fx,unsigned fy,
      77                 :            :                                unsigned sx,unsigned sy,
      78                 :            :                                bool resize_first)
      79                 :            :     {
      80                 :          2 :         unsigned time = 0;
      81         [ -  + ]:          2 :         if(found_delay.first != ~0u)
      82                 :            :         {
      83                 :          0 :             time = found_delay.second * 250; // milliseconds
      84                 :          0 :             s[found_delay.first+1] = 0;
      85                 :            :         }
      86                 :            : 
      87         [ +  - ]:          2 :         auto [vcw, vch] = ui.GetCellSize();
      88         [ +  - ]:          2 :         auto [ww, wh] = ui.GetWindowSize();
      89   [ +  -  +  +  :          2 :         if(time == 0 && fx==vcw && fy==vch && sx==ww && sy==wh)
          -  +  -  -  -  
                      - ]
      90                 :            :         {
      91                 :            :             // Nothing to do
      92                 :          0 :             return;
      93                 :            :         }
      94                 :            : 
      95                 :          2 :         unsigned step = 13; // 240fps = 4.166 ms per frame, 120fps = 8.3 ms per frame
      96   [ -  +  +  - ]:          2 :         unsigned eat_time = std::max(std::min(time, 800u), 200u);
      97                 :            : 
      98         [ -  + ]:          2 :         fprintf(stderr, "Resizing - combined with delay %c%u ms (using %u ms for resize)\n",
      99                 :            :             resize_first ? '+' : '-',
     100                 :            :             time, eat_time);
     101                 :            : 
     102                 :            : #ifndef TURBOHACK
     103         [ -  + ]:          2 :         if(!resize_first)
     104                 :            :         {
     105                 :          0 :             unsigned eat = (eat_time/step)*step;
     106         [ #  # ]:          0 :             if(eat > time) { time -= eat;
     107         [ #  # ]:          0 :                              data.emplace_back(std::string(""));
     108                 :          0 :                              SleepFor(time/1e3); } else time = 0;
     109                 :            :         }
     110   [ -  +  +  + ]:         34 :         for(unsigned g=step, t=0; t<eat_time; t+=g, time = time>g ? time-g : 0)
     111                 :            :         {
     112                 :         32 :             double factor = t*1.0/eat_time;
     113                 :         32 :             unsigned do_fx = vcw + int(fx-vcw)*factor;
     114                 :         32 :             unsigned do_fy = vch + int(fy-vch)*factor;
     115                 :         32 :             unsigned do_sx = ww  + int(sx-ww)*factor;
     116                 :         32 :             unsigned do_sy = wh  + int(sy-wh)*factor;
     117                 :         32 :             if(1)
     118                 :            :             {
     119                 :         32 :                 std::lock_guard<std::mutex> lk(lock);
     120   [ +  -  +  -  :         64 :                 data.push_back(std::array{do_fx,do_fy,do_sx,do_sy});
                   -  - ]
     121                 :          0 :             }
     122                 :         32 :             SleepFor(g/1e3);
     123                 :            :         }
     124                 :            : #endif
     125                 :            :         // Instant
     126                 :          2 :         if(true)
     127                 :            :         {
     128                 :          2 :             std::lock_guard<std::mutex> lk(lock);
     129   [ +  -  +  -  :          4 :             data.push_back(std::array{fx,fy,sx,sy});
                   -  - ]
     130                 :          0 :         }
     131         [ +  - ]:          2 :         data.emplace_back(std::string(""));
     132         [ -  + ]:          2 :         if(time > 0)
     133                 :            :         {
     134                 :            : #ifdef TURBOHACK
     135                 :            :             if(TimeHackActive) time = std::max(150u,std::min(200u, time / 20)); //HACK
     136                 :            : #endif
     137                 :          0 :             SleepFor(time/1e3);
     138                 :            :         }
     139                 :          2 :         ResetSeed();
     140                 :          1 :     };
     141                 :            : 
     142   [ +  +  -  + ]:       8593 :     for(std::size_t pos = 0; pos < s.size() && !terminate; ++pos)
     143                 :            :     {
     144                 :            : #ifdef TURBOHACK
     145                 :            :         if(GetTime() >= 2*60+40) TimeHackActive = false;
     146                 :            : #endif
     147                 :            : 
     148                 :            :         //std::cerr << s[pos] << '\n';
     149   [ +  +  +  + ]:       8592 :         switch(s[pos])
     150                 :            :         {
     151                 :          2 :             case U'\U00007FFD':
     152                 :          2 :             {
     153                 :          2 :                 unsigned font = s[++pos];
     154                 :          2 :                 unsigned size = s[++pos];
     155                 :            : 
     156                 :          2 :                 unsigned fx = font%32, fy = font/32;
     157                 :          2 :                 unsigned sx = size%1024, sy = size/1024;
     158                 :            : 
     159                 :            :                 /* Check if there is delay soon hereafter */
     160                 :          2 :                 std::pair<unsigned, unsigned> found_delay{ ~0u, 0 };
     161                 :            : 
     162         [ +  - ]:          2 :                 for(unsigned find = pos+1; find < s.size(); ++find)
     163                 :            :                 {
     164         [ -  + ]:          2 :                     if(s[find] == U'\U00007FFE')
     165                 :            :                     {
     166                 :          0 :                         found_delay = {find, s[find+1]};
     167                 :          0 :                         break;
     168                 :            :                     }
     169         [ -  + ]:          2 :                     if(s[find] == U'') continue; // ok
     170         [ -  + ]:          2 :                     if(s[find] == U'') continue; // ok
     171   [ -  +  -  - ]:          2 :                     if(s[find] == U'' && s[find+1] == 'd') { ++find;
     172         [ #  # ]:          0 :                                                               if(s[find+1]=='\r') ++find;
     173                 :          0 :                                                               continue; } // ok
     174   [ +  +  -  + ]:          2 :                     if(s[find] == U'\033' && s[find+1]=='f') { ++find; continue; }
     175                 :            :                     break;
     176                 :            :                 }
     177                 :            : 
     178                 :          2 :                 Do_Delay_Resize(found_delay, fx,fy,sx,sy, true);
     179                 :          2 :                 break;
     180                 :            :             }
     181                 :          3 :             case U'\U00007FFE':
     182                 :          3 :             {
     183         [ +  - ]:          3 :                 unsigned delay = s[++pos] * 250; // milliseconds
     184         [ +  - ]:          3 :                 if(!delay) break;
     185                 :            : 
     186                 :            :                 /* Check if there is resize soon hereafter */
     187                 :          3 :                 std::pair<unsigned, unsigned> found_resize{ ~0u, 0 };
     188                 :          3 :                 std::pair<unsigned, unsigned> resize_type{};
     189                 :            : 
     190         [ +  + ]:          3 :                 for(unsigned find = pos+1; find < s.size(); ++find)
     191                 :            :                 {
     192         [ -  + ]:          2 :                     if(s[find] == U'\U00007FFD')
     193                 :            :                     {
     194                 :          0 :                         resize_type = {s[find+1], s[find+2]};
     195                 :          0 :                         found_resize = {pos-1, delay/250};
     196                 :          0 :                         break;
     197                 :            :                     }
     198         [ -  + ]:          2 :                     if(s[find] == U'') continue; // ok
     199         [ -  + ]:          2 :                     if(s[find] == U'') continue; // ok
     200   [ -  +  -  - ]:          2 :                     if(s[find] == U'' && s[find+1] == 'd') { ++find;
     201         [ #  # ]:          0 :                                                               if(s[find+1]=='\r') ++find;
     202                 :          0 :                                                               continue; } // ok
     203   [ -  +  -  - ]:          2 :                     if(s[find] == U'\033' && s[find+1]=='f') { ++find; continue; }
     204                 :            :                     break;
     205                 :            :                 }
     206                 :            : 
     207         [ -  + ]:          3 :                 if(found_resize.first != ~0u)
     208                 :            :                 {
     209                 :          0 :                     auto[font,size] = resize_type;
     210                 :          0 :                     unsigned fx = font%32, fy = font/32;
     211                 :          0 :                     unsigned sx = size%1024, sy = size/1024;
     212                 :          0 :                     Do_Delay_Resize(found_resize, fx,fy,sx,sy, false);
     213                 :            :                 }
     214                 :            :                 else
     215                 :            :                 {
     216                 :            :                     //fprintf(stderr, "Performing delay: %u ms\n", delay);
     217                 :          3 :                     if(delay)
     218                 :            :                     {
     219                 :            : #ifdef TURBOHACK
     220                 :            :                         if(TimeHackActive) delay = std::max(150u,std::min(200u, delay / 20)); //HACK
     221                 :            : #endif
     222                 :          3 :                         SleepFor(delay/1e3);
     223                 :            :                     }
     224                 :          3 :                     ResetSeed();
     225                 :            :                 }
     226                 :            :                 break;
     227                 :            :             }
     228                 :         34 :             case U'\U00007FFF':
     229                 :         34 :             {
     230                 :         34 :                 cur_speed = s[++pos];
     231                 :            :                 //fprintf(stderr, "Input speed changed to %u\n", cur_speed);
     232                 :         34 :                 break;
     233                 :            :             }
     234                 :       8553 :             default:
     235                 :       8553 :             {
     236                 :       8553 :                 bool allow_merge  = false;
     237                 :       8553 :                 unsigned use_speed = cur_speed;
     238                 :       8553 :                 unsigned category = 0; // special key
     239         [ +  - ]:       8553 :                 if(s[pos] == U'\003'
     240   [ +  -  +  +  :       8553 :                 || (s[pos] == U'x' && had_ctrlk))
                   +  + ]
     241                 :            :                 {
     242                 :            :                     // Reset seed always when ^C is pressed
     243                 :          9 :                     ResetSeed();
     244                 :            :                 }
     245         [ +  + ]:       8553 :                 if(s[pos] < 0x80)
     246         [ +  + ]:      12376 :                     category = (Delay_CharClassTable[s[pos]/2] >> (4-4*(s[pos]%2))) & 15;
     247                 :       8553 :                 unsigned factor = 20;
     248         [ +  + ]:       8553 :                 if(s[pos] == prev_key)
     249                 :            :                 {
     250         [ +  + ]:        830 :                     if(repeats >= 2) factor = 15; else { factor = 70; ++repeats; }
     251                 :            :                 }
     252   [ +  +  +  +  :       7723 :                 else switch(repeats = 0, category / 2)
             +  -  +  - ]
     253                 :            :                 {
     254                 :        583 :                     case 0: // 0 special key
     255                 :        583 :                         factor = 70;
     256                 :        583 :                         break;
     257                 :        382 :                     case 1: // 2 fast ctrl
     258                 :        382 :                         factor = 13;
     259         [ +  + ]:        382 :                         if(s[pos] == U'\033')
     260                 :            :                         {
     261                 :        124 :                             had_ctrlk = true;
     262                 :            :                         }
     263                 :            :                         break;
     264                 :        374 :                     case 2: // 4 ctrl k
     265                 :        374 :                         factor = 35 + random(10);
     266                 :        374 :                         had_ctrlk = true;
     267                 :        374 :                         break;
     268                 :       4408 :                     case 3: // 6 alphanumeric
     269   [ +  +  +  + ]:       4483 :                         modifier = std::min(std::max(modifier + int(random(3)-1) * int(random(3)-1), 1), 18);
     270                 :       4408 :                         factor = (random(modifier*5) + 20 + random(46) + random(93)) / 3;
     271                 :       4408 :                         allow_merge = true;
     272         [ +  + ]:       4408 :                         if(had_ctrlk)
     273                 :            :                         {
     274                 :        488 :                             factor = 35 + random(10);
     275                 :        488 :                             had_ctrlk = false;
     276                 :            :                         }
     277                 :            :                         break;
     278                 :       1482 :                     case 4: // 8 slow key
     279                 :       1482 :                         factor = 1000 / (10 + random(51));
     280                 :       1482 :                         allow_merge = true;
     281                 :       1482 :                         break;
     282                 :          0 :                     case 5: // A pondering
     283                 :          0 :                         factor = 224;
     284                 :          0 :                         break;
     285                 :        494 :                     case 6: // C arrow
     286                 :            :                         // TODO: If previous key was ctrl-Z, give factor=20
     287                 :        494 :                         factor = 1000 / (8 + random(66)); // max: 125ms, min: 13ms
     288                 :        494 :                         break;
     289                 :            :                 }
     290                 :            : #ifdef TURBOHACK
     291                 :            :                 unsigned u=use_speed;
     292                 :            :                 #define use_speed u
     293                 :            :                 if(TimeHackActive) use_speed = std::min(2u, use_speed);
     294                 :            : #endif
     295                 :       8553 :                 unsigned delay = factor * use_speed / 16;
     296         [ +  + ]:       8553 :                 if(use_speed == 1) delay /= 50;
     297                 :            : 
     298                 :       8553 :                 SleepFor(delay/1e3);
     299                 :       8553 :                 prev_key = s[pos];
     300         [ +  - ]:      17106 :                 std::string str = ToUTF8(std::u32string(1, s[pos]));
     301                 :            : 
     302                 :       8553 :                 {
     303         [ +  - ]:       8553 :                 std::lock_guard<std::mutex> lk(lock);
     304   [ +  +  +  +  :       8553 :                 if(allow_merge && !data.empty() && std::holds_alternative<std::string>(data.back()))
                   -  + ]
     305   [ +  -  +  -  :      13438 :                     std::get<std::string>(data.back()) += std::move(str);
                   +  - ]
     306                 :            :                 else
     307         [ +  - ]:       3668 :                     data.emplace_back(std::move(str));
     308                 :          0 :                 }
     309                 :            : 
     310                 :       8553 :                 break;
     311                 :            :             }
     312                 :            :         }
     313                 :            :     }
     314                 :          1 : }
     315                 :            : 
     316                 :      39050 : AutoInputResponse GetAutoInput()
     317                 :            : {
     318                 :      39050 :     std::lock_guard<std::mutex> lk(lock);
     319         [ +  + ]:      39050 :     if(data.empty())
     320                 :            :     {
     321                 :      35349 :         return std::string{};
     322                 :            :     }
     323                 :       3701 :     auto result = std::move(data.front());
     324                 :       3701 :     data.pop_front();
     325                 :       3701 :     return result;
     326         [ +  - ]:      39050 : }
     327                 :            : 
     328                 :            : #include <fstream>
     329                 :            : 
     330                 :          1 : void AutoInputStart(std::string_view filename)
     331                 :            : {
     332         [ +  - ]:          1 :     std::string s;
     333                 :          1 :     try {
     334   [ +  -  +  - ]:          1 :         std::ifstream t(std::string(filename), std::ios::binary);
     335         [ +  - ]:          1 :         t.seekg(0, std::ios::end);
     336   [ +  -  +  - ]:          1 :         s.reserve(t.tellg());
     337         [ +  - ]:          1 :         t.seekg(0, std::ios::beg);
     338         [ +  - ]:          1 :         s.assign( std::istreambuf_iterator<char>(t),
     339                 :            :                   std::istreambuf_iterator<char>() );
     340                 :          1 :     }
     341                 :          0 :     catch(...)
     342                 :            :     {
     343         [ -  - ]:          0 :     }
     344         [ +  - ]:          1 :     if(!s.empty())
     345                 :            :     {
     346                 :          1 :         finished = false;
     347                 :            :         //s = "rm -f 'gruu.cc' '.#gruu.cc'\nstrace -otmptmp joe -tab 4 gruu.cc\nu" + s;
     348         [ +  - ]:          1 :         std::thread t([](std::u32string data){
     349         [ +  - ]:          1 :             AutoInputProvider(data);
     350                 :          1 :             finished = true;
     351   [ +  -  +  - ]:          1 :         }, FromUTF8(s));
     352         [ +  - ]:          1 :         t.detach();
     353                 :          1 :     }
     354                 :          1 : }
     355                 :          1 : void AutoInputEnd()
     356                 :            : {
     357                 :          1 :     terminate = true;
     358                 :          1 : }
     359                 :            : 
     360                 :      39052 : bool AutoInputActive()
     361                 :            : {
     362                 :      39052 :     return finished == false;
     363                 :            : }
     364                 :            : 
     365                 :            : #ifdef RUN_TESTS
     366                 :          3 : TEST(AutoInput, active_should_be_false)
     367                 :            : {
     368                 :          1 :     EXPECT_FALSE(AutoInputActive());
     369                 :          1 : }
     370                 :          3 : TEST(AutoInput, works)
     371                 :            : {
     372                 :          1 :     SetTimeFactor(0.);
     373                 :          1 :     AutoInputStart("test/inputter.dat");
     374                 :          1 :     unsigned num_events = 0;
     375                 :          1 :     AdvanceTime(3600);
     376         [ +  + ]:      39051 :     while(AutoInputActive())
     377                 :            :     {
     378                 :      39050 :         auto resp = GetAutoInput();
     379                 :      39050 :         ++num_events;
     380                 :      39050 :     }
     381                 :          1 :     EXPECT_GE(num_events, 10000u);
     382                 :          1 :     EXPECT_LT(num_events, 150000u);
     383                 :          1 :     AutoInputEnd();
     384                 :          1 :     terminate = false;
     385                 :          1 : }
     386                 :            : #endif

Generated by: LCOV version 1.16