aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2017-03-02 21:23:39 +0100
committertomsmeding <tom.smeding@gmail.com>2017-03-02 21:23:39 +0100
commitfb0cdfe743869f6592090e441a071f0904a80634 (patch)
tree60a6caee28ab702efb2a3e72b6cf5644ca1949c1
parent39dbc1934d506f4b1da32621a8ff7e2e165a0bb0 (diff)
Optimize terminal usage
The ^C-catching stuff will not work on windows; need to look into that
-rw-r--r--main.cpp22
-rw-r--r--screenbuffer.cpp152
-rw-r--r--screenbuffer.h24
-rw-r--r--world.cpp24
-rw-r--r--world.h4
5 files changed, 216 insertions, 10 deletions
diff --git a/main.cpp b/main.cpp
index 3d4a048..d75c253 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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();
+};
diff --git a/world.cpp b/world.cpp
index b74e7cb..0af24a2 100644
--- a/world.cpp
+++ b/world.cpp
@@ -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;
}
}
diff --git a/world.h b/world.h
index e964e8d..caaa4f0 100644
--- a/world.h
+++ b/world.h
@@ -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;
};