123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207#1 "./ANSITerminal_unix.ml"(* File: ANSITerminal_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.
*)(** Seethe file(s) ctlseqs.* (unix; in Debian package xspecs)
CSI = "\027[" (ESC [)
man console_codes
*)(* man tty(4) *)openPrintfopenScanfincludeANSITerminal_commonletisatty=refUnix.isattyletis_out_channel_attych=!isatty(Unix.descr_of_out_channelch)(* Cursor *)letset_cursorxy=ifis_out_channel_attystdoutthen(ifx<=0then(ify>0thenprintf"\027[%id%!"y)else(* x > 0 *)ify<=0thenprintf"\027[%iG%!"xelseprintf"\027[%i;%iH%!"yx)letmove_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%!"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 *)withScan_failure_->if!alarm||pos=126thenfailwith"ANSITerminal.input_answer"elseifbuf.[pos]='\000'thenget_answerposelseget_answer(pos+l)intryignore(Unix.writefdinquery0(Bytes.lengthquery));ignore(Unix.alarm1);letr=get_answer0inrestore();rwithe->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_attystdoutthen(trysend_and_read_responseUnix.stdinpos_cursor_query"\027[%d;%dR"(funyx->(x,y))with_->failwith"ANSITerminal.pos_cursor")elsefailwith"ANSITerminal.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"ANSITerminal.resize: width <= 0";ifheight<=0theninvalid_arg"ANSITerminal.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="ANSITerminal_term_size"letsize()=if!isattyUnix.stdinthen(size_Unix.stdin)elsefailwith"ANSITerminal.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_attystdoutthen(iflines>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"|ForegroundDefault->"39"|BackgroundBlack->"40"|BackgroundRed->"41"|BackgroundGreen->"42"|BackgroundYellow->"43"|BackgroundBlue->"44"|BackgroundMagenta->"45"|BackgroundCyan->"46"|BackgroundWhite->"47"|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)