Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file lTerm.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350(*
* lTerm.ml
* --------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of Lambda-Term.
*)openLwt_reactopenLTerm_geomletreturn,(>>=)=Lwt.return,Lwt.(>>=)letuspace=Uchar.of_char' 'letyspace=Zed_char.unsafe_of_uCharuspace(* +-----------------------------------------------------------------+
| TTYs sizes |
+-----------------------------------------------------------------+ *)externalget_size_from_fd:Unix.file_descr->size="lt_term_get_size_from_fd"externalset_size_from_fd:Unix.file_descr->size->unit="lt_term_set_size_from_fd"letget_size_from_fdfd=Lwt_unix.check_descriptorfd;get_size_from_fd(Lwt_unix.unix_file_descrfd)letset_size_from_fdfdsize=Lwt_unix.check_descriptorfd;set_size_from_fd(Lwt_unix.unix_file_descrfd)size(* +-----------------------------------------------------------------+
| The terminal type |
+-----------------------------------------------------------------+ *)exceptionNot_a_ttylet()=Printexc.register_printer(function|Not_a_tty->Some"terminal is not a tty"|_->None)moduleInt_map=Map.Make(structtypet=intletcompareab=a-bend)typet={model:string;colors:int;windows:bool;bold_is_bright:bool;color_map:LTerm_color_mappings.map;(* Informations. *)mutableraw_mode:bool;(* Whether the terminal is currently in raw mode. *)mutableincoming_fd:Lwt_unix.file_descr;mutableoutgoing_fd:Lwt_unix.file_descr;(* File descriptors. *)mutableic:Lwt_io.input_channel;mutableoc:Lwt_io.output_channel;(* Channels. *)mutableinput_stream:charLwt_stream.t;(* Stream of characters read from the terminal. *)mutablenext_event:LTerm_event.tLwt.toption;(* Thread reading the next event from the terminal. We cannot cancel
the reading of an event, so we keep the last thread to reuse it
in case the user cancels [read_event]. *)mutableread_event:bool;(* Whether a thread is currently reading an event. *)mutablelast_reported_size:size;(* The last size reported by [read_event]. *)mutablesize:size;(* The current size of the terminal. *)(* Characters encodings. *)outgoing_is_utf8:bool;(* Whether the outgoing encoding is UTF-8. *)notify:LTerm_event.tLwt_condition.t;(* Condition used to send a spontaneous event. *)mutableevent:unitevent;(* Event which handles SIGWINCH. *)mutableincoming_is_a_tty:bool;mutableoutgoing_is_a_tty:bool;(* Whether input/output are tty devices. *)mutableescape_time:float;(* Time to wait before returning the escape key. *)}(* +-----------------------------------------------------------------+
| Signals |
+-----------------------------------------------------------------+ *)letresize_event,send_resize=E.create()letsend_resize()=send_resize()let()=matchLTerm_unix.sigwinchwith|None->(* Check for size when something happen. *)ignore(LTerm_dlist.add_lsend_resize(LTerm_dlist.create()))|Somesignum->tryignore(Lwt_unix.on_signalsignum(fun_->send_resize()))withNot_found->ignore(LTerm_dlist.add_lsend_resize(LTerm_dlist.create()))(* +-----------------------------------------------------------------+
| Creation |
+-----------------------------------------------------------------+ *)letdefault_model=trySys.getenv"TERM"withNot_found->"dumb"letcolors_of_term=function|"Eterm-256color"->256|"Eterm-88color"->88|"gnome-256color"->256|"iTerm.app"->256|"konsole-256color"->256|"mlterm-256color"->256|"mrxvt-256color"->256|"putty-256color"->256|"rxvt-256color"->256|"rxvt-88color"->88|"rxvt-unicode-256color"->256|"rxvt-unicode"->88|"screen-256color"->256|"screen-256color-bce"->256|"screen-256color-bce-s"->256|"screen-256color-s"->256|"st-256color"->256|"vte-256color"->256|"xterm-256color"->256|"xterm+256color"->256|"xterm-88color"->88|"xterm+88color"->88|_->16exceptionNo_such_encodingofstringletempty_stream=Lwt_stream.from(fun()->returnNone)letcreate?(windows=Sys.win32)?(model=default_model)incoming_fdincoming_channeloutgoing_fdoutgoing_channel=Lwt.catch(fun()->(* Colors stuff. *)letcolors=ifwindowsthen16elsecolors_of_termmodelinletbold_is_bright=matchmodelwith|"linux"(* The linux frame buffer *)|"xterm-color"(* The MacOS-X terminal *)->true|_->falseinletcolor_map=matchcolorswith|16->LTerm_color_mappings.colors_16|88->LTerm_color_mappings.colors_88|256->LTerm_color_mappings.colors_256|n->Printf.ksprintffailwith"LTerm.create: unknown number of colors (%d)"nin(* Encodings. *)(* Check if fds are ttys. *)Lwt_unix.isattyincoming_fd>>=funincoming_is_a_tty->Lwt_unix.isattyoutgoing_fd>>=funoutgoing_is_a_tty->(* Create the terminal. *)letterm={model;colors;windows;bold_is_bright;color_map;raw_mode=false;incoming_fd;outgoing_fd;ic=incoming_channel;oc=outgoing_channel;input_stream=empty_stream;next_event=None;read_event=false;outgoing_is_utf8=true;notify=Lwt_condition.create();event=E.never;incoming_is_a_tty;outgoing_is_a_tty;escape_time=0.1;size={rows=0;cols=0};last_reported_size={rows=0;cols=0};}interm.input_stream<-Lwt_stream.from(fun()->Lwt_io.read_char_optterm.ic);(* Setup initial size and size updater. *)ifterm.outgoing_is_a_ttythenbeginletcheck_size()=letsize=get_size_from_fdterm.outgoing_fdinifsize<>term.sizethenbeginterm.size<-size;Lwt_condition.signalterm.notify(LTerm_event.Resizesize)endinterm.size<-get_size_from_fdterm.outgoing_fd;term.last_reported_size<-term.size;term.event<-E.mapcheck_sizeresize_eventend;returnterm)Lwt.failletset_io?incoming_fd?incoming_channel?outgoing_fd?outgoing_channelterm=letgetoptx=matchoptwith|Somex->x|None->xinletincoming_fd=getincoming_fdterm.incoming_fdandoutgoing_fd=getoutgoing_fdterm.outgoing_fdandincoming_channel=getincoming_channelterm.icandoutgoing_channel=getoutgoing_channelterm.ocin(* Check if fds are ttys. *)Lwt_unix.isattyincoming_fd>>=funincoming_is_a_tty->Lwt_unix.isattyoutgoing_fd>>=funoutgoing_is_a_tty->(* Apply changes. *)term.incoming_fd<-incoming_fd;term.outgoing_fd<-outgoing_fd;term.ic<-incoming_channel;term.oc<-outgoing_channel;term.incoming_is_a_tty<-incoming_is_a_tty;term.outgoing_is_a_tty<-outgoing_is_a_tty;return()letmodelt=t.modelletcolorst=t.colorsletwindowst=t.windowsletis_a_ttyt=t.incoming_is_a_tty&&t.outgoing_is_a_ttyletincoming_is_a_ttyt=t.incoming_is_a_ttyletoutgoing_is_a_ttyt=t.outgoing_is_a_ttyletescape_timet=t.escape_timeletset_escape_timettime=t.escape_time<-timeletsizeterm=ifterm.outgoing_is_a_ttythenbeginletsize=get_size_from_fdterm.outgoing_fdinifsize<>term.sizethenbeginterm.size<-size;Lwt_condition.signalterm.notify(LTerm_event.Resizesize)end;sizeendelseraiseNot_a_ttyletget_sizeterm=Lwt.catch(fun()->return(sizeterm))Lwt.failletset_size__=Lwt.fail(Failure"LTerm.set_size is deprecated")(* +-----------------------------------------------------------------+
| Events |
+-----------------------------------------------------------------+ *)letparse_charfirst_bytest=letopenLwtinletcp1=int_of_charfirst_byteinletparsest=matchfirst_bytewith|'\x00'..'\x7f'->return(Uchar.of_intcp1)|'\xc0'..'\xdf'->Lwt_stream.nextst>|=int_of_char>>=funcp2->return@@Uchar.of_int(((cp1land0x1f)lsl6)lor(cp2land0x3f))|'\xe0'..'\xef'->Lwt_stream.nextst>|=int_of_char>>=funcp2->Lwt_stream.nextst>|=int_of_char>>=funcp3->return@@Uchar.of_int(((cp1land0x0f)lsl12)lor((cp2land0x3f)lsl6)lor(cp3land0x3f))|'\xf0'..'\xf7'->Lwt_stream.nextst>|=int_of_char>>=funcp2->Lwt_stream.nextst>|=int_of_char>>=funcp3->Lwt_stream.nextst>|=int_of_char>>=funcp4->return@@Uchar.of_int(((cp1land0x07)lsl18)lor((cp2land0x3f)lsl12)lor((cp3land0x3f)lsl6)lor(cp4land0x3f))|_->assertfalseinLwt.catch(fun()->Lwt_stream.parsestparse)(function|Lwt_stream.Empty->return(Uchar.of_charfirst_byte)|exn->Lwt.failexn)letread_charterm=beginLwt_stream.getterm.input_stream>>=funbyte_opt->matchbyte_optwith|Somebyte->returnbyte|None->Lwt.failEnd_of_fileend>>=funfirst_byte->Lwt.catch(fun()->Lwt_stream.parseterm.input_stream(parse_charfirst_byte))(function|Lwt_stream.Empty->return(Uchar.of_charfirst_byte)|exn->Lwt.failexn)>>=funchar->return(LTerm_event.Key{LTerm_key.control=false;LTerm_key.meta=false;LTerm_key.shift=false;LTerm_key.code=LTerm_key.Charchar;})letrecnext_eventterm=ifterm.windowsthenLTerm_windows.read_console_inputterm.incoming_fd>>=funinput->matchinputwith|LTerm_windows.Resize->ifterm.outgoing_is_a_ttythenletsize=get_size_from_fdterm.outgoing_fdinifsize<>term.sizethenbeginterm.size<-size;return(LTerm_event.Resizesize)endelsenext_eventtermelsenext_eventterm|LTerm_windows.Keykey->return(LTerm_event.Keykey)|LTerm_windows.Mousemouse->letwindow=(LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fd).LTerm_windows.windowinreturn(LTerm_event.Mouse{mousewithLTerm_mouse.row=mouse.LTerm_mouse.row-window.row1;LTerm_mouse.col=mouse.LTerm_mouse.col-window.col1;})elseLTerm_unix.parse_event~escape_time:term.escape_timeterm.input_streamletwrap_next_eventnext_eventterm=matchterm.next_eventwith|Somethread->thread|None->(* Create a non-cancelable thread. *)letwaiter,wakener=Lwt.wait()interm.next_event<-Somewaiter;(* Connect the [next_event term] thread to [waiter]. *)ignore(Lwt.try_bind(fun()->next_eventterm)(funv->term.next_event<-None;Lwt.wakeupwakenerv;return())(fune->term.next_event<-None;Lwt.wakeup_exnwakenere;return()));waiterletread_eventterm=ifterm.read_eventthenLwt.fail(Failure"LTerm.read_event: cannot read events from two thread at the same time")elseifterm.size<>term.last_reported_sizethenbeginterm.last_reported_size<-term.size;return(LTerm_event.Resizeterm.last_reported_size)endelsebeginterm.read_event<-true;Lwt.finalize(fun()->ifterm.incoming_is_a_ttythenLwt.pick[wrap_next_eventnext_eventterm;Lwt_condition.waitterm.notify]>>=funev->matchevwith|LTerm_event.Resizesize->term.last_reported_size<-size;return(LTerm_event.Resizesize)|ev->returnevelsewrap_next_eventread_charterm)(fun()->term.read_event<-false;return())end(* +-----------------------------------------------------------------+
| Modes |
+-----------------------------------------------------------------+ *)typemode=|Mode_fake|Mode_unixofUnix.terminal_io|Mode_windowsofLTerm_windows.console_modeletenter_raw_modeterm=ifterm.incoming_is_a_ttythenifterm.raw_modethenreturnMode_fakeelseifterm.windowsthenbeginletmode=LTerm_windows.get_console_modeterm.incoming_fdinLTerm_windows.set_console_modeterm.incoming_fd{modewithLTerm_windows.cm_echo_input=false;LTerm_windows.cm_line_input=false;LTerm_windows.cm_mouse_input=true;LTerm_windows.cm_processed_input=false;LTerm_windows.cm_window_input=true;};term.raw_mode<-true;return(Mode_windowsmode)endelsebeginLwt_unix.tcgetattrterm.incoming_fd>>=funattr->Lwt_unix.tcsetattrterm.incoming_fdUnix.TCSAFLUSH{attrwith(* Inspired from Python-3.0/Lib/tty.py: *)Unix.c_brkint=false;Unix.c_inpck=false;Unix.c_istrip=false;Unix.c_ixon=false;Unix.c_csize=8;Unix.c_parenb=false;Unix.c_echo=false;Unix.c_icanon=false;Unix.c_vmin=1;Unix.c_vtime=0;Unix.c_isig=false;}>>=fun()->term.raw_mode<-true;return(Mode_unixattr)endelseLwt.failNot_a_ttyletleave_raw_modetermmode=ifterm.incoming_is_a_ttythenmatchmodewith|Mode_fake->return()|Mode_unixattr->term.raw_mode<-false;Lwt_unix.tcsetattrterm.incoming_fdUnix.TCSAFLUSHattr|Mode_windowsmode->term.raw_mode<-false;LTerm_windows.set_console_modeterm.incoming_fdmode;return()elseLwt.failNot_a_ttyletenable_mouseterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenreturn()elseLwt_io.writeterm.oc"\027[?1000h"elseLwt.failNot_a_ttyletdisable_mouseterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenreturn()elseLwt_io.writeterm.oc"\027[?1000l"elseLwt.failNot_a_tty(* +-----------------------------------------------------------------+
| Cursor |
+-----------------------------------------------------------------+ *)letshow_cursorterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletsize,_=LTerm_windows.get_console_cursor_infoterm.outgoing_fdinLTerm_windows.set_console_cursor_infoterm.outgoing_fdsizetrue;return()endelseLwt_io.writeterm.oc"\027[?25h"elseLwt.failNot_a_ttylethide_cursorterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletsize,_=LTerm_windows.get_console_cursor_infoterm.outgoing_fdinLTerm_windows.set_console_cursor_infoterm.outgoing_fdsizefalse;return()endelseLwt_io.writeterm.oc"\027[?25l"elseLwt.failNot_a_ttyletgototermcoord=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginLwt_io.flushterm.oc>>=fun()->letwindow=(LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fd).LTerm_windows.windowinLTerm_windows.set_console_cursor_positionterm.outgoing_fd{row=window.row1+coord.row;col=window.col1+coord.col;};return()endelsebeginLwt_io.fprintterm.oc"\027[H">>=fun()->(ifcoord.row>0thenLwt_io.fprintfterm.oc"\027[%dB"coord.rowelsereturn())>>=fun()->(ifcoord.col>0thenLwt_io.fprintfterm.oc"\027[%dC"coord.colelsereturn())>>=fun()->return()endelseLwt.failNot_a_ttyletmovetermrowscols=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginLwt_io.flushterm.oc>>=fun()->letpos=(LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fd).LTerm_windows.cursor_positioninLTerm_windows.set_console_cursor_positionterm.outgoing_fd{row=pos.row+rows;col=pos.col+cols;};return()endelsebeginmatchrowswith|nwhenn<0->Lwt_io.fprintfterm.oc"\027[%dA"(-n)|nwhenn>0->Lwt_io.fprintfterm.oc"\027[%dB"n|_->return()end>>=fun()->beginmatchcolswith|nwhenn<0->Lwt_io.fprintfterm.oc"\027[%dD"(-n)|nwhenn>0->Lwt_io.fprintfterm.oc"\027[%dC"n|_->return()endelseLwt.failNot_a_tty(* +-----------------------------------------------------------------+
| Erasing text |
+-----------------------------------------------------------------+ *)letclear_screenterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspace(info.LTerm_windows.size.cols*info.LTerm_windows.size.rows){row=0;col=0}inreturn()endelseLwt_io.writeterm.oc"\027[2J"elseLwt.failNot_a_ttyletclear_screen_nextterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspace(info.LTerm_windows.size.cols*(info.LTerm_windows.size.rows-info.LTerm_windows.cursor_position.row)+info.LTerm_windows.size.cols-info.LTerm_windows.cursor_position.col)info.LTerm_windows.cursor_positioninreturn()endelseLwt_io.writeterm.oc"\027[J"elseLwt.failNot_a_ttyletclear_screen_prevterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspace(info.LTerm_windows.size.cols*info.LTerm_windows.cursor_position.row+info.LTerm_windows.cursor_position.col){row=0;col=0}inreturn()endelseLwt_io.writeterm.oc"\027[1J"elseLwt.failNot_a_ttyletclear_lineterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspaceinfo.LTerm_windows.size.cols{row=info.LTerm_windows.cursor_position.row;col=0}inreturn()endelseLwt_io.writeterm.oc"\027[2K"elseLwt.failNot_a_ttyletclear_line_nextterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspace(info.LTerm_windows.size.cols-info.LTerm_windows.cursor_position.col)info.LTerm_windows.cursor_positioninreturn()endelseLwt_io.writeterm.oc"\027[K"elseLwt.failNot_a_ttyletclear_line_prevterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinlet_=LTerm_windows.fill_console_output_characterterm.outgoing_fduspaceinfo.LTerm_windows.cursor_position.col{row=info.LTerm_windows.cursor_position.row;col=0}inreturn()endelseLwt_io.writeterm.oc"\027[1K"elseLwt.failNot_a_tty(* +-----------------------------------------------------------------+
| State |
+-----------------------------------------------------------------+ *)letsave_stateterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenreturn()elseLwt_io.writeterm.oc"\027[?1049h"elseLwt.failNot_a_ttyletload_stateterm=ifterm.outgoing_is_a_ttythenifterm.windowsthenreturn()elseLwt_io.writeterm.oc"\027[?1049l"elseLwt.failNot_a_tty(* +-----------------------------------------------------------------+
| String recoding |
+-----------------------------------------------------------------+ *)(*
let vline = Uchar.of_char '|'
let vlline = Uchar.of_char '+'
let dlcorner = Uchar.of_char '+'
let urcorner = Uchar.of_char '+'
let huline = Uchar.of_char '+'
let hdline = Uchar.of_char '+'
let vrline = Uchar.of_char '+'
let hline = Uchar.of_char '-'
let cross = Uchar.of_char '+'
let ulcorner = Uchar.of_char '+'
let drcorner = Uchar.of_char '+'
let question = Uchar.of_char '?'
*)(* Map characters that cannot be encoded to ASCII ones. *)(*let map_char char =
match Uchar.to_int char with
| 0x2500 -> hline
| 0x2501 -> hline
| 0x2502 -> vline
| 0x2503 -> vline
| 0x2504 -> hline
| 0x2505 -> hline
| 0x2506 -> vline
| 0x2507 -> vline
| 0x2508 -> hline
| 0x2509 -> hline
| 0x250a -> vline
| 0x250b -> vline
| 0x250c -> drcorner
| 0x250d -> drcorner
| 0x250e -> drcorner
| 0x250f -> drcorner
| 0x2510 -> dlcorner
| 0x2511 -> dlcorner
| 0x2512 -> dlcorner
| 0x2513 -> dlcorner
| 0x2514 -> urcorner
| 0x2515 -> urcorner
| 0x2516 -> urcorner
| 0x2517 -> urcorner
| 0x2518 -> ulcorner
| 0x2519 -> ulcorner
| 0x251a -> ulcorner
| 0x251b -> ulcorner
| 0x251c -> vrline
| 0x251d -> vrline
| 0x251e -> vrline
| 0x251f -> vrline
| 0x2520 -> vrline
| 0x2521 -> vrline
| 0x2522 -> vrline
| 0x2523 -> vrline
| 0x2524 -> vlline
| 0x2525 -> vlline
| 0x2526 -> vlline
| 0x2527 -> vlline
| 0x2528 -> vlline
| 0x2529 -> vlline
| 0x252a -> vlline
| 0x252b -> vlline
| 0x252c -> hdline
| 0x252d -> hdline
| 0x252e -> hdline
| 0x252f -> hdline
| 0x2530 -> hdline
| 0x2531 -> hdline
| 0x2532 -> hdline
| 0x2533 -> hdline
| 0x2534 -> huline
| 0x2535 -> huline
| 0x2536 -> huline
| 0x2537 -> huline
| 0x2538 -> huline
| 0x2539 -> huline
| 0x253a -> huline
| 0x253b -> huline
| 0x253c -> cross
| 0x253d -> cross
| 0x253e -> cross
| 0x253f -> cross
| 0x2540 -> cross
| 0x2541 -> cross
| 0x2542 -> cross
| 0x2543 -> cross
| 0x2544 -> cross
| 0x2545 -> cross
| 0x2546 -> cross
| 0x2547 -> cross
| 0x2548 -> cross
| 0x2549 -> cross
| 0x254a -> cross
| 0x254b -> cross
| 0x254c -> hline
| 0x254d -> hline
| 0x254e -> vline
| 0x254f -> vline
| 0x2550 -> hline
| 0x2551 -> vline
| _ ->
let nfd_seq= Uunf.decomp char in
if Array.length nfd_seq > 0 then
let uchar= Uunf.d_uchar nfd_seq.(0) in
if Uchar.to_int uchar <= 127 then
uchar
else
question
else
question
*)letencode_stringstr=strletencode_charch=Zed_utf8.singletonch(* +-----------------------------------------------------------------+
| Styled printing |
+-----------------------------------------------------------------+ *)moduleCodes=structletbold=";1"letunderline=";4"letblink=";5"letreverse=";7"letforeground=30letbackground=40endletfprinttermstr=Lwt_io.fprintterm.ocstrletfprintltermstr=fprintterm(str^"\n")letfprintftermfmt=Printf.ksprintf(funstr->fprinttermstr)fmtletfprintlftermfmt=Printf.ksprintf(funstr->fprintltermstr)fmtletadd_intbufn=letrecloop=function|0->()|n->loop(n/10);Buffer.add_charbuf(Char.unsafe_chr(48+(nmod10)))inifn=0thenBuffer.add_charbuf'0'elseloopnletmap_colortermrgb=letopenLTerm_color_mappingsinletmap=term.color_mapin(* The [String.unsafe_get]s are safe because the private type
[LTerm_style.color] ensure that all components are in the range
[0..255]. *)Char.code(String.unsafe_getmap.map(Char.code(String.unsafe_getmap.index_rr)+map.count_r*(Char.code(String.unsafe_getmap.index_gg)+map.count_g*Char.code(String.unsafe_getmap.index_bb))))letadd_indextermbufbasen=ifn<8thenbeginBuffer.add_charbuf';';add_intbuf(base+n)endelseifn<16&&term.bold_is_brightthenifbase=Codes.foregroundthenbeginBuffer.add_stringbuf";1;";add_intbuf(base+n-8)endelsebeginBuffer.add_charbuf';';add_intbuf(base+n-8)endelsebeginBuffer.add_charbuf';';add_intbuf(base+8);Buffer.add_stringbuf";5;";add_intbufnendletadd_colortermbufbase=function|LTerm_style.Default->()|LTerm_style.Indexn->add_indextermbufbasen|LTerm_style.RGB(r,g,b)->add_indextermbufbase(map_colortermrgb)letadd_styletermbufstyle=letopenLTerm_styleinBuffer.add_stringbuf"\027[0";(matchstyle.boldwithSometrue->Buffer.add_stringbufCodes.bold|_->());(matchstyle.underlinewithSometrue->Buffer.add_stringbufCodes.underline|_->());(matchstyle.blinkwithSometrue->Buffer.add_stringbufCodes.blink|_->());(matchstyle.reversewithSometrue->Buffer.add_stringbufCodes.reverse|_->());(matchstyle.foregroundwithSomecolor->add_colortermbufCodes.foregroundcolor|None->());(matchstyle.backgroundwithSomecolor->add_colortermbufCodes.backgroundcolor|None->());Buffer.add_charbuf'm'letexpandtermtext=ifArray.lengthtext=0then""elsebeginletbuf=Buffer.create256inBuffer.add_stringbuf"\027[0m";letrecloopidxprev_style=ifidx=Array.lengthtextthenbeginBuffer.add_stringbuf"\027[0m";Buffer.contentsbufendelsebeginletch,style=Array.unsafe_gettextidxinifnot(LTerm_style.equalstyleprev_style)thenadd_styletermbufstyle;Buffer.add_stringbuf(Zed_char.to_utf8ch);loop(idx+1)styleendinloop0LTerm_style.noneendletwindows_fg_colorterm=function|LTerm_style.Default->7|LTerm_style.Indexn->n|LTerm_style.RGB(r,g,b)->map_colortermrgbletwindows_bg_colorterm=function|LTerm_style.Default->0|LTerm_style.Indexn->n|LTerm_style.RGB(r,g,b)->map_colortermrgbletwindows_default_attributes={LTerm_windows.foreground=7;LTerm_windows.background=0}letwindows_attributes_of_styletermstyle=letopenLTerm_styleinifstyle.reverse=Sometruethen{LTerm_windows.foreground=(matchstyle.backgroundwithSomecolor->windows_bg_colortermcolor|None->0);LTerm_windows.background=(matchstyle.foregroundwithSomecolor->windows_fg_colortermcolor|None->7);}else{LTerm_windows.foreground=(matchstyle.foregroundwithSomecolor->windows_fg_colortermcolor|None->7);LTerm_windows.background=(matchstyle.backgroundwithSomecolor->windows_bg_colortermcolor|None->0);}letfprints_windowstermoctext=letrecloopidxprev_attr=ifidx=Array.lengthtextthenbeginLwt_io.flushoc>>=fun()->LTerm_windows.set_console_text_attributeterm.outgoing_fdwindows_default_attributes;return()endelsebeginletch,style=Array.unsafe_gettextidxinletattr=windows_attributes_of_styletermstyleinbeginifattr<>prev_attrthenLwt_io.flushoc>>=fun()->LTerm_windows.set_console_text_attributeterm.outgoing_fdattr;return()elsereturn()end>>=fun()->letchars=Zed_char.to_rawchinlets=chars|>List.mapencode_char|>String.concat""inLwt_io.writeocs>>=fun()->loop(idx+1)attrendinLwt_io.flushoc>>=fun()->LTerm_windows.set_console_text_attributeterm.outgoing_fdwindows_default_attributes;loop0windows_default_attributesletfprintstermtext=ifterm.outgoing_is_a_ttythenifterm.windowsthenLwt_io.atomic(funoc->fprints_windowstermoctext)term.ocelsefprintterm(expandtermtext)elsefprintterm(Zed_string.to_utf8(LTerm_text.to_stringtext))letfprintlstermtext=fprintsterm(Array.appendtext(LTerm_text.of_utf8"\n"))(* +-----------------------------------------------------------------+
| Printing with contexts |
+-----------------------------------------------------------------+ *)typecontext={ctx_term:t;ctx_oc:Lwt_io.output_channel;mutablectx_style:LTerm_style.t;mutablectx_attr:LTerm_windows.text_attributes;}letclear_stylestermoc=ifterm.outgoing_is_a_ttythenifterm.windowsthenLwt_io.flushoc>>=fun()->LTerm_windows.set_console_text_attributeterm.outgoing_fdwindows_default_attributes;return()elseLwt_io.writeoc"\027[0m"elsereturn()letwith_contexttermf=Lwt_io.atomic(funoc->letctx={ctx_term=term;ctx_oc=oc;ctx_style=LTerm_style.none;ctx_attr=windows_default_attributes;}inclear_stylestermoc>>=fun()->Lwt.finalize(fun()->fctx)(fun()->clear_stylestermoc))term.ocletupdate_stylectxstyle=ifctx.ctx_term.outgoing_is_a_ttythenbeginifctx.ctx_term.windowsthenbeginletattr=windows_attributes_of_stylectx.ctx_termstyleinifattr<>ctx.ctx_attrthenLwt_io.flushctx.ctx_oc>>=fun()->LTerm_windows.set_console_text_attributectx.ctx_term.outgoing_fdattr;ctx.ctx_attr<-attr;return()elsereturn()endelsebeginifnot(LTerm_style.equalstylectx.ctx_style)thenbeginletbuf=Buffer.create16inadd_stylectx.ctx_termbufstyle;Lwt_io.writectx.ctx_oc(Buffer.contentsbuf)>>=fun()->ctx.ctx_style<-style;return()endelsereturn()endendelsereturn()letcontext_termctx=ctx.ctx_termletcontext_occtx=ctx.ctx_oc(* +-----------------------------------------------------------------+
| Styles setting |
+-----------------------------------------------------------------+ *)letset_styletermstyle=ifterm.outgoing_is_a_ttythenifterm.windowsthenbeginletattr=windows_attributes_of_styletermstyleinLwt_io.atomic(funoc->Lwt_io.flushoc>>=fun()->LTerm_windows.set_console_text_attributeterm.outgoing_fdattr;return())term.ocendelsebeginletbuf=Buffer.create16inadd_styletermbufstyle;Lwt_io.fprintterm.oc(Buffer.contentsbuf)endelsereturn()(* +-----------------------------------------------------------------+
| Rendering |
+-----------------------------------------------------------------+ *)letsame_stylep1p2=letopenLTerm_drawinp1.bold=p2.bold&&p1.underline=p2.underline&&p1.blink=p2.blink&&p1.reverse=p2.reverse&&p1.foreground=p2.foreground&&p1.background=p2.backgroundletunknown_uchar=Uchar.of_int0xfffdletunknown_char=Zed_char.unsafe_of_uCharunknown_ucharletunknown_utf8=Zed_char.to_utf8unknown_charletrender_styletermbufold_pointnew_point=letopenLTerm_drawinifnot(same_stylenew_pointold_point)thenbegin(* Reset styles if they are different from the previous point. *)Buffer.add_stringbuf"\027[0";ifnew_point.boldthenBuffer.add_stringbufCodes.bold;ifnew_point.underlinethenBuffer.add_stringbufCodes.underline;ifnew_point.blinkthenBuffer.add_stringbufCodes.blink;ifnew_point.reversethenBuffer.add_stringbufCodes.reverse;add_colortermbufCodes.foregroundnew_point.foreground;add_colortermbufCodes.backgroundnew_point.background;Buffer.add_charbuf'm';endletrender_pointtermbufold_pointnew_point=render_styletermbufold_pointnew_point;(* Skip control characters, otherwise output will be messy. *)ifUchar.to_int(Zed_char.corenew_point.LTerm_draw.char)<32thenBuffer.add_stringbufunknown_utf8elseBuffer.add_stringbuf(Zed_char.to_utf8new_point.LTerm_draw.char)typerender_kind=Render_screen|Render_boxletrender_update_unixtermkindold_matrixmatrix=letopenLTerm_drawinletbuf=Buffer.create16inBuffer.add_stringbuf(matchkindwith|Render_screen->(* Go the the top-left and reset attributes *)"\027[H\027[0m"|Render_box->(* Go the the beginnig of line and reset attributes *)"\r\027[0m");(* The last displayed point. *)letlast_point=ref{char=yspace;bold=false;underline=false;blink=false;reverse=false;foreground=LTerm_style.default;background=LTerm_style.default;}inletrows=Array.lengthmatrixandold_rows=Array.lengthold_matrixinfory=0torows-1doletline=Array.unsafe_getmatrixyin(* If the current line is equal to the displayed one, skip it *)ify>=old_rows||line<>Array.unsafe_getold_matrixythenbeginforx=0toArray.lengthline-1doletpoint=!(Array.unsafe_getlinex)inmatchpointwith|Elemelem->render_pointtermbuf!last_pointelem;last_point:=elem|WidthHolder_n->()doneend;ify<rows-1thenBuffer.add_charbuf'\n'done;Buffer.add_stringbuf"\027[0m";(* Go to the beginning of the line if rendering a box. *)ifkind=Render_boxthenBuffer.add_charbuf'\r';fprintterm(Buffer.contentsbuf)letblank_windows={LTerm_windows.ci_char=yspace;LTerm_windows.ci_foreground=7;LTerm_windows.ci_background=0;}letwindows_char_infotermpointchar=ifpoint.LTerm_draw.reversethen{LTerm_windows.ci_char=char;LTerm_windows.ci_foreground=windows_bg_colortermpoint.LTerm_draw.background;LTerm_windows.ci_background=windows_fg_colortermpoint.LTerm_draw.foreground;}else{LTerm_windows.ci_char=char;LTerm_windows.ci_foreground=windows_fg_colortermpoint.LTerm_draw.foreground;LTerm_windows.ci_background=windows_bg_colortermpoint.LTerm_draw.background;}letrender_windowstermkindhandle_newlinesmatrix=(* Build the matrix of char infos *)letmatrix=Array.map(funline->letlen=Array.lengthline-(ifhandle_newlinesthen1else0)iniflen<0theninvalid_arg"LTerm.print_box_with_newlines";letres=Array.makelenblank_windowsinletrecloopi=ifi=lenthenreselsebeginmatch!(Array.unsafe_getlinei)with|LTerm_draw.Elempoint->letcode=Uchar.to_int(Zed_char.corepoint.LTerm_draw.char)inifhandle_newlines&&code=10thenbegin(* Copy styles. *)Array.unsafe_setresi(windows_char_infotermpointyspace);fori=i+1tolen-1domatch!(Array.unsafe_getlinei)with|LTerm_draw.Elempoint->Array.unsafe_setresi(windows_char_infotermpointyspace)|_->()done;resendelsebeginletchar=ifcode<32thenunknown_charelsepoint.LTerm_draw.charinArray.unsafe_setresi(windows_char_infotermpointchar);loop(i+1)end|WidthHolder_n->resendinloop0)matrixinletrows=Array.lengthmatrixinbeginmatchkindwith|Render_screen->return()|Render_box->(* Ensure that there is enough place to display the box. *)fprintterm"\r">>=fun()->fprintterm(String.make(rows-1)'\n')>>=fun()->Lwt_io.flushterm.ocend>>=fun()->letinfo=LTerm_windows.get_console_screen_buffer_infoterm.outgoing_fdinletwindow_rect=info.LTerm_windows.windowinletrect=matchkindwith|Render_screen->window_rect|Render_box->{window_rectwithrow1=info.LTerm_windows.cursor_position.row-(rows-1);row2=info.LTerm_windows.cursor_position.row+1}inignore(LTerm_windows.write_console_outputterm.outgoing_fdmatrix{rows=Array.lengthmatrix;cols=ifmatrix=[||]then0elseArray.lengthmatrix.(0)}{row=0;col=0}rect);return()letrender_updatetermold_matrixmatrix=ifterm.outgoing_is_a_ttythenifterm.windowsthenrender_windowstermRender_screenfalsematrixelserender_update_unixtermRender_screenold_matrixmatrixelseLwt.failNot_a_ttyletrendertermm=render_updateterm[||]mletprint_boxtermmatrix=ifterm.outgoing_is_a_ttythenbeginifArray.lengthmatrix>0thenbeginifterm.windowsthenrender_windowstermRender_boxfalsematrixelserender_update_unixtermRender_box[||]matrixendelsefprintterm"\r"endelseLwt.failNot_a_ttyletprint_box_with_newlines_unixtermmatrix=letopenLTerm_drawinletbuf=Buffer.create16in(* Go the the beginnig of line and reset attributes *)Buffer.add_stringbuf"\r\027[0m";(* The last displayed point. *)letlast_point=ref{char=yspace;bold=false;underline=false;blink=false;reverse=false;foreground=LTerm_style.default;background=LTerm_style.default;}inletrows=Array.lengthmatrixinfory=0torows-1doletline=Array.unsafe_getmatrixyinletcols=Array.lengthline-1inifcols<0theninvalid_arg"LTerm.print_box_with_newlines";letrecloopx=match!(Array.unsafe_getlinex)with|Elempoint->letcode=Uchar.to_int(Zed_char.corepoint.char)inifx=colsthenbeginifcode=10&&y<rows-1thenBuffer.add_charbuf'\n'endelseifcode=10thenbegin(* Use the style of the newline for the rest of the line. *)render_styletermbuf!last_pointpoint;last_point:=point;(* Erase everything until the end of line. *)Buffer.add_stringbuf"\027[K";ify<rows-1thenBuffer.add_charbuf'\n'endelsebeginrender_pointtermbuf!last_pointpoint;last_point:=point;loop(x+1)end|WidthHolder_n->loop(x+1)inloop0done;Buffer.add_stringbuf"\027[0m\r";fprintterm(Buffer.contentsbuf)letprint_box_with_newlinestermmatrix=ifterm.outgoing_is_a_ttythenbeginifArray.lengthmatrix>0thenbeginifterm.windowsthenrender_windowstermRender_boxtruematrixelseprint_box_with_newlines_unixtermmatrixendelsefprintterm"\r"endelseLwt.failNot_a_tty(* +-----------------------------------------------------------------+
| Misc |
+-----------------------------------------------------------------+ *)letflushterm=Lwt_io.flushterm.ocletget_size_from_fdfd=return(get_size_from_fdfd)letset_size_from_fdfdsize=return(set_size_from_fdfdsize)(* +-----------------------------------------------------------------+
| Standard terminals |
+-----------------------------------------------------------------+ *)letstdout=lazy(createLwt_unix.stdinLwt_io.stdinLwt_unix.stdoutLwt_io.stdout)letstderr=lazy(createLwt_unix.stdinLwt_io.stdinLwt_unix.stderrLwt_io.stderr)letprintstr=Lazy.forcestdout>>=funterm->fprinttermstrletprintlstr=Lazy.forcestdout>>=funterm->fprintltermstrletprintffmt=Printf.ksprintfprintfmtletprintsstr=Lazy.forcestdout>>=funterm->fprintstermstrletprintlffmt=Printf.ksprintfprintlfmtletprintlsstr=Lazy.forcestdout>>=funterm->fprintlstermstrleteprintstr=Lazy.forcestderr>>=funterm->fprinttermstrleteprintlstr=Lazy.forcestderr>>=funterm->fprintltermstrleteprintffmt=Printf.ksprintfeprintfmtleteprintsstr=Lazy.forcestderr>>=funterm->fprintstermstrleteprintlffmt=Printf.ksprintfeprintlfmtleteprintlsstr=Lazy.forcestderr>>=funterm->fprintlstermstr