123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291#1 "./ansi_unix.ml"(* File: Ansi_unix.ml
Allow colors, cursor movements, erasing,... under Unix shells.
*********************************************************************
Copyright 2004 by Troestler Christophe Christophe.Troestler(at)umons.ac.be
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License version 3 as published by
the Free Software Foundation, with the special exception on linking described
in file LICENSE.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the file LICENSE for more details. *)(* man tty(4) *)openPrintfopenScanfincludeAnsi_commonletisatty=refUnix.isattyletis_out_channel_attych=!isatty(Unix.descr_of_out_channelch)(* Cursor *)let set_cursorxy=ifis_out_channel_attystdoutthenifx<=0then (ify>0thenprintf"\027[%id%!"y)elseif(* x > 0 *)y<=0thenprintf"\027[%iG%!"xelseprintf"\027[%i;%iH%!"yxletmove_cursorxy=ifis_out_channel_attystdoutthen(ifx>0thenprintf"\027[%iC%!"xelseifx<0thenprintf"\027[%iD%!"(-x);ify>0thenprintf"\027[%iB%!"yelseify<0thenprintf"\027[%iA%!"(-y))letsave_cursor()=ifis_out_channel_attystdoutthenprintf"\027[s%!"letrestore_cursor()=ifis_out_channel_attystdoutthenprintf"\027[u%!"letshow_cursor()=ifis_out_channel_attystdoutthenprintf"\027[?25h"lethide_cursor()=ifis_out_channel_attystdoutthenprintf"\027[?25l"letmove_bol()=print_string"\r";flushstdout(* Inpired by http://www.ohse.de/uwe/software/resize.c.html and
http://qemacs.sourcearchive.com/documentation/0.3.1.cvs.20050713-5/tty_8c-source.html *)letsend_and_read_responsefdinqueryfmtf=letalarm=reffalseinletset_alarm(_:int)=alarm:=trueinletold_alarm=Sys.signalSys.sigalrm(Sys.Signal_handleset_alarm)inlettty=Unix.tcgetattrfdininUnix.tcsetattrfdinUnix.TCSANOW{ttywithUnix.c_ignbrk=false;c_brkint=false;c_parmrk=false;c_istrip=false;c_inlcr =false;c_igncr=false;c_icrnl=false;c_ixon=false;c_opost=true;c_csize=8;c_parenb=false;c_icanon=false;c_isig=false;c_echo=false;c_echonl=false;c_vmin=1;c_vtime=0};letrestore()=ignore(Unix.alarm0);Unix.tcsetattrfdinUnix.TCSANOWtty;Sys.set_signalSys.sigalrmold_alarminletbuf=Bytes.make127'\000'in(* FIXME: make it more robust so that it ignores previous key pressed. *)letrecget_answerpos=letl=Unix.readfdinbufpos1inletbuf=Bytes.unsafe_to_stringbufin(* local use only *)trysscanfbuffmtf(* bail out as soon as enough info is present *)with|Scan_failure_->if!alarm||pos=126thenfailwith"Ansi.input_answer"elseifbuf.[pos]='\000'thenget_answerposelseget_answer(pos+l)intryignore(Unix.writefdinquery0(Bytes.lengthquery));ignore(Unix.alarm1);letr=get_answer0inrestore();rwith|e->restore();raisee(* Query Cursor Position <ESC>[6n *)(* Report Cursor Position <ESC>[{ROW};{COLUMN}R *)letpos_cursor_query=Bytes.of_string"\027[6n"letpos_cursor()=ifis_out_channel_attystdoutthentrysend_and_read_responseUnix.stdinpos_cursor_query"\027[%d;%dR"(funyx->x,y)with|_->failwith"Ansi.pos_cursor"elsefailwith"Ansi.pos_cursor: not a TTY"(* See also the output of 'resize -s x y' (e.g. in an Emacs shell). *)letresizewidthheight=ifis_out_channel_attystdoutthen(ifwidth<=0theninvalid_arg"Ansi.resize: width <= 0";ifheight<=0theninvalid_arg"Ansi.resize: height <= 0";printf"\027[8;%i;%it%!"heightwidth)(* FIXME: what about the following recipe: If you run echo -e "\e[18t" then
xterm will respond with a line of the form ESC [ 8 ; height ; width t It
generates this line as if it were typed input, so it can then be read by your
program on stdin. *)externalsize_:Unix.file_descr->int*int="Ansi_term_size"letsize()=if!isattyUnix.stdinthensize_Unix.stdinelsefailwith"Ansi.size: not a TTY"(* Erasing *)leteraseloc=ifis_out_channel_attystdoutthen(print_string(matchlocwith|Eol->"\027[K"|Above->"\027[1J"|Below->"\027[0J"|Screen->"\027[2J");flushstdout)(* Scrolling *)letscrolllines=ifis_out_channel_attystdouttheniflines>0thenprintf"\027[%iS%!"lineselseiflines<0thenprintf"\027[%iT%!"(-lines)letstyle_to_string=function|Reset->"0"|Bold->"1"|Underlined->"4"|Blink->"5"|Inverse->"7"|Hidden->"8"|ForegroundBlack->"30"|ForegroundRed->"31"|ForegroundGreen->"32"|ForegroundYellow->"33"|ForegroundBlue->"34"|ForegroundMagenta->"35"|ForegroundCyan->"36"|ForegroundWhite->"37"|ForegroundBright_black->"30;1"|ForegroundBright_red->"31;1"|ForegroundBright_green->"32;1"|ForegroundBright_yellow->"33;1"|ForegroundBright_blue->"34;1"|ForegroundBright_magenta->"35;1"|ForegroundBright_cyan->"36;1"|ForegroundBright_white->"37;1"|ForegroundDefault->"39"|BackgroundBlack->"40"|BackgroundRed->"41"|BackgroundGreen->"42"|BackgroundYellow->"43"|BackgroundBlue->"44"|BackgroundMagenta->"45"|BackgroundCyan->"46"|BackgroundWhite->"47"|BackgroundBright_black->"40;1"|BackgroundBright_red->"41;1"|BackgroundBright_green->"42;1"|BackgroundBright_yellow->"43;1"|BackgroundBright_blue->"44;1"|BackgroundBright_magenta->"45;1"|BackgroundBright_cyan->"46;1"|BackgroundBright_white->"47;1"|BackgroundDefault->"49"letprint_withpr~ttystyletxt=ifttythen(pr"\027[";pr(String.concat";"(List.mapstyle_to_stringstyle));pr"m");prtxt;iftty&&!autoresetthenpr"\027[0m"letprint_stringstyletxt=print_withprint_stringstyletxt~tty:(is_out_channel_attystdout)letprerr_stringstyletxt=print_withprerr_stringstyletxt~tty:(is_out_channel_attystderr)letprintfstyle=ksprintf(print_stringstyle)leteprintfstyle=ksprintf(prerr_stringstyle)letto_stringstyletxt=lets="\027["^String.concat";"(List.mapstyle_to_stringstyle)^"m"^txtinif!autoresetthens^"\027[0m"elsesletsprintfstyle=ksprintf(to_stringstyle)