diff options
author | tomsmeding <tom.smeding@gmail.com> | 2017-03-02 21:23:39 +0100 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2017-03-02 21:23:39 +0100 |
commit | fb0cdfe743869f6592090e441a071f0904a80634 (patch) | |
tree | 60a6caee28ab702efb2a3e72b6cf5644ca1949c1 | |
parent | 39dbc1934d506f4b1da32621a8ff7e2e165a0bb0 (diff) |
Optimize terminal usage
The ^C-catching stuff will not work on windows; need to look into that
-rw-r--r-- | main.cpp | 22 | ||||
-rw-r--r-- | screenbuffer.cpp | 152 | ||||
-rw-r--r-- | screenbuffer.h | 24 | ||||
-rw-r--r-- | world.cpp | 24 | ||||
-rw-r--r-- | world.h | 4 |
5 files changed, 216 insertions, 10 deletions
@@ -8,6 +8,7 @@ #include <tuple> #include <cctype> #include <cassert> +#include <signal.h> #include <sys/time.h> #include "params.h" #include "world.h" @@ -210,6 +211,16 @@ string preprocess(istream &file){ } +static ScreenBuffer *sb; + +static void signalHandler(int sig){ + if(sig==SIGINT){ + sb->emergencyDeinit(); + _exit(130); + } +} + + int main(int argc,char **argv){ struct timeval tv; gettimeofday(&tv,nullptr); @@ -249,11 +260,14 @@ int main(int argc,char **argv){ } } - world.print(); + sb=new ScreenBuffer(SIZE*3,SIZE); + signal(SIGINT,signalHandler); + world.print(*sb); for(int i=0;i<C::autoTimeout;i++){ - world.tick(); usleep(3000); - cout<<"\x1B[2J\x1B[H"; - world.print(); + world.tick(); + world.print(*sb); + sb->draw(); } + delete sb; } diff --git a/screenbuffer.cpp b/screenbuffer.cpp new file mode 100644 index 0000000..a382e82 --- /dev/null +++ b/screenbuffer.cpp @@ -0,0 +1,152 @@ +#define _GNU_SOURCE +#include <iostream> +#include <cstdarg> +#include <cstdlib> +#include <cstring> +#include <cassert> +#include <unistd.h> +#include "screenbuffer.h" + +using namespace std; + + +static void initTerminal(){ + cout<<"\x1B[?1049h" // start alternate screen + "\x1B[2J" // clear screen + "\x1B[H" // move to top left + <<flush; +} + +static void deinitTerminal(){ + cout<<"\x1B[?1049l" // end alternate screen + <<flush; +} + + +ScreenBuffer::ScreenBuffer(int W,int H) + :W(W),H(H){ + screen=new char[W*H]; + prevscreen=new char[W*H]; + memset(screen,' ',W*H); + memset(prevscreen,' ',W*H); + initTerminal(); +} + +ScreenBuffer::~ScreenBuffer(){ + delete[] screen; + delete[] prevscreen; + deinitTerminal(); +} + +void ScreenBuffer::moveto(int x,int y){ + assert(x>=0&&x<W&&y>=0&&y<H); + curx=x; + cury=y; +} + +void ScreenBuffer::printstr(const char *buf){ + int basex=curx; + for(int i=0;buf[i];i++){ + if(buf[i]!='\n'){ + if(curx==W){ + if(cury==H-1){ + curx--; + break; + } + curx=basex; + cury++; + } + screen[W*cury+curx]=buf[i]; + curx++; + } else { + if(cury==H-1)break; + curx=basex; + } + } + + if(curx==W){ + if(cury==H-1){ + curx--; + } else { + curx=basex; + cury++; + } + } +} + +__attribute__((format (printf, 2, 3))) +int ScreenBuffer::printf(const char *format,...){ + va_list ap; + va_start(ap,format); + char *buf; + int ret=vasprintf(&buf,format,ap); + va_end(ap); + assert(buf); + + printstr(buf); + + free(buf); + return ret; +} + +__attribute__((format (printf, 4, 5))) +int ScreenBuffer::mvprintf(int x,int y,const char *format,...){ + moveto(x,y); + + va_list ap; + va_start(ap,format); + char *buf; + int ret=vasprintf(&buf,format,ap); + va_end(ap); + assert(buf); + + printstr(buf); + + free(buf); + return ret; +} + +void ScreenBuffer::draw(){ + bool changed[W*H]; + for(int i=0;i<W*H;i++){ + changed[i]=screen[i]!=prevscreen[i]; + } + for(int y=0;y<H;y++){ + for(int x=1;x<W-1;x++){ + changed[W*y+x]|=changed[W*y+x-1]&changed[W*y+x+1]; + } + } + + bool prepped=false; + + for(int y=0;y<H;y++){ + bool needtomove=true; + for(int x=0;x<W;x++){ + if(!changed[W*y+x]){ + needtomove=true; + continue; + } + if(!prepped){ + cout<<"\x1B[?25l"; // invisible cursor + prepped=true; + } + if(needtomove){ + cout<<"\x1B["<<y+1<<';'<<x+1<<'H'; + needtomove=false; + } + cout.put(screen[W*y+x]); + } + } + if(prepped){ + cout<<"\x1B[?12;25h" // visible cursor + <<"\x1B["<<H<<';'<<W<<'H' // move past screen + <<flush; + } + + memcpy(prevscreen,screen,W*H); +} + +void ScreenBuffer::emergencyDeinit(){ + static const char *str="\n\x1B[?1049l\x1B[12;25h^C\n"; + write(1,str,strlen(str)); +} diff --git a/screenbuffer.h b/screenbuffer.h new file mode 100644 index 0000000..49cc5c5 --- /dev/null +++ b/screenbuffer.h @@ -0,0 +1,24 @@ +#pragma once + +using namespace std; + + +class ScreenBuffer{ + int W,H; + char *prevscreen,*screen; + int curx=0,cury=0; + + void printstr(const char *buf); + +public: + ScreenBuffer(int W,int H); + ~ScreenBuffer(); + + void moveto(int x,int y); + int printf(const char *format,...) __attribute__((format (printf, 2, 3))); + int mvprintf(int x,int y,const char *format,...) __attribute__((format (printf, 4, 5))); + + void draw(); + + void emergencyDeinit(); +}; @@ -316,14 +316,28 @@ Robot* World::targetbot(const Robot *r){ return *targetbotptr(r); } -void World::print() const { +void World::print(ostream &os) const { for(int y=0;y<SIZE;y++){ for(int x=0;x<SIZE;x++){ - if(board[y][x]==nullptr)cout<<".."; - else cout<<string(2,"^>v<"[board[y][x]->heading%4]); - cout<<' '; + if(board[y][x]==nullptr)os<<".."; + else os<<string(2,"^>v<"[board[y][x]->heading%4]); + os<<' '; + } + os<<endl; + } +} + +void World::print(ScreenBuffer &sb) const { + for(int y=0;y<SIZE;y++){ + sb.moveto(0,y); + for(int x=0;x<SIZE;x++){ + if(board[y][x]==nullptr)sb.printf(".."); + else { + char c="^>v<"[board[y][x]->heading%4]; + sb.printf("%c%c",c,c); + } + sb.printf(" "); } - cout<<endl; } } @@ -6,6 +6,7 @@ #include <vector> #include <cstdint> #include "params.h" +#include "screenbuffer.h" using namespace std; @@ -100,7 +101,8 @@ public: void tick(); Robot* targetbot(const Robot *r); - void print() const; + void print(ostream &os) const; + void print(ScreenBuffer &sb) const; }; |