Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file widget.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643open!Importopen!AsyncmoduleR=Render.Make(Draw_notty)moduleHierarchy=structtypenode={mutablevisible:bool;signals:Wave.tlist;children:nodeBase.Map.M(String).t}[@@derivingsexp_of]typecurrently_rendered={actual_wave:Wave.tarray;for_rendering:Wave.tarray}[@@derivingsexp_of]typet={cfg:Waves.Config.t;node:node;mutablecurrently_rendered:currently_renderedoption}[@@derivingsexp_of]letempty_node={visible=false;signals=[];children=Map.empty(moduleString)};;letrecupdate~path~wavet=matchpathwith|[]->assertfalse|[_]->{twithsignals=wave::t.signals}|hd::tl->letchildren=Map.updatet.childrenhd~f:(function|None->update~path:tl~waveempty_node|Somex->update~path:tl~wavex)in{twithchildren};;lettoggle_moduletname=letreclooppath(node:node)=matchpathwith|[]->node.visible<-notnode.visible|hd::tl->looptl(Map.find_exnnode.childrenhd)in(tryloop(String.splitname~on:'$')t.nodewith|Not_found_s_->raise_s[%message"Cannot resolve key in module"name]);t.currently_rendered<-None;;letof_waves(waves:Waves.t)=letinit=empty_nodeinletret=Array.foldwaves.waves~init~f:(funaccwave->letpath=String.split~on:'$'(Wave.get_namewave)inupdate~path~waveacc)inret.visible<-true;{cfg=waves.cfg;node=ret;currently_rendered=None};;letiter_wave~ft=letrecloop~depth~rev_pathnode=ifnode.visiblethen(List.iter~f:(funnode->f~depthnode)node.signals;Map.iterinode.children~f:(fun~key~data:node->letmodule_name=String.concat~sep:"$"(List.rev(key::rev_path))inf~depth(Wave.Emptymodule_name);loop~rev_path:(key::rev_path)~depth:(depth+1)node))inloop~depth:0~rev_path:[]t.node;;letset_currently_renderedt=letactual_wave=ref[]inletfor_rendering=ref[]inlet()=iter_wavet~f:(fun~depthw->actual_wave:=w::!actual_wave;letpadding=String.init(depth*2)~f:(fun_->' ')inletname=String.split~on:'$'(Wave.get_namew)|>List.last_exninletname=matchwwith|Empty_->"<"^name^">"|_->nameinfor_rendering:=Wave.set_namew(padding^name)::!for_rendering)inletfor_rendering=Array.of_list_rev!for_renderinginletactual_wave=Array.of_list_rev!actual_waveint.currently_rendered<-Some{for_rendering;actual_wave};;letfind_actual_waveti=set_currently_renderedt;(Option.value_exnt.currently_rendered).actual_wave.(i);;letget_currently_rendered_wavest=set_currently_renderedt;letcurrently_rendered=(Option.value_exnt.currently_rendered).for_renderingin{Waves.cfg=t.cfg;waves=currently_rendered};;endmoduleSignals_window=structtypet={hierarchy:Hierarchy.t;max_signal_name_width:int;num_waves:int;style:(Draw_notty.style[@sexp.opaque])}[@@derivingsexp_of]letcreate~waves~hierarchy={hierarchy;max_signal_name_width=R.get_max_signal_widthwaves;num_waves=R.get_max_signalswaves;style=Render.Styles.(colourwhite_on_black).signals};;letdraw~ctx~boundst=R.draw_signals~style:t.style~ctx~bounds(Hierarchy.get_currently_rendered_wavest.hierarchy);;endmoduleValues_window=structtypet={hierarchy:Hierarchy.t;mutablemax_value_width:int;num_waves:int;style:(Draw_notty.style[@sexp.opaque])}[@@derivingsexp_of]letcreate~waves~hierarchy=letmax_value_width=R.get_estimated_max_value_widthwavesin{hierarchy;max_value_width;num_waves=R.get_max_signalswaves;style=Render.Styles.(colourwhite_on_black).values};;letdraw~ctx~boundst=letoffset=t.hierarchy.cfg.value_scrollint.hierarchy.cfg.value_scroll<-max0(min(t.max_value_width-1)(t.max_value_width-offset));t.max_value_width<-R.draw_values~style:t.style~ctx~bounds(Hierarchy.get_currently_rendered_wavest.hierarchy);t.hierarchy.cfg.value_scroll<-offset;;endmoduleWaves_window=structtypet={hierarchy:Hierarchy.t;mutablenum_cycles:int;num_waves:int;style:(Draw_notty.style[@sexp.opaque])}[@@derivingsexp_of]letcreate~(waves:Waves.t)~hierarchy={hierarchy;num_cycles=0;num_waves=R.get_max_signalswaves;style=Render.Styles.(colourwhite_on_black).waves};;letdraw~ctx~boundst=R.draw_wave~style:t.style~ctx~bounds(Hierarchy.get_currently_rendered_wavest.hierarchy);;endmoduleWith_bounds=structtype'at={bounds:Draw.rect;window:'a}[@@derivingsexp_of]endmoduleBorder=structletadjust(x:Draw.rect)={Draw.r=x.r+1;c=x.c+1;w=x.w-2;h=x.h-2};;letdraw~ctx~boundslabel=Draw_notty.draw_box~ctx~bounds~style:Draw.Style.defaultlabel;;endmoduleWaveform_window=structtypet={signals_window:Signals_window.tWith_bounds.t;values_window:Values_window.tWith_bounds.t;waves_window:Waves_window.tWith_bounds.t;scroll_signals:Scroll.HScrollbar.t;scroll_values:Scroll.HScrollbar.t;scroll_waves:Scroll.HScrollbar.t;scroll_vert:Scroll.VScrollbar.t;max_signal_offset:int;max_cycle_offset:int}[@@derivingsexp_of]letget_signal_offset(t:t)=t.waves_window.window.hierarchy.cfg.start_signalletset_signal_offset(t:t)offset=t.waves_window.window.hierarchy.cfg.start_signal<-max0(min(t.max_signal_offset-1)offset);Scroll.Scrollable.set_offsett.scroll_vert.scrollableoffset;;letget_cycle_offset(t:t)=t.waves_window.window.hierarchy.cfg.start_cycleletset_cycle_offset(t:t)offset=t.waves_window.window.hierarchy.cfg.start_cycle<-max0(min(t.max_cycle_offset-1)offset);Scroll.Scrollable.set_offsett.scroll_waves.scrollableoffset;;let_get_signal_name_offset(t:t)=t.signals_window.window.hierarchy.cfg.signal_scroll;;letset_signal_name_offset(t:t)offset=t.signals_window.window.hierarchy.cfg.signal_scroll<-max0(min(t.signals_window.window.max_signal_name_width-1)offset);Scroll.Scrollable.set_offsett.scroll_signals.scrollableoffset;;let_get_value_offset(t:t)=t.values_window.window.hierarchy.cfg.value_scrollletset_value_offset(t:t)offset=t.values_window.window.hierarchy.cfg.value_scroll<-max0(min(t.values_window.window.max_value_width-1)offset);Scroll.Scrollable.set_offsett.scroll_values.scrollableoffset;;letcreate~signals_width~values_width~rows~colswaves=lethbarheight=1inletvbarwidth=2inlethierarchy=Hierarchy.of_waveswavesinletsignals_window:Signals_window.tWith_bounds.t={bounds={r=0;c=0;w=signals_width;h=rows-hbarheight};window=Signals_window.create~waves~hierarchy}inletvalues_window:Values_window.tWith_bounds.t={bounds={r=0;c=signals_width;w=values_width;h=rows-hbarheight};window=Values_window.create~waves~hierarchy}inletwaves_window:Waves_window.tWith_bounds.t=letsum=signals_width+values_widthin{bounds={r=0;c=sum;w=cols-sum-vbarwidth;h=rows-hbarheight};window=Waves_window.create~waves~hierarchy}inletscroll_vert=Scroll.VScrollbar.create{Draw.r=0;c=cols-vbarwidth;w=vbarwidth;h=rows-hbarheight}inletscroll_signals=Scroll.HScrollbar.create{Draw.r=rows-hbarheight;c=0;w=signals_width;h=hbarheight}inletscroll_values=Scroll.HScrollbar.create{Draw.r=rows-hbarheight;c=signals_width;w=values_width;h=hbarheight}inletscroll_waves=letsum=signals_width+values_widthinScroll.HScrollbar.create{Draw.r=rows-hbarheight;c=sum;w=cols-sum-vbarwidth;h=hbarheight}inletmax_signal_offset=R.get_max_signalswavesinletmax_cycle_offset=R.get_max_cycleswavesinletwaveform={signals_window;values_window;waves_window;scroll_signals;scroll_values;scroll_waves;scroll_vert;max_signal_offset;max_cycle_offset}inScroll.Scrollable.set_rangescroll_vert.scrollablesignals_window.window.num_waves;scroll_vert.scrollable.adj.on_offset_change<-set_signal_offsetwaveform;Scroll.Scrollable.set_rangescroll_waves.scrollablemax_cycle_offset;scroll_waves.scrollable.adj.on_offset_change<-set_cycle_offsetwaveform;Scroll.Scrollable.set_rangescroll_signals.scrollablesignals_window.window.max_signal_name_width;scroll_signals.scrollable.adj.on_offset_change<-set_signal_name_offsetwaveform;Scroll.Scrollable.set_rangescroll_values.scrollablevalues_window.window.max_value_width;Scroll.Scrollable.set_offsetscroll_values.scrollable(values_window.window.max_value_width-1);scroll_values.scrollable.adj.on_offset_change<-set_value_offsetwaveform;waveform;;letdraw~ctx(t:t)=letdraw_with_borderf~ctx~boundsnamea=f~ctx~bounds:(Border.adjustbounds)a;Border.draw~ctx~boundsnameindraw_with_borderSignals_window.draw~ctx~bounds:t.signals_window.bounds"signals"t.signals_window.window;draw_with_borderValues_window.draw~ctx~bounds:t.values_window.bounds"values"t.values_window.window;draw_with_borderWaves_window.draw~ctx~bounds:t.waves_window.bounds"waves"t.waves_window.window;Scroll.VScrollbar.draw~ctx~style:Draw.Style.defaultt.scroll_vert;Scroll.HScrollbar.draw~ctx~style:Draw.Style.defaultt.scroll_signals;Scroll.HScrollbar.draw~ctx~style:Draw.Style.defaultt.scroll_values;Scroll.HScrollbar.draw~ctx~style:Draw.Style.defaultt.scroll_waves;;letscale_key_handler(t:t)key=letcfg=t.waves_window.window.hierarchy.cfginmatchkeywith|`ASCII'=',[]->cfg.wave_width<-cfg.wave_width+1;true|`ASCII'-',[]->cfg.wave_width<-cfg.wave_width-1;true|`ASCII'+',[]->cfg.wave_height<-cfg.wave_height+1;true|`ASCII'_',[]->cfg.wave_height<-max0(cfg.wave_height-1);true|_->false;;letscroll_key_handler(t:t)key=matchkeywith|`Arrow`Left,[]->set_cycle_offsett(get_cycle_offsett-1);true|`Arrow`Right,[]->set_cycle_offsett(get_cycle_offsett+1);true|`Arrow`Up,[]->set_signal_offsett(get_signal_offsett-1);true|`Arrow`Down,[]->set_signal_offsett(get_signal_offsett+1);true|_->false;;letzrect={Draw.r=0;c=0;w=0;h=0}letlast_mouse_button:(Notty.Unescape.button*Notty.Unescape.mods)optionref=refNone;;letmouse_handler(t:t)((button,(col,row),mods)asmouse:Notty.Unescape.mouse)=letcfg=t.waves_window.window.hierarchy.cfginletpickf=matchR.pick~bounds:{waves=t.waves_window.bounds;values=zrect;signals=zrect;status=zrect}~r:row~c:col(Hierarchy.get_currently_rendered_wavest.waves_window.window.hierarchy)with|R.Wave(cycle,signal)->fcyclesignal;true|_->falseinletin_bounds(bounds:Draw.rect)=row>=bounds.r&&col>=bounds.c&&row<bounds.r+bounds.h&&col<bounds.c+bounds.winlettoggle_modulebutton=ifin_boundst.signals_window.bounds&&Poly.equalbutton(Some(`Left,[]))then(letoffset=get_signal_offsettinlethierarchy=t.signals_window.window.hierarchyinletwave_height=hierarchy.cfg.wave_heightinletwaves=Hierarchy.get_currently_rendered_waveshierarchyinletselected_index=letcurrent_position=ref(row-1)inletrecloopilower=ifi=Array.lengthwaves.wavesthenNoneelse(letdelta=snd(R.get_wave_height(wave_height,waves.waves.(i)))inifi<offsetthen(current_position:=!current_position+delta;loop(i+1)(lower+delta))elseiflower<=!current_position&&!current_position<lower+deltathenSomeielseloop(i+1)(lower+delta))inloop00inmatchselected_indexwith|None->false|Someselected_index->(matchwaves.waves.(selected_index)with|Empty_->letname=Wave.get_name(Hierarchy.find_actual_wavehierarchyselected_index)inHierarchy.toggle_modulehierarchyname;true|_->false))elsefalseinletupdate_cursorbutton=ifin_boundst.waves_window.bounds&&Poly.equalbutton(Some(`Left,[]))thenpick(funcycle_->cfg.wave_cursor<-cycle)elsefalseinletupdate_mouse_button_scrollbutton=matchbuttonwith|Some(`Scroll`Up,[])->set_signal_offsett(get_signal_offsett-1);true|Some(`Scroll`Down,[])->set_signal_offsett(get_signal_offsett+1);true|Some(`Scroll`Up,[`Ctrl])->set_cycle_offsett(get_cycle_offsett-1);true|Some(`Scroll`Down,[`Ctrl])->set_cycle_offsett(get_cycle_offsett+1);true|_->falseinletupdate_scroll_bar(scroll:Scroll.Scrollbar.t)_=in_boundsscroll.bounds&&Scroll.Scrollbar.mouse_eventscrollmouseinmatchbuttonwith|`Pressb->last_mouse_button:=Some(b,mods);List.fold_left[update_cursor;update_mouse_button_scroll;update_scroll_bart.scroll_vert;update_scroll_bart.scroll_waves;update_scroll_bart.scroll_signals;update_scroll_bart.scroll_values;toggle_module]~init:false~f:(funaccf->acc||f!last_mouse_button)|`Release->letbutton=!last_mouse_buttoninlast_mouse_button:=None;update_cursorbutton|`Drag->update_cursor!last_mouse_button;;(* return true to redraw *)lethandler(t:t)event=matcheventwith|`Mousemouse->mouse_handlertmouse|`Keykey->List.fold_left[scale_key_handler;scroll_key_handler]~init:false~f:(funaccf->acc||ftkey)|`Resize_|`Paste_->false;;endmoduleContext=structtypet={term:Notty_async.Term.t;mutablerows:int;mutablecols:int;waves:Waves.t;mutablewaveform:Waveform_window.t;events:[Notty.Unescape.event|`Resizeofint*int]Pipe.Reader.t;stop:unitDeferred.t;mutabledraw_ctx:Draw_notty.ctx;signals_width:int;values_width:int}letcreate~signals_width~values_widthwaves=let%bindterm=Notty_async.Term.create()inletcols,rows=Notty_async.Term.sizeterminletwaveform=Waveform_window.create~signals_width~values_width~cols~rowswavesinletevents=Notty_async.Term.eventsterminletstop=Pipe.closedeventsinlet%bind()=Notty_async.Term.cursortermNoneinletdraw_ctx=Draw_notty.init~rows~colsinreturn{term;rows;cols;events;stop;waves;waveform;draw_ctx;signals_width;values_width};;letresize~rows~colst=letwaveform=Waveform_window.create~signals_width:t.signals_width~values_width:t.values_width~cols~rowst.wavesinletdraw_ctx=Draw_notty.init~rows~colsint.rows<-rows;t.cols<-cols;t.waveform<-waveform;t.draw_ctx<-draw_ctx;;letdraw(ctx:t)=Waveform_window.draw~ctx:ctx.draw_ctxctx.waveform;letimage=Draw_notty.to_imagectx.draw_ctxinNotty_async.Term.imagectx.termimage;;lethandle_eventctxevent=lethandlerevent=Waveform_window.handlerctx.waveformeventinmatcheventwith|`Mouse_->handlerevent|`Keykey->(matchkeywith|`ASCII'q',[]|`Escape,[]->Pipe.close_readctx.events;false|_->handlerevent)|`Resize(cols,rows)->resize~rows~colsctx;true|`Paste_->false;;letiter_eventsctx=(* process events in batches and draw at the end. Given rendering can be slow, this
behaves much better - especially over a ssh connection. *)Pipe.iter'ctx.events~f:(funq->letredraw=Core_kernel.Queue.foldq~init:false~f:(funredrawevent->ifhandle_eventctxeventthentrueelseredraw)inifredrawthendrawctxelsereturn());;endletrun_waves?(signals_width=20)?(values_width=20)waves=let%bindctx=Context.create~signals_width~values_widthwavesinlet%bind()=Context.drawctxindon't_wait_for(Context.iter_eventsctx);ctx.stop;;letrun?signals_width?values_widthwaves=Thread_safe.block_on_async(fun()->run_waves?signals_width?values_widthwaves)|>Result.ok_exn;;letrun_and_close?signals_width?values_widthwaves=don't_wait_for(let%bind()=run_waves?signals_width?values_widthwavesinshutdown0;return());Core.never_returns(Scheduler.go());;letrun_interactive_viewer?signals_width?values_width?display_rulest=run_and_close?signals_width?values_width{cfg=Waves.Config.default;waves=Waveform.sort_ports_and_formatstdisplay_rules};;