123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385openCoreopenKey_handlers_intfmoduleMake_common(X:sigtypetvalquit:t->Event_response.tvalscroll_vertical:t->by:int->Event_response.tend)=structopenXletkey_functions={Key_actions.Common.quit;scroll_up=scroll_vertical~by:(-1);scroll_down=scroll_vertical~by:1;scroll_up_fast=scroll_vertical~by:(-10);scroll_down_fast=scroll_vertical~by:10};;endmoduleWaveform_window=structmoduleMake(Data:Hardcaml_waveterm_kernel.Data.S)(Hierarchy:Hierarchy.M(Data).S)(Ui_state:Ui_state.M(Data)(Hierarchy).S)(X:Waveform_window_apiwithtypehierarchy:=Hierarchy.t)=structincludeXopenEvent_responseletshow_help_=Show_helpletcursor_centered_zoomt~new_wave_width=letcfg=cfgtinletnew_wave_width=letnew_wave_width=Int.minnew_wave_width1_000inletnew_wave_width=Int.maxnew_wave_width(-1_000_000)innew_wave_widthin(* Set the new wave width *)cfg.wave_width<-new_wave_width;(* Center on cursor, if set *)Option.iter(Cursors.get_offset(cursorst))~f:(funcursor_offset->set_cycle_offsett(cursor_offset-(cycles_displayedt/2)));;letcursor_centered_zoom_outt=cursor_centered_zoomt~new_wave_width:((cfgt).wave_width-1);Redraw_window;;letcursor_centered_zoom_int=cursor_centered_zoomt~new_wave_width:((cfgt).wave_width+1);Redraw_window;;letcursor_centered_zoom_out_fastt=letcfg=cfgtinletnew_wave_width=ifcfg.wave_width=0then-1elseifcfg.wave_width<0thencfg.wave_width*2elsecfg.wave_width/2incursor_centered_zoomt~new_wave_width;Redraw_window;;letcursor_centered_zoom_in_fastt=letcfg=cfgtinletnew_wave_width=ifcfg.wave_width=0then1elseifcfg.wave_width>0thencfg.wave_width*2elsecfg.wave_width/2incursor_centered_zoomt~new_wave_width;Redraw_window;;letexpand_signals_windowt=letcfg=cfgtincfg.signals_width<-cfg.signals_width+1;Recreate_window;;letshrink_signals_windowt=letcfg=cfgtincfg.signals_width<-max2(cfg.signals_width-1);Recreate_window;;letexpand_values_windowt=letcfg=cfgtincfg.values_width<-cfg.values_width+1;Recreate_window;;letshrink_values_windowt=letcfg=cfgtincfg.values_width<-max2(cfg.values_width-1);Recreate_window;;letscroll_wave_to_beginningt=set_cycle_offsett0;Redraw_window;;letscroll_wave_to_endt=set_cycle_offsett(max_cycle_offsett);Redraw_window;;letscroll_to_first_signalt=set_signal_offsett0;Redraw_window;;letscroll_to_last_signalt=set_signal_offsett(max_signal_offsett);Redraw_window;;letscroll_horizontalt~by=letby=by*scroll_steptinset_cycle_offsett(get_cycle_offsett+by);Redraw_window;;letmove_cursor_verticalt~by=matchmax_viewtwith|None->(* this shouldn't happen. *)No_action|Some(_max_cycle,max_signal)->letselected_signal=change_selected_signal_index~bytinletsignal_offset=get_signal_offsettinifselected_signal<signal_offsetthenset_signal_offsettselected_signalelseOption.itermax_signal~f:(funmax_signal->ifselected_signal>max_signal-1then((* iterate down 1 signal at a time until the selected signal is in view.
Why is this difficult? Because the height of each signal differs. Another
approach would be to iterate backwards from the selected signal and count
the height relative to the window bounds. *)letrecfind_in_view()=letsignal_offset=get_signal_offsett+1inset_signal_offsettsignal_offset;matchmax_viewtwith|None->()|Some(_max_cycle,max_signal)->(matchmax_signalwith|None->()|Somemax_signal->ifmax_signal>=selected_signal+1then()elsefind_in_view())infind_in_view()));Redraw_window;;letmove_cursor_horizontalt~by=letby=by*scroll_steptinmatchmax_viewtwith|None->No_action|Some(max_cycle,_max_signal)->(matchget_cursor_offsettwith|None->(* No cursor - scroll screen *)set_cycle_offsett(get_cycle_offsett+by)|Somecursor_offset->(* move cursor and update scroll if needed *)set_cursor_offsett(cursor_offset+by);Option.iter(get_cursor_offsett)~f:(funcursor_offset->ifcursor_offset<get_cycle_offsettthenset_cycle_offsettcursor_offsetelseifcursor_offset>max_cyclethen(letdiff=cursor_offset-max_cycleinset_cycle_offsett(get_cycle_offsett+diff))));Redraw_window;;letcenter_on_cursort=Option.iter(get_cursor_offsett)~f:(funcursor_offset->set_cycle_offsett(cursor_offset-(cycles_displayedt/2)));Redraw_window;;letset_cursor_to_first_wave_on_screent=set_cursor_offsett(get_cycle_offsett);Redraw_window;;letset_selected_cursort~index=Cursors.set_selected(cursorst)index;Redraw_window;;letset_selected_cursor_and_centert~index=Cursors.set_selected(cursorst)index;center_on_cursort;;letremove_selected_cursort=Cursors.set_offset(cursorst)None;Redraw_window;;letmaybe_toggle_modulet=ifHierarchy.toggle_selected_module_if_present(hierarchyt)thenRedraw_windowelseNo_action;;letsave_ui_statet=Core.Out_channel.write_all(ui_state_filet)~data:(Ui_state.of_hierarchy(hierarchyt)|>[%sexp_of:Ui_state.t]|>Sexp.to_string_hum);Redraw_window;;letload_ui_statet=matchOption.try_with(fun()->Sexplib.Sexp.load_sexp(ui_state_filet)|>[%of_sexp:Ui_state.t])with|Somestate->Ui_state.apply_tostate(hierarchyt);Recreate_window|None->No_action;;letsearch_for_transitiont~direction:search_forwards_or_backwards=letcursor=cursorstinletwave_cursor=matchCursors.get_offsetcursorwith|Someoffset->Int.max0offset|None->0inletnew_cycle_offset=Hierarchy.move_to_delta_on_active_node~start_cycle:wave_cursor~search_forwards_or_backwards(hierarchyt)inmatchnew_cycle_offsetwith|None->No_action|Somenew_cycle_offset->letdistance=new_cycle_offset-wave_cursorinCursors.set_offsetcursor(Somenew_cycle_offset);(* If the wave we are trying to select is going to be out of bounds, we move the
window just enough so we can see it. *)set_cycle_offsett(get_cycle_offsett+distance);Redraw_window;;letcycle_wave_formatt=Hierarchy.cycle_wave_format(hierarchyt);Redraw_window;;letreset_wave_formatt=Hierarchy.reset_wave_format(hierarchyt);Redraw_window;;letcycle_colourt=Hierarchy.cycle_colour(hierarchyt);Redraw_window;;lettoggle_boldt=Hierarchy.toggle_bold(hierarchyt);Redraw_window;;moduleCommon=Make_common(structtypenonrect=X.tletquit_=Escapeletscroll_verticalt~by=set_signal_offsett(get_signal_offsett+by);Redraw_window;;end)letkey_functions={Key_actions.common=Common.key_functions;waveform_window={show_help;cursor_centered_zoom_in;cursor_centered_zoom_out;cursor_centered_zoom_in_fast;cursor_centered_zoom_out_fast;expand_signals_window;shrink_signals_window;expand_values_window;shrink_values_window;scroll_wave_to_beginning;scroll_wave_to_end;scroll_to_first_signal;scroll_to_last_signal;scroll_left=scroll_horizontal~by:(-1);scroll_right=scroll_horizontal~by:1;scroll_left_fast=scroll_horizontal~by:(-10);scroll_right_fast=scroll_horizontal~by:10;move_cursor_up=move_cursor_vertical~by:(-1);move_cursor_down=move_cursor_vertical~by:1;move_cursor_left=move_cursor_horizontal~by:(-1);move_cursor_right=move_cursor_horizontal~by:1;move_cursor_up_fast=move_cursor_vertical~by:(-10);move_cursor_down_fast=move_cursor_vertical~by:10;move_cursor_left_fast=move_cursor_horizontal~by:(-10);move_cursor_right_fast=move_cursor_horizontal~by:10;center_on_cursor;set_cursor_to_first_wave_on_screen;select_cursor_1=set_selected_cursor~index:0;select_cursor_2=set_selected_cursor~index:1;select_cursor_3=set_selected_cursor~index:2;select_cursor_4=set_selected_cursor~index:3;select_cursor_5=set_selected_cursor~index:4;select_cursor_1_and_center=set_selected_cursor_and_center~index:0;select_cursor_2_and_center=set_selected_cursor_and_center~index:1;select_cursor_3_and_center=set_selected_cursor_and_center~index:2;select_cursor_4_and_center=set_selected_cursor_and_center~index:3;select_cursor_5_and_center=set_selected_cursor_and_center~index:4;remove_selected_cursor;maybe_toggle_module;save_ui_state;load_ui_state;search_for_transition_forward=search_for_transition~direction:`Forwards;search_for_transition_backward=search_for_transition~direction:`Backwards;cycle_wave_format;reset_wave_format;cycle_colour;toggle_bold}};;letkey_tablekeys=Hashtbl.of_alist_exn(moduleKey_actions.Key)(Key_actions.zipkeyskey_functions|>Key_actions.to_list);;endendmoduleHelp_window=structmoduleMake(X:Help_api)=structincludeXopenEvent_responsemoduleCommon=Make_common(structtypenonrect=X.tletquit_=Show_waveformletscroll_verticalt~by=scroll_verticalt~by;Redraw_window;;end)letkey_tablekeys=Hashtbl.of_alist_exn(moduleKey_actions.Key)(Key_actions.Common.zipkeysCommon.key_functions|>Key_actions.Common.to_list);;endend