ch = $ch; $this->attr = $attr; } public function __get(string $key): mixed { switch ($key) { case 'attr': return $this->attr; case 'ch': return $this->ch; default: throw new \Exception('Unknown key:'.$key); } } public function __isset($key): bool { return isset($this->{$key}); } public function __set(string $key,mixed $value): void { switch ($key) { case 'ch': if (strlen($value) !== 1) throw new \Exception(sprintf('CH can only be 1 char: [%s]',$value)); $this->{$key} = $value; break; default: throw new \Exception('Unknown key:'.$key); } } public function __toString() { return sprintf('%04x [%s]|',$this->attr,$this->ch); } /** * Return the color codes required to draw the current character * * @param Mode $mo Service we are rendering for * @param int|null $last last rendered char * @param bool $debug debug mode * @return string|NULL * @throws \Exception */ public function attr(Mode $mo,int $last=NULL,bool $debug=FALSE): string|NULL { $ansi = collect(); if ($debug) dump('- last:'.$last.', this:'.$this->attr); switch ($mo->name) { case 'ansi': if ($debug) { dump(' - this BG_BLACK:'.($this->attr & Ansi::BG_BLACK)); dump(' - last BG_BLACK:'.($last & Ansi::BG_BLACK)); dump(' - this HIGH:'.($this->attr & Ansi::HIGH)); dump(' - last HIGH:'.($last & Ansi::HIGH)); dump(' - this BLINK:'.($this->attr & Ansi::BLINK)); dump(' - last BLINK:'.($last & Ansi::BLINK)); } // If high was in the last, and we dont have high now, we need 0, but we need to turn back on flash if it was there // If flash was in the last, and we dont have flash now, we need to 0 but we need to turn on high if it was there $reset = FALSE; if ((($this->attr & Ansi::BG_BLACK) && (! ($last & Ansi::BG_BLACK))) || ((! ($this->attr & Ansi::BLINK)) && ($last & Ansi::BLINK)) || ((! ($this->attr & Ansi::HIGH)) && ($last & Ansi::HIGH))) { $ansi->push(Ansi::I_CLEAR_CODE); $reset = TRUE; $last = Ansi::BG_BLACK|Ansi::LIGHTGRAY; } if (($this->attr & Ansi::HIGH) && ((($this->attr & Ansi::HIGH) !== ($last & Ansi::HIGH)) || ($reset && ($last & Ansi::HIGH)))) { $ansi->push(Ansi::I_HIGH_CODE); } if (($this->attr & Ansi::BLINK) && ((($this->attr & Ansi::BLINK) !== ($last & Ansi::BLINK)) || ($reset && ($last & Ansi::BLINK)))) { $ansi->push(Ansi::I_BLINK_CODE); } $c = ($this->attr & 0x07); $l = ($last & 0x07); // Foreground switch ($c) { case Ansi::BLACK: $r = Ansi::FG_BLACK_CODE; break; case Ansi::RED: $r = Ansi::FG_RED_CODE; break; case Ansi::GREEN: $r = Ansi::FG_GREEN_CODE; break; case Ansi::BROWN: $r = Ansi::FG_BROWN_CODE; break; case Ansi::BLUE: $r = Ansi::FG_BLUE_CODE; break; case Ansi::MAGENTA: $r = Ansi::FG_MAGENTA_CODE; break; case Ansi::CYAN: $r = Ansi::FG_CYAN_CODE; break; case Ansi::LIGHTGRAY: $r = Ansi::FG_LIGHTGRAY_CODE; break; } if ($r && ($c !== $l)) $ansi->push($r); // Background if ($this->attr & 0x70) { $c = ($this->attr & 0x70); $l = ($last & 0x70); switch ($this->attr & 0x70) { case Ansi::BG_BLACK: $r = Ansi::BG_BLACK_CODE; break; case Ansi::BG_RED: $r = Ansi::BG_RED_CODE; break; case Ansi::BG_GREEN: $r = Ansi::BG_GREEN_CODE; break; case Ansi::BG_BROWN: $r = Ansi::BG_BROWN_CODE; break; case Ansi::BG_BLUE: $r = Ansi::BG_BLUE_CODE; break; case Ansi::BG_MAGENTA: $r = Ansi::BG_MAGENTA_CODE; break; case Ansi::BG_CYAN: $r = Ansi::BG_CYAN_CODE; break; case Ansi::BG_LIGHTGRAY: $r = Ansi::BG_LIGHTGRAY_CODE; break; } if ($r && ($c !== $l)) $ansi->push($r); } if ($debug) dump([' - ansi:' =>$ansi]); return $ansi->count() ? sprintf('%s[%sm',($debug ? '': "\x1b"),$ansi->join(';')) : NULL; case 'viewdata': if ($debug) dump(sprintf('Last: %02x, Attr: %02x',$last,$this->attr)); switch ($this->attr) { // \x08 case Viewdata::BLINK: $r = Viewdata::I_BLINK_CODE; break; // \x09 case Viewdata::STEADY: $r = Viewdata::I_STEADY; break; // \x0c case Viewdata::NORMAL: $r = Viewdata::I_NORMAL; break; // \x0d case Viewdata::DOUBLE: $r = Viewdata::I_DOUBLE_CODE; break; // \x18 case Viewdata::CONCEAL: $r = Viewdata::I_CONCEAL; break; // \x19 case Viewdata::BLOCKS: $r = Viewdata::I_BLOCKS; break; // \x1a case Viewdata::SEPARATED: $r = Viewdata::I_SEPARATED; break; // \x1c case Viewdata::BLACKBACK: $r = Viewdata::I_BLACKBACK; break; // \x1d case Viewdata::NEWBACK: $r = Viewdata::I_NEWBACK; break; // \x1e case Viewdata::HOLD: $r = Viewdata::I_HOLD; break; // \x1f case Viewdata::RELEASE: $r = Viewdata::I_REVEAL; break; // Not handled // \x0a-b,\x0e-f,\x1b case 0xff00: dump($this->attr); break; default: $mosiac = ($this->attr & Viewdata::MOSIAC); $c = ($this->attr & 0x07); if ($debug) dump(sprintf('Last: %02x, Attr: %02x, Color: %02x',$last,$this->attr,$c)); // Color control \x00-\x07, \x10-\x17 switch ($c) { /* case Viewdata::BLACK: $r = Viewdata::FG_BLACK_CODE; break; */ case Viewdata::RED: $r = $mosiac ? Viewdata::MOSIAC_RED_CODE : Viewdata::FG_RED_CODE; break; case Viewdata::GREEN: $r = $mosiac ? Viewdata::MOSIAC_GREEN_CODE : Viewdata::FG_GREEN_CODE; break; case Viewdata::YELLOW: $r = $mosiac ? Viewdata::MOSIAC_YELLOW_CODE : Viewdata::FG_YELLOW_CODE; break; case Viewdata::BLUE: $r = $mosiac ? Viewdata::MOSIAC_BLUE_CODE : Viewdata::FG_BLUE_CODE; break; case Viewdata::MAGENTA: $r = $mosiac ? Viewdata::MOSIAC_MAGENTA_CODE : Viewdata::FG_MAGENTA_CODE; break; case Viewdata::CYAN: $r = $mosiac ? Viewdata::MOSIAC_CYAN_CODE : Viewdata::FG_CYAN_CODE; break; case Viewdata::WHITE: $r = $mosiac ? Viewdata::MOSIAC_WHITE_CODE : Viewdata::FG_WHITE_CODE; break; default: if ($debug) dump('Not a color?:'.$c); return NULL; } } if ($debug) dump(sprintf('= result: ESC[%s](%02x) for [%s]',chr($r),$r,$this->ch)); return chr($r); default: throw new \Exception($this->type.': has not been implemented'); } } }