//KeyboardBuffer.java import java.awt.*; import java.awt.event.*; import java.io.*; /** * * This is a subclass of TextArea which keeps track of the * location of the cursor, and distinguishes text typed * below the cursor from text printed or edited above the * cursor. It is intended as a helper class for ConsoleWindow * */ class windowOutputStream extends java.io.OutputStream { KeyboardBuffer window; byte buffer [ ] = new byte[128]; int idx = 0; public void flush () throws IOException { if(idx>0) { int n = idx; idx=0; write(buffer,0,n); } } void safeflush () throws IOException { flush(); } windowOutputStream (KeyboardBuffer window) { this.window = window; } public void write (int b) throws IOException { buffer[idx++]=(byte)b; if(idx>=buffer.length) {flush();} } public void write (byte b[], int from, int len) throws IOException { flush(); window.write(b,from,len); } } class windowInputStream extends java.io.InputStream { KeyboardBuffer window; windowInputStream (KeyboardBuffer window) { this.window = window; } public int available () throws IOException { window.outputstream.safeflush(); return(window.available()); } public int read () throws IOException { window.outputstream.safeflush(); int newchar = window.read(); return(newchar); } public int read(byte ar[],int from, int len) throws IOException {int nchars=0; while(ncharsmarked_position)) { if(!linemode) {charisready = true; } else {for(int i=marked_position; i0)) {charisready=false; super.insert(str,pos); if(pos0) {String str = new String(sb,0,len); int current_len = getText().length(); if((current_len+len) > bufferchars) {/* get rid of some */ int remchars = Math.min(200,current_len/3); replaceRange("",0,remchars-1); } {charisready=false; boolean oldkd = keyisdown; keyisdown=true; charisready=false; insert(str,marked_position); marked_position+=str.length(); keyisdown=oldkd; set_charisready(); }} } public synchronized void replaceRange (String str, int start, int end) { charisready=false; //System.out.println("Replacerange " + str + " " + start + " " + end); super.replaceRange(str,start,end); if(marked_position>=end) { AddToMarkedPosition(str.length()-(end-start)); } else {set_charisready();}; } private char readChar () { char value = (char)0; if(charisready) { String str = getText(); int newlen = str.length(); if((newlen>marked_position)) {value = str.charAt(marked_position++); } } return(value); } private void Do_Obituary () throws IOException { if(dead) throw new IOException(this.toString() + ": I'm dead" ); } private char waitChar () throws IOException { char ch=(char)0; while(!dead && (ch=readChar())==(char)0) { suspendedthread=Thread.currentThread(); //System.out.println("Going to suspend thread" + suspendedthread); suspendedthread.suspend(); //System.out.println("thread" + suspendedthread + " running again"); } Do_Obituary(); //System.out.println("WaitChar returns " + (int)ch ); return(ch); } // stream methods public void die () { dead=true; if(suspendedthread!=null) { suspendedthread.resume(); } } public int available () { /* here's a tricky bit: there are at least three threads involved here, the system thread actually maintaining the buffer, the console thread running the window, and the client thread reading/writing the streams. Rather than let the client know what's really going on, we only tell him what we want him to know, so he doesn't accidentally notice the buffer in some transient state */ return(charisready ? getText().length()-marked_position : 0); } public int read () throws IOException { char ch=waitChar(); return((int)ch); } public void write (int v) throws IOException { char ch =(char)v; /* although this looks circuitous, this should actually be cons free */ Character chr = new Character(ch); Do_Obituary(); insertTextBeforeMark(chr.toString()); } public void write (byte data[], int from, int len) throws IOException { Do_Obituary(); insertTextBeforeMark(new String(data,from,len)); } }