root/lang/actionscript/FxTerm/trunk/FxTerm.as @ 7245

Revision 7245, 32.3 kB (checked in by frsyuki, 5 years ago)

lang/actionscript/FxTerm: added ansi color selector

Line 
1
2package
3{
4
5import flash.events.*;
6import flash.utils.ByteArray;
7import flash.display.Sprite;
8import flash.display.Shape;
9import flash.display.BitmapData;
10import flash.display.Bitmap;
11import flash.geom.Rectangle;
12import flash.text.TextField;
13import flash.text.TextFieldAutoSize;
14import flash.text.TextFormat;
15import flash.utils.IDataOutput;
16
17public class FxTerm extends Sprite implements IDataOutput
18{
19        // XXX embed font
20        //[Embed(source='M+2VM+IPAG-circle.ttf', fontName='mplus-ipa')]
21        //private static const mPlusIPA:Class
22
23        private var screen:Array;  // Array of Array of TextCell
24
25        private var curattr:uint;  // current attribute
26
27        private var cols:int = 0;       // cols by single character wide
28        private var rows:int = 0;       // rows by single character wide
29        private var ccol:int = 0;       // current col by single character wide
30        private var crow:int = 0;       // current row by single character wide
31        private var saved_col:int = 0;
32        private var saved_row:int = 0;
33        private var scrolltop:int;
34        private var scrollbottom:int;
35
36        private var lineDirty:Array;      // Array of Boolean
37        private var cursorDirty:Boolean;
38
39        private var escapeSequence:ByteArray;
40        private var escaped:Boolean = false;
41        private var graphmode:Boolean = false;
42
43        private var drawBuffer:ByteArray;
44
45        private var _decoderBuffer:ByteArray;
46
47        private var ansiForegroundColors:Array;    // Array of RGB, length must be 7
48        private var ansiBackgroundColors:Array;    // Array of RGB, length must be 7
49        private var displayTextFormat:TextFormat;
50        private var lineHeight:uint;
51        private var cursorWidth:uint;
52        private var calibrateFontWidth:uint;
53        private var calibrateFontHeight:uint;
54        private var lineGapFilling:uint;
55
56        private var fieldArray:Array;     // Array of TextField
57        private var background:Bitmap;    // background bitmap
58        private var cursorCanvas:Shape;   // canvas for drawing cursor
59
60        private var characterWidthMeasure:TextField;  // FIXME
61
62        private const ESCAPED_TEXT_MAX:uint = 128;
63        private const MAX_CSI_ES_PARAMS:uint = 32;
64
65        // IDataOutput interface
66        private var _middleBuffer:ByteArray;
67
68        /**
69         * Character code of input stream.
70         */
71        public var charSet:String;
72
73        /**
74         * Constructor.
75         * @param cols  Number of columns
76         * @param rows  Number of rows
77         */
78        public function FxTerm(col:uint = 80, row:uint = 20):void
79        {
80                super();
81
82                screen = new Array();
83                lineDirty = new Array();
84                escapeSequence = new ByteArray();
85
86                // default color setting
87                ansiForegroundColors = [
88                                0xF0F0F0,  // normal
89                                0xFF0000,  // red
90                                0x00FF00,  // green
91                                0xFFFF00,  // yellow
92                                0x6E6EFF,  // blue
93                                0xFF00FF,  // magenta
94                                0x00FFFF,  // cyan
95                                0xFFFFFF,  // white
96                        ];
97                ansiBackgroundColors = [
98                                //0x1F1F1F,  // normal
99                                0x000000,  // normal
100                                0xFF0000,  // red
101                                0x00FF00,  // green
102                                0xFFFF00,  // yellow
103                                0x6E6EFF,  // blue
104                                0xFF00FF,  // magenta
105                                0x00FFFF,  // cyan
106                                0xFFFFFF,  // white
107                        ];
108
109                fieldArray = new Array();
110                background = new Bitmap();
111                this.addChild(background);
112                cursorCanvas = new Shape();   // cursorCanvas must be above background
113                this.addChild(cursorCanvas);
114
115                charSet = "utf-8";  // default charset
116                lineGapFilling = 3; // FIXME
117
118                displayTextFormat = new TextFormat();
119                //displayTextFormat.font = "mplus-ipa";  // XXX embed font
120                displayTextFormat.font = "_typewriter";
121                displayTextFormat.kerning = false;
122                displayTextFormat.size = 12;
123                fontCalibration();
124
125                // FIXME
126                characterWidthMeasure = new TextField();
127                characterWidthMeasure.autoSize = TextFieldAutoSize.LEFT;
128                characterWidthMeasure.defaultTextFormat = displayTextFormat;
129
130                drawBuffer = new ByteArray();
131                _decoderBuffer = new ByteArray();
132                _middleBuffer = new ByteArray();
133
134                resize(col, row);
135        }
136
137        /**
138         * Set console text format.
139         * @param format  The text format. <i>color</i> parameter is ignored.
140         *                Use setForegroundColors or setForegroundColor method instead.
141         */
142        public function set textFormat(format:TextFormat):void
143        {
144                displayTextFormat = format;
145                fontCalibration();
146                adjustDisplay();
147        }
148
149        /**
150         * Get font size.
151         */
152        public function get fontSize():Object
153        {
154                return displayTextFormat.size;
155        }
156
157        /**
158         * Set font size.
159         */
160        public function set fontSize(value:Object):void
161        {
162                displayTextFormat.size = value;
163                textFormat = displayTextFormat;
164        }
165
166        /**
167         * Get font family.
168         */
169        public function get font():String
170        {
171                return displayTextFormat.font;
172        }
173
174        /**
175         * Set font family.
176         */
177        public function set font(value:String):void
178        {
179                displayTextFormat.font = value;
180                textFormat = displayTextFormat;
181        }
182
183        /**
184         * Set ANSI text colors.
185         * @param color8  Array of RGB color code whose length is 8
186         */
187        public function set foregroundColors(color8:Array):void
188        {
189                ansiForegroundColors = color8;
190        }
191
192        /**
193         * Set ANSI background colors.
194         * @param color8  Array of RGB color code whose length is 8
195         */
196        public function set backgroundColors(color8:Array):void
197        {
198                ansiBackgroundColors = color8;
199        }
200
201        /**
202         * Get ANSI text color.
203         * @param n      ANSI color code
204         */
205        public function getForegroundColor(n:uint):uint
206        {
207                return ansiForegroundColors[n];
208        }
209
210        /**
211         * Set ANSI text color.
212         * @param n      ANSI color code
213         * @param color  RGB color code
214         */
215        public function setForegroundColor(n:uint, color:uint):void
216        {
217                ansiForegroundColors[n] = color;
218                refresh(true);  // force refresh
219        }
220
221        /**
222         * Get ANSI background color.
223         * @param n      ANSI color code
224         */
225        public function getBackgroundColor(n:uint):uint
226        {
227                return ansiBackgroundColors[n];
228        }
229
230        /**
231         * Set ANSI background color.
232         * @param n      ANSI color code
233         * @param color  RGB color code
234         */
235        public function setBackgroundColor(n:uint, color:uint):void
236        {
237                ansiBackgroundColors[n] = color;
238                refresh(true);  // force refresh
239        }
240
241        /**
242         * Get number of columns
243         */
244        public function get col():uint
245        {
246                return cols;
247        }
248
249        /**
250         * Set number of columns
251         */
252        public function set col(n:uint):void
253        {
254                resize(n, rows);
255        }
256
257        /**
258         * Get number of rows
259         */
260        public function get row():uint
261        {
262                return rows;
263        }
264
265        /**
266         * Set number of rows
267         */
268        public function set row(n:uint):void
269        {
270                resize(cols, n);
271        }
272
273        /**
274         * Resize the console size.
275         * @param newcols  Number of columns
276         * @param newrows  Number of rows
277         */
278        public function resize(newcols:uint, newrows:uint):void
279        {
280                if( newcols <= 1 ) { newcols = 1; }
281                if( newrows <= 1 ) { newrows = 1; }
282
283                screen.length = newrows;
284                lineDirty.length = newrows;
285
286                var r:int;
287                var c:int;
288                var tf:TextField;
289                var sc:Array;
290
291                if( rows < newrows ) {
292                        fieldArray.length = newrows;
293                        // expand rows
294                        for(r = rows; r < newrows; ++r) {
295                                sc = screen[r] = new Array();
296                                sc.length = cols;   // FIXME cols < newcols ? cols : newcols
297                                for(c = 0; c < cols; ++c) {
298                                        sc[c] = new TextCell();
299                                }
300                                tf = fieldArray[r] = new TextField();
301                                tf.autoSize = TextFieldAutoSize.LEFT;
302                                tf.defaultTextFormat = displayTextFormat;
303                                tf.selectable = true;
304                                //tf.embedFonts = true;  // XXX embed font
305                                tf.x = 0;
306                                // tf.y will be set at adjustDisplay()
307                                addChild(tf);
308                        }
309                } else if( rows > newrows ) {
310                        // reduce rows
311                        var redto:int = numChildren - (rows - newrows);
312                        for(r = numChildren-1; r >= redto; --r) {
313                                removeChildAt(r);
314                        }
315                }
316
317                if( cols < newcols ) {
318                        // expand cols
319                        for(r = 0; r < newrows; ++r) {
320                                sc = screen[r];
321                                sc.length = newcols;
322                                for(c = cols; c < newcols; ++c) {
323                                        sc[c] = new TextCell();
324                                }
325                        }
326                } else if( cols > newcols ) {
327                        // reduce cols
328                        for(r = 0; rows < newrows; ++r) {
329                                screen[r].length = newcols;
330                                // FIXME cut fieldArray[r] short?
331                        }
332                }
333
334                cols = newcols;
335                rows = newrows;
336
337                if( ccol >= cols ) { ccol = cols - 1; }
338                if( crow >= rows ) { crow = rows - 1; }
339
340                // FIXME
341                scrolltop = 0;
342                scrollbottom = rows - 1;
343
344                adjustDisplay();
345        }
346
347        private function fontCalibration():void
348        {
349                var tf:TextField = new TextField();
350                tf.width = 0;
351                tf.height = 0;
352                tf.autoSize = TextFieldAutoSize.LEFT;
353                tf.defaultTextFormat = displayTextFormat;
354                tf.text = "hj";
355                calibrateFontWidth  = tf.width / 2 - 2;
356                calibrateFontHeight = tf.height;
357                lineHeight = tf.height - lineGapFilling;
358        }
359
360        private function adjustDisplay():void
361        {
362                var dwidth:Number  = calibrateFontWidth  * cols;
363                var dheight:Number = lineHeight * rows + lineGapFilling;
364                background.bitmapData = new BitmapData(
365                                dwidth,
366                                dheight,
367                                false,   // no transparent
368                                ansiBackgroundColors[0]  // fill with normal background color
369                                );
370                //cursorCanvas.width = dwidth;
371                //cursorCanvas.height = dheight;
372                //this.width = dwidth;
373                //this.height = dheight;
374                for(var r:uint = 0; r < rows; ++r) {
375                        fieldArray[r].y = lineHeight * r;
376                }
377                refresh(true);  // force refresh
378        }
379
380
381        /**
382         * refresh the console
383         * @param force  force redraw whole screen
384         */
385        public function refresh(force:Boolean = true):void
386        {
387                if(force) {
388                        for(var f:uint = 0; f < rows; ++f) {
389                                lineDirty[f] = true;
390                        }
391                }
392
393                var field:TextField;
394                var attr:uint = screen[0][0];
395                var attrStart:uint;
396
397                drawBuffer.length = 0;
398                for(var r:uint = 0; r < rows; ++r) {
399                        if( !lineDirty[r] ) { continue; }  // redraw only dirty lines
400                        lineDirty[r] = false;
401                        field = fieldArray[r];
402                        field.text = "";
403                        field.width = 0;
404                        attrStart = 0;
405                        for(var c:uint = 0; c < cols; ++c) {
406                                if( attr != screen[r][c].attr ) {
407                                        attrStart += appendText(field, drawBuffer, attr, attrStart);
408                                        attr = screen[r][c].attr;
409                                }
410                                drawBuffer.writeMultiByte(screen[r][c].text, "utf-8");
411                                if( screen[r][c].wide ) {
412                                        ++c;
413                                }
414                        }
415                        if( drawBuffer.length > 0 ) {
416                                appendText(field, drawBuffer, attr, attrStart);
417                        }
418                }
419
420                // draw cursor
421                if( cursorDirty || force ) {
422                        cursorDirty = false;
423                        cursorCanvas.graphics.clear();
424                        // FIXME  cursor color
425                        cursorCanvas.graphics.beginFill(ansiForegroundColors[0], 1.0);
426                        cursorCanvas.graphics.drawRect(
427                                        (this.width / cols - 0.05) * ccol + 2,
428                                        lineHeight * crow + lineGapFilling,
429                                        ( screen[crow][ccol].wide ?
430                                          calibrateFontWidth * 2 - 2 :  // wide width
431                                          calibrateFontWidth ),
432                                        lineHeight );
433                }
434        }
435
436        private function appendText(field:TextField, buf:ByteArray, attr:uint, attrStart:uint):uint
437        {
438                buf.position = 0;
439                var str:String = buf.readMultiByte(buf.length, "utf-8");
440                buf.length = 0;
441
442                if(str.length == 0) { return 0; }
443       
444                var textStart:Number = field.width;
445       
446                // FIXME copy?
447                //var format:TextFormat = new TextFormat(displayTextFormat);
448                var format:TextFormat = displayTextFormat;
449                format.color = ansiForegroundColors[ attrForeground(attr) ];
450       
451                field.appendText(str);
452                field.setTextFormat(format, attrStart, attrStart + str.length);
453       
454                var textEnd:Number = field.width;
455       
456                // draw background color
457                background.bitmapData.fillRect(
458                                new Rectangle(
459                                        textStart - 2,
460                                        field.y + lineGapFilling,
461                                        textEnd - textStart + 1,
462                                        lineHeight
463                                        ),
464                                ansiBackgroundColors[ attrBackground(attr) ]
465                                );
466       
467                return str.length;
468        }
469
470        /**
471         * Writes the specified byte array to the console.
472         */
473        public function writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0):void
474        {
475                var decoder:ByteArray = _decoderBuffer;
476                var c:String;
477                var len:uint = (length != 0 ? length : bytes.length);
478                for(var n:uint = 0; n < len; ++n) {
479                        var b:uint = bytes[n];
480                        if( escaped ) {
481                                // escape sequence
482                                escapeSequence.writeByte(b);
483                                tryInterpretEscapeSequence();
484                        } else if( decoder.length == 0 ) {
485                                if( b >= 1 && b <= 31 ) {
486                                        // control character
487                                        handleControlChar(b);
488                                } else if( b <= 0x7F ) {
489                                        // single byte character
490                                        if( b != 0 ) {  // ignore NULL
491                                                c = String.fromCharCode(b); // FIXME
492                                                // TODO: graphmode
493                                                putNormalCharacter(c);
494                                        }
495                                } else {
496                                        // first byte of multibyte character
497                                        decoder.writeByte(b);
498                                }
499                        } else {
500                                decoder.position = decoder.length;
501                                decoder.writeByte(b);
502                                try {
503                                        decoder.position = 0;
504                                        c = decoder.readMultiByte(decoder.length, charSet);
505                                        if( c.length > 0 ) {
506                                                // complete multibyte character
507                                                decoder.length = 0;
508                                                putNormalCharacter(c);
509                                        }
510                                } catch (e:Error) {
511                                        // insufficient data to complete multibyte character
512                                }
513                        }
514                }
515        }
516
517        private function cursorLineDown():void
518        {
519                crow++;
520                cursorDirty = true;
521                if( crow <= scrollbottom ) { return; }
522
523                // must scroll the scrolling region up by 1 line, and put cursor on
524                // last line of it
525                crow = scrollbottom;
526
527                var line:Array;
528                // splice back of scroll bottom
529                var outpos:Array = screen.splice(scrollbottom + 1);
530                // splice scroll region
531                var region:Array = screen.splice(scrolltop + 1);
532                // pop scroll top
533                var clearbuf:Array = screen.pop();
534                // concat scroll region
535                screen = screen.concat(region); // FIXME which is faster?
536                //for each(line in region) { screen.push(line); }
537                // push empty line
538                clearScreenRow(clearbuf, 0);
539                screen.push(clearbuf);
540                // concat back of scroll bottom
541                //screen = screen.concat(outpos); // FIXME which is faster?
542                for each(line in outpos) { screen.push(line);}
543
544                for(var i:int = scrolltop; i <= scrollbottom; ++i) {
545                        lineDirty[i] = true;
546                }
547        }
548
549        private function cursorLineUp():void
550        {
551                crow--;  // crow must be signed int
552                cursorDirty = true;
553                if( crow >= scrolltop ) { return; }
554
555                // must scroll the scrolling region up by 1 line, and put cursor on
556                // first line of it
557                crow = scrolltop;
558
559                var line:Array;
560                // splice back of scroll bottom
561                var outpos:Array = screen.splice(scrollbottom + 1);
562                // pop scroll bottom
563                var clearbuf:Array = screen.pop();
564                // splice scroll region
565                var region:Array = screen.splice(scrolltop);
566                // push empty line
567                clearScreenRow(clearbuf, 0);
568                screen.push(clearbuf);
569                // concat back of scroll region
570                screen = screen.concat(region); // FIXME which is faster?
571                //for each(line in region) { screen.push(line); }
572                // concat back of scroll bottom
573                //screen = screen.concat(outpos); // FIXME which is faster?
574                for each(line in outpos) { screen.push(line); }
575
576                for(var i:int = scrolltop; i <= scrollbottom; ++i) {
577                        lineDirty[i] = true;
578                }
579        }
580
581        private function putNormalCharacter(c:String):void
582        {
583                var wide:Boolean = isWideCharacter(c);
584                if( wide && ccol+1 >= cols ) {
585                        screen[crow][ccol].wide = false;
586                        screen[crow][ccol].text = " ";
587                        cursorLineDown();
588                        ccol = 0;
589                }
590                screen[crow][ccol].text = c;
591                screen[crow][ccol].attr = curattr;
592                lineDirty[crow] = true;
593                if( ccol > 0 && screen[crow][ccol-1].wide ) {
594                        screen[crow][ccol-1].wide = false;
595                        screen[crow][ccol-1].text = " ";
596                }
597                if( wide ) {
598                        screen[crow][ccol].wide = true;
599                        screen[crow][ccol+1].wide = false;
600                        //screen[crow][ccol+1].text = " ";
601                        ccol += 2;
602                } else {
603                        if( screen[crow][ccol].wide ) {
604                                screen[crow][ccol].wide = false;
605                                screen[crow][ccol+1].wide = false;
606                        }
607                        ccol++;
608                }
609                if( ccol >= cols ) {
610                        cursorLineDown();
611                        ccol = 0;
612                }
613                cursorDirty = true;
614        }
615
616
617        // TODO
618        private function putGraphmodeCharacter(c:String):void
619        {
620                // TODO
621                putNormalCharacter(c);
622        }
623
624        private function handleControlChar(c:uint):void
625        {
626                switch (c) {
627                case 0x0d:  // carriage return
628                        trace("carriage return");
629                        ccol = 0;
630                        cursorDirty = true;
631                        return;
632                case 0x0a:  // line feed
633                        trace("line feed");
634                        cursorLineDown();
635                        break;
636                case 0x08:  // backspace
637                        if(ccol > 0) { ccol--; }
638                        cursorDirty = true;
639                        break;
640                case 0x09:  // tab
641                        var t:uint = 8 - ccol % 8;
642                        if( t == 0 ) { t = 8; }
643                        while( t > 0 ) {
644                                putNormalCharacter(' ');
645                                --t;
646                        }
647                        break;
648                case 0x1B:  // begin escape sequence (aborting previous one if any)
649                        newEscapeSequence();
650                        break;
651                case 0x0E:  // enter graphical character mode
652                        graphmode = true;
653                        break;
654                case 0x0F: // exit graphical character mode
655                        graphmode = false;
656                        break;
657                case 0x9B:  // CSI character. Equivalent to ESC [
658                        newEscapeSequence();
659                        escapeSequence.writeByte('['.charCodeAt());
660                        break;
661                case 0x18:  // these interrupt escape sequences
662                case 0x1A:
663                        cancelEscapeSequence();
664                        break;
665                case 0x07:  // bell
666                        // FIXME visual bell?
667                        break;
668                default:
669                        trace("Unrecognized control char: " + c);
670                }
671        }
672
673        private function tryInterpretEscapeSequence():void
674        {
675                var firstchar:uint = escapeSequence[0];
676                var lastchar:uint = escapeSequence[escapeSequence.length-1];
677                if( firstchar == '['.charCodeAt() ) {
678                        // CSI escape sequence
679                        if(     (lastchar >= 'a'.charCodeAt() && lastchar <= 'z'.charCodeAt()) ||
680                                        (lastchar >= 'A'.charCodeAt() && lastchar <= 'Z'.charCodeAt()) ||
681                                        lastchar == '@'.charCodeAt() || lastchar == '`'.charCodeAt() ) {
682                                interpretCSI();
683                                cancelEscapeSequence();
684                        } else {
685                                // could not yet be parsed
686                                return;
687                        }
688                } else if( firstchar == ']'.charCodeAt() ) {
689                        // XTerm escape sequence
690                        if(lastchar == 0x07) {
691                                // TODO
692                                trace("Ignoreing XTerm ES: " + escapeSequence.toString());
693                                cancelEscapeSequence();
694                        } else {
695                                // could not yet be parsed
696                                return;
697                        }
698                } else {
699                        trace("Unrecognized ES: " + escapeSequence.toString());
700                        cancelEscapeSequence();
701                }
702        }
703
704        private function newEscapeSequence():void
705        {
706                escaped = true
707                escapeSequence.length = 0;
708        }
709
710        private function cancelEscapeSequence():void
711        {
712                escaped = false;
713                escapeSequence.length = 0;
714        }
715
716        private function interpretCSI():void
717        {
718                if( escapeSequence[1] == '?'.charCodeAt() ) {
719                        /* private-mode CSI */
720                        trace("Ignoreing private-mode CSI");
721                        return;
722                }
723
724                var csiparam:Array = new Array();
725                var len:uint = escapeSequence.length;
726
727                // parse numeric parameters
728                for(var n:uint = 1; n < len - 1; ++n) {
729                        var b:uint = escapeSequence[n];
730                        if( b == ';'.charCodeAt() ) {
731                                if(csiparam.length >= MAX_CSI_ES_PARAMS) { return; }  // too long!
732                                csiparam.push(0);
733                        } else if( b >= '0'.charCodeAt() && b <= '9'.charCodeAt() ) {
734                                if( csiparam.length == 0 ) { csiparam.push(0); }
735                                csiparam.push( csiparam.pop() * 10 + b - '0'.charCodeAt() );
736                        } else {
737                                break;
738                        }
739                }
740                trace("csi " + escapeSequence + ": " + csiparam);
741
742                var verb:uint = escapeSequence[escapeSequence.length-1];
743
744                switch(verb) {
745                case 'm'.charCodeAt(): // it's a 'set attribute' sequence
746                        interpretCSI_SGR(csiparam);
747                        break;
748                case 'J'.charCodeAt(): // it's an 'erase display' sequence
749                        interpretCSI_ED(csiparam);
750                        break;
751                case 'H'.charCodeAt(): // it's a 'move cursor' sequence
752                case 'f'.charCodeAt():
753                        interpretCSI_CUP(csiparam);
754                        break;
755                case 'A'.charCodeAt(): // it is a 'relative move'
756                case 'B'.charCodeAt():
757                case 'C'.charCodeAt():
758                case 'D'.charCodeAt():
759                case 'E'.charCodeAt():
760                case 'F'.charCodeAt():
761                case 'G'.charCodeAt():
762                case 'e'.charCodeAt():
763                case 'a'.charCodeAt():
764                case 'd'.charCodeAt():
765                case '`'.charCodeAt():
766                        interpretCSI_C(verb, csiparam);
767                        break;
768                case 'K'.charCodeAt(): /* erase line */
769                        interpretCSI_EL(csiparam);
770                        break;
771                case '@'.charCodeAt(): /* insert characters */
772                        interpretCSI_ICH(csiparam);
773                        break;
774                case 'P'.charCodeAt(): /* delete characters */
775                        interpretCSI_DCH(csiparam);
776                        break;
777                case 'L'.charCodeAt(): /* insert lines */
778                        interpretCSI_IL(csiparam);
779                        break;
780                case 'M'.charCodeAt(): /* delete lines */
781                        interpretCSI_DL(csiparam);
782                        break;
783                case 'X'.charCodeAt(): /* erase characters */
784                        interpretCSI_ECH(csiparam);
785                        break;
786                case 'r'.charCodeAt(): /* set scrolling region */
787                        interpretCSI_DECSTBM(csiparam);
788                        break;
789                case 's'.charCodeAt(): /* save cursor location */
790                        interpretCSI_SAVECUR(csiparam);
791                        break;
792                case 'u'.charCodeAt(): /* restore cursor location */
793                        interpretCSI_RESTORECUR(csiparam);
794                        break;
795                default:
796                        trace("Unrecognized CSI");
797                }
798        }
799
800        // interprets a 'set attribute' (SGR) CSI escape sequence
801        private function interpretCSI_SGR(param:Array):void
802        {
803                if(param.length == 0) {
804                        // special case: reset attributes
805                        curattr = attrReset();
806                        return;
807                }
808
809                // From http://vt100.net/docs/vt510-rm/SGR table 5-16
810                // 0    All attributes off
811                // 1    Bold
812                // 4    Underline
813                // 5    Blinking
814                // 7    Negative image
815                // 8    Invisible image
816                // 10   The ASCII character set is the current 7-bit
817                //      display character set (default) - SCO Console only.
818                // 11   Map Hex 00-7F of the PC character set codes
819                //      to the current 7-bit display character set
820                //      - SCO Console only.
821                // 12   Map Hex 80-FF of the current character set to
822                //      the current 7-bit display character set - SCO
823                //      Console only.
824                // 22   Bold off
825                // 24   Underline off
826                // 25   Blinking off
827                // 27   Negative image off
828                // 28   Invisible image off
829       
830                var len:uint = param.length;
831                for each(var p:uint in param) {
832                        if(p == 0) {  // reset
833                                curattr = attrReset();
834                        } else if( p == 1 || p == 2 || p == 4 ) {  // set bold
835                                curattr = attrModifyBold(curattr, true);
836                        } else if( p == 5 ) {  // set blink
837                                curattr = attrModifyBlink(curattr, true);
838                        } else if( p == 7 || p == 27 ) {  // reverse video
839                                var bg:uint = attrBackground(curattr);
840                                var fg:uint = attrForeground(curattr);
841                                curattr = attrModifyBackground(curattr, fg);
842                                curattr = attrModifyForeground(curattr, bg);
843                        } else if( p == 8 ) {  // invisible
844                                curattr = 0x0;  // FIXME
845                        } else if( p == 22 || p == 24 ) {  // bold off
846                                curattr = attrModifyBold(curattr, false);
847                        } else if( p == 25 ) {  // blink off
848                                curattr = attrModifyBlink(curattr, false);
849                        } else if( p == 28 ) {  // invisible off
850                                curattr = attrReset();
851                        } else if( p >= 30 && p <= 37 ) {  // set fg
852                                curattr = attrModifyForeground(curattr, p - 30);
853                        } else if( p == 38 ) {
854                                // TODO 256 background color
855                        } else if( p >= 40 && p <= 47 ) {  // set bg
856                                curattr = attrModifyBackground(curattr, p - 40);
857                        } else if( p == 38 ) {
858                                // TODO 256 background color
859                        } else if( p == 39 ) {  // reset foreground to default
860                                curattr = attrModifyForeground(curattr, 7);
861                        } else if( p == 49 ) {  // reset backspace to default
862                                curattr = attrModifyBackground(curattr, 0);
863                        }
864                }
865        }
866
867        // interprets an 'erase display' (ED) escape sequence
868        private function interpretCSI_ED(param:Array):void
869        {
870                var r:uint;
871                var c:uint;
872                if( param.length > 0 && param[0] == 2 ) {
873                        // clear whole screen
874                        for(r = 0; r < rows; ++r) {
875                                lineDirty[r] = true;
876                                clearScreenRow(screen[r], curattr);
877                        }
878                } else if( param.length > 0 && param[0] == 1 ) {
879                        // clear from origin to current cursor position
880                        for(r = 0; r < crow; ++r) {
881                                lineDirty[r] = true;
882                                clearScreenRow(screen[r], curattr);
883                        }
884                        lineDirty[crow] = true;
885                        for(c = 0; c <= ccol; ++c) {
886                                screen[crow][c].clear(curattr);
887                        }
888                } else {
889                        // clear from current cursor position to the end
890                        lineDirty[crow] = true;
891                        for(c = 0; c <= ccol; ++c) {
892                                screen[crow][c].clear(curattr);
893                        }
894                        for(r = crow + 1; r < rows; ++r) {
895                                lineDirty[r] = true;
896                                clearScreenRow(screen[r], curattr);
897                        }
898                }
899        }
900
901        // interprets a 'move cursor' (CUP) escape sequence
902        private function interpretCSI_CUP(param:Array):void
903        {
904                if( param.length == 0 ) {
905                        // special case
906                        crow = ccol = 0;
907                        cursorDirty = true;
908                        return;
909                } else if( param.length < 2 ) {  // malformed
910                        return;
911                }
912       
913                crow = param[0] - 1;  // convert from 1-based to 0-based
914                ccol = param[1] - 1;  // convert from 1-based to 0-based
915       
916                if( crow < 0 ) { crow = 0; }
917                if( ccol < 0 ) { ccol = 0; }
918                if( crow >= rows ) { crow = rows - 1; }
919                if( ccol >= cols ) { ccol = cols - 1; }
920
921                cursorDirty = true;
922        }
923       
924        // Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
925        // CPL, CHA, HPR, VPA, VPR, HPA
926        private function interpretCSI_C(verb:uint, param:Array):void
927        {
928                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
929                switch(verb) {
930                case 'A'.charCodeAt():
931                        crow -= n;
932                        if( crow < 0 ) { crow = 0; }
933                        break;
934                case 'B'.charCodeAt():
935                case 'e'.charCodeAt():
936                        crow += n;
937                        if( crow >= rows ) { crow = rows - 1; }
938                        break;
939                case 'C'.charCodeAt():
940                case 'a'.charCodeAt():
941                        ccol += n;
942                        if( ccol >= cols ) { ccol = cols - 1; }
943                        break;
944                case 'D'.charCodeAt():
945                        ccol -= n;
946                        if( ccol < 0 ) { ccol = 0; }
947                        break;
948                case 'E'.charCodeAt():
949                        crow += n;
950                        ccol = 0;
951                        if( crow >= rows ) { crow = rows - 1; }
952                        break;
953                case 'F'.charCodeAt():
954                        crow -= n;
955                        ccol = 0;
956                        if( crow < 0 ) { crow = 0; }
957                        break;
958                case 'G'.charCodeAt():
959                case '`'.charCodeAt():
960                        ccol = param[0] - 1;
961                        if( ccol < 0 ) { ccol = 0; }
962                        if( ccol >= cols ) { ccol = cols - 1; }
963                        break;
964                case 'd'.charCodeAt():
965                        crow = param[0] - 1;
966                        if( crow < 0 ) { crow = 0; }
967                        if( crow >= rows ) { crow = rows - 1; }
968                        break;
969                }
970       
971                cursorDirty = true;
972        }
973
974        // Interpret the 'erase line' escape sequence
975        private function interpretCSI_EL(param:Array):void
976        {
977                var cmd:int = param.length > 0 ? param[0] : 0;
978                var c:int;
979                switch(cmd) {
980                case 1:
981                        for(c = 0; c <= ccol; ++c) {
982                                screen[crow][c].clear(curattr);
983                        }
984                        break;
985                case 2:
986                        clearScreenRow(screen[crow], curattr);
987                        break;
988                default:
989                        for(c = ccol; c < cols; ++c) {
990                                screen[crow][c].clear(curattr);
991                        }
992                        break;
993                }
994                lineDirty[crow] = true;
995        }
996       
997        // Interpret the 'insert blanks' sequence (ICH)
998        private function interpretCSI_ICH(param:Array):void
999        {
1000                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
1001                var c:int;
1002
1003                var poped:Array = screen[crow].splice(ccol + n);
1004                var range:Array = screen[crow].splice(ccol);
1005                for each(var cell:TextCell in poped) { cell.clear(); }
1006                screen[crow] = screen[crow].concat(poped);
1007                screen[crow] = screen[crow].concat(range);
1008
1009                lineDirty[crow] = true;
1010        }
1011       
1012        // Interpret the 'delete chars' sequence (DCH)
1013        private function interpretCSI_DCH(param:Array):void
1014        {
1015                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
1016                var range:Array = screen[crow].splice(cols - n);
1017                var poped:Array = screen[crow].splice(ccol);
1018                for each(var cell:TextCell in poped) { cell.clear(); }
1019                screen[crow] = screen[crow].concat(poped);
1020                screen[crow] = screen[crow].concat(range);
1021
1022                var c:int;
1023                for(c = ccol; c < cols - n; ++c) {
1024                        screen[crow][c].copy( screen[crow][c + n] );
1025                }
1026                for(c = cols - n; c < cols; ++c) {
1027                        screen[crow][c].clear(curattr);
1028                }
1029                lineDirty[crow] = true;
1030        }
1031       
1032        // Interpret an 'insert line' sequence (IL)
1033        private function interpretCSI_IL(param:Array):void
1034        {
1035                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
1036       
1037                var line:Array;
1038                // splice back of scroll bottom
1039                var outpos:Array = screen.splice(scrollbottom + 1);
1040                // pop n lines
1041                var poped:Array = screen.splice(scrollbottom - n + 1);
1042                // splice crow and back of crow
1043                var post:Array = screen.splice(crow);
1044                // insert n lines
1045                for each(line in poped) {  // clear the lines
1046                        for each(var cell:TextCell in line) {
1047                                cell.clear(curattr);
1048                        }
1049                }
1050                for each(line in poped) { screen.push(line); }
1051                // concat back of crow
1052                screen = screen.concat(post); // FIXME which is faster?
1053                //for each(line in post) { screen.push(line); }
1054                // concat back of scroll bottom
1055                //screen = screen.concat(outpos); // FIXME which is faster?
1056                for each(line in outpos) { screen.push(line); }
1057       
1058                for(var r:int = crow; r <= scrollbottom; ++r) {
1059                        lineDirty[r] = true;
1060                }
1061        }
1062       
1063        // Interpret a 'delete line' sequence (DL)
1064        private function interpretCSI_DL(param:Array):void
1065        {
1066                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
1067       
1068                var line:Array;
1069                // splice back of scroll bottom
1070                var outpos:Array = screen.splice(scrollbottom + 1);
1071                // splice back of n lines
1072                var region:Array = screen.splice(crow + n);
1073                // pop n lines
1074                var poped:Array = screen.splice(crow);
1075                // concat back of n lines
1076                screen = screen.concat(region); // FIXME which is faster?
1077                //for each(line in region) { screen.push(line); }
1078                // insert n empty lines
1079                for each(line in poped) {  // clear the lines
1080                        for each(var c:TextCell in line) {
1081                                c.clear(curattr);
1082                        }
1083                }
1084                screen = screen.concat(poped); // FIXME which is faster?
1085                //for each(line in poped) { screen.push(line); }
1086                // concat back of scroll bottom
1087                //screen = screen.concat(outpos); // FIXME which is faster?
1088                for each(line in outpos) { screen.push(line); }
1089       
1090                for(var r:int = crow; r <= scrollbottom; ++r) {
1091                        lineDirty[r] = true;
1092                }
1093        }
1094       
1095        // Interpret an 'erase characters' (ECH) sequence
1096        private function interpretCSI_ECH(param:Array):void
1097        {
1098                var n:int = (param.length > 0 && param[0] > 0) ? param[0] : 1;
1099                for(var c:int = ccol; c < ccol + n && c < cols; ++c) {
1100                        screen[crow][c].clear(curattr);
1101                }
1102                lineDirty[crow] = true;
1103        }
1104       
1105        // Interpret a 'set scrolling region' (DECSTBM) sequence
1106        private function interpretCSI_DECSTBM(param:Array):void
1107        {
1108                var newtop:int;
1109                var newbottom:int;
1110       
1111                if( param.length == 0 ) {
1112                        newtop = 0;
1113                        newbottom = rows - 1;
1114                } else if( param.length < 2 ) {  // malformed
1115                        return;
1116                } else {
1117                        newtop = param[0] - 1;
1118                        newbottom = param[1] - 1;
1119                }
1120       
1121                // clamp to bounds
1122                if (newtop < 0) { newtop = 0; }
1123                if (newtop >= rows) { newtop = rows - 1; }
1124                if (newbottom < 0) { newbottom = 0; }
1125                if (newbottom >= rows) { newbottom = rows - 1; }
1126       
1127                // check for range validity
1128                if( newtop > newbottom ) { return; }
1129                scrolltop = newtop;
1130                scrollbottom = newbottom;
1131        }
1132       
1133        private function interpretCSI_SAVECUR(param:Array):void
1134        {
1135                saved_col = ccol;
1136                saved_row = crow;
1137        }
1138       
1139        private function interpretCSI_RESTORECUR(param:Array):void
1140        {
1141                ccol = saved_col;
1142                crow = saved_row;
1143                cursorDirty = true;
1144        }
1145
1146
1147        // FIXME  how ad-hoc!
1148        // FIXME  this method doesn't work when font size is very small
1149        private function isWideCharacter(c:String):Boolean
1150        {
1151                characterWidthMeasure.width = 0;
1152                characterWidthMeasure.text = c + "m";
1153                var cWidth:Number = characterWidthMeasure.width / 2 - 2;
1154                if( cWidth >= calibrateFontWidth-1 && cWidth <= calibrateFontWidth+1 ) {
1155                        return false;
1156                } else {
1157                        return true;
1158                }
1159        }
1160
1161
1162        private static function clearScreenRow(row:Array, fillattr:uint = 0):void
1163        {
1164                for each(var r:TextCell in row) {
1165                        r.clear(fillattr);
1166                }
1167        }
1168       
1169        private static function attrModifyBackground(attr:uint, newbg:uint):uint
1170        {
1171                attr &= 0xF8;
1172                attr |= newbg;
1173                return attr;
1174        }
1175        private static function attrModifyForeground(attr:uint, newfg:uint):uint
1176        {
1177                attr &= 0x8F;
1178                attr |= (newfg << 4);
1179                return attr;
1180        }
1181        private static function attrModifyBold(attr:uint, enable:Boolean):uint
1182        {
1183                attr &= 0x7F;
1184                if(enable) { attr |= 0x80; }
1185                return attr;
1186        }
1187        private static function attrModifyBlink(attr:uint, enable:Boolean):uint
1188        {
1189                attr &= 0xF7;
1190                if(enable) { attr |= 0x08; }
1191                return attr;
1192        }
1193        private static function attrReset():uint
1194        {
1195                return 0x70;
1196        }
1197        private static function attrBackground(attr:uint):uint
1198        {
1199                return (attr & 0x07);
1200        }
1201        private static function attrForeground(attr:uint):uint
1202        {
1203                return ((attr & 0x70) >> 4);
1204        }
1205        private static function attrIsBold(attr:uint):Boolean
1206        {
1207                return (attr & 0x80) != 0;
1208        }
1209        private static function attrIsBlink(attr:uint):Boolean
1210        {
1211                return (attr & 0x08) != 0;
1212        }
1213
1214
1215
1216        /**
1217         * IDataOutput interface
1218         */
1219        public function set objectEncoding(value:uint):void
1220        {
1221                _middleBuffer.objectEncoding = value;
1222        }
1223        public function get objectEncoding():uint
1224        {
1225                return _middleBuffer.objectEncoding;
1226        }
1227
1228        public function set endian(value:String):void
1229        {
1230                _middleBuffer.endian = value;
1231        }
1232        public function get endian():String
1233        {
1234                return _middleBuffer.endian;
1235        }
1236
1237        public function writeBoolean(value:Boolean):void
1238        {
1239                var buffer:ByteArray = _middleBuffer;
1240                buffer.writeBoolean(value);
1241                writeBytes(buffer);
1242                buffer.length = 0;
1243        }
1244
1245        public function writeByte(value:int):void
1246        {
1247                var buffer:ByteArray = _middleBuffer;
1248                buffer.writeByte(value);
1249                writeBytes(buffer);
1250                buffer.length = 0;
1251        }
1252
1253        public function writeDouble(value:Number):void
1254        {
1255                var buffer:ByteArray = _middleBuffer;
1256                buffer.writeDouble(value);
1257                writeBytes(buffer);
1258                buffer.length = 0;
1259        }
1260
1261        public function writeFloat(value:Number):void
1262        {
1263                var buffer:ByteArray = _middleBuffer;
1264                buffer.writeFloat(value);
1265                writeBytes(buffer);
1266                buffer.length = 0;
1267        }
1268
1269        public function writeInt(value:int):void
1270        {
1271                var buffer:ByteArray = _middleBuffer;
1272                buffer.writeInt(value);
1273                writeBytes(buffer);
1274                buffer.length = 0;
1275        }
1276
1277        public function writeMultiByte(value:String, charSet:String):void
1278        {
1279                var buffer:ByteArray = _middleBuffer;
1280                buffer.writeMultiByte(value, charSet);
1281                writeBytes(buffer);
1282                buffer.length = 0;
1283        }
1284
1285        public function writeObject(object:*):void
1286        {
1287                var buffer:ByteArray = _middleBuffer;
1288                buffer.writeObject(object);
1289                writeBytes(buffer);
1290                buffer.length = 0;
1291        }
1292
1293        public function writeShort(value:int):void
1294        {
1295                var buffer:ByteArray = _middleBuffer;
1296                buffer.writeShort(value);
1297                writeBytes(buffer);
1298                buffer.length = 0;
1299        }
1300
1301        public function writeUnsignedInt(value:uint):void
1302        {
1303                var buffer:ByteArray = _middleBuffer;
1304                buffer.writeUnsignedInt(value);
1305                writeBytes(buffer);
1306                buffer.length = 0;
1307        }
1308
1309        public function writeUTF(value:String):void
1310        {
1311                var buffer:ByteArray = _middleBuffer;
1312                buffer.writeUTF(value);
1313                writeBytes(buffer);
1314                buffer.length = 0;
1315        }
1316
1317        public function writeUTFBytes(value:String):void
1318        {
1319                var buffer:ByteArray = _middleBuffer;
1320                buffer.writeUTFBytes(value);
1321                writeBytes(buffer);
1322                buffer.length = 0;
1323        }
1324
1325}
1326
1327
1328}  // package
1329
Note: See TracBrowser for help on using the browser.