aboutsummaryrefslogtreecommitdiff
path: root/screenbuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'screenbuffer.cpp')
-rw-r--r--screenbuffer.cpp152
1 files changed, 152 insertions, 0 deletions
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));
+}