2022-05-03 10:07:22 +00:00
//
// xemu User Interface
//
// Copyright (C) 2020-2022 Matt Borgerson
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
# include "monitor.hh"
# include "imgui.h"
# include "misc.hh"
# include "font-manager.hh"
// Portable helpers
static int Stricmp ( const char * str1 , const char * str2 ) { int d ; while ( ( d = toupper ( * str2 ) - toupper ( * str1 ) ) = = 0 & & * str1 ) { str1 + + ; str2 + + ; } return d ; }
static char * Strdup ( const char * str ) { size_t len = strlen ( str ) + 1 ; void * buf = malloc ( len ) ; IM_ASSERT ( buf ) ; return ( char * ) memcpy ( buf , ( const void * ) str , len ) ; }
static void Strtrim ( char * str ) { char * str_end = str + strlen ( str ) ; while ( str_end > str & & str_end [ - 1 ] = = ' ' ) str_end - - ; * str_end = 0 ; }
MonitorWindow : : MonitorWindow ( )
{
is_open = false ;
memset ( InputBuf , 0 , sizeof ( InputBuf ) ) ;
HistoryPos = - 1 ;
AutoScroll = true ;
ScrollToBottom = false ;
}
MonitorWindow : : ~ MonitorWindow ( )
{
}
void MonitorWindow : : Draw ( )
{
if ( ! is_open ) return ;
int style_pop_cnt = PushWindowTransparencySettings ( true ) + 1 ;
ImGui : : PushStyleColor ( ImGuiCol_ChildBg , ImU32 ( ImColor ( 0 , 0 , 0 , 80 ) ) ) ;
ImGuiIO & io = ImGui : : GetIO ( ) ;
ImVec2 window_pos = ImVec2 ( 0 , io . DisplaySize . y / 2 ) ;
ImGui : : SetNextWindowPos ( window_pos , ImGuiCond_Appearing ) ;
ImGui : : SetNextWindowSize ( ImVec2 ( io . DisplaySize . x , io . DisplaySize . y / 2 ) , ImGuiCond_Appearing ) ;
if ( ImGui : : Begin ( " Monitor " , & is_open , ImGuiWindowFlags_NoCollapse ) ) {
const float footer_height_to_reserve = ImGui : : GetStyle ( ) . ItemSpacing . y + ImGui : : GetFrameHeightWithSpacing ( ) ; // 1 separator, 1 input text
ImGui : : BeginChild ( " ScrollingRegion " , ImVec2 ( 0 , - footer_height_to_reserve ) , false , ImGuiWindowFlags_HorizontalScrollbar ) ; // Leave room for 1 separator + 1 InputText
ImGui : : PushStyleVar ( ImGuiStyleVar_ItemSpacing , ImVec2 ( 4 , 1 ) ) ; // Tighten spacing
ImGui : : PushFont ( g_font_mgr . m_fixed_width_font ) ;
2022-06-04 06:11:12 +00:00
// FIXME: Replace scroll to bottom hack when https://github.com/ocornut/imgui/issues/1972 is resolved.
// ImGui does not provide any mechanism to adjust scrolling in an InputTextMultiline and does not
// provide any other widget that allows for selectable text.
char * buffer = xemu_get_monitor_buffer ( ) ;
size_t buffer_len = strlen ( buffer ) ;
// Calculating the precise size will cause an unnecessary vertical scrollbar in the InputTextMultiline.
int num_newlines = 2 ;
const char * start = buffer ;
while ( start ) {
start = strchr ( start , ' \n ' ) ;
if ( start ) {
+ + num_newlines ;
+ + start ;
}
}
float input_height = fmax ( ImGui : : GetWindowHeight ( ) ,
g_font_mgr . m_fixed_width_font - > FontSize * num_newlines ) ;
ImGui : : PushID ( " #MonitorOutput " ) ;
ImGui : : InputTextMultiline ( " " ,
buffer ,
buffer_len ,
ImVec2 ( - 1.0f , input_height ) ,
ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoUndoRedo ) ;
ImGui : : PopID ( ) ;
2022-05-03 10:07:22 +00:00
ImGui : : PopFont ( ) ;
if ( ScrollToBottom | | ( AutoScroll & & ImGui : : GetScrollY ( ) > = ImGui : : GetScrollMaxY ( ) ) ) {
ImGui : : SetScrollHereY ( 1.0f ) ;
}
ScrollToBottom = false ;
ImGui : : PopStyleVar ( ) ;
ImGui : : EndChild ( ) ;
ImGui : : Separator ( ) ;
// Command-line
bool reclaim_focus = ImGui : : IsWindowAppearing ( ) ;
ImGui : : SetNextItemWidth ( - 1 ) ;
ImGui : : PushFont ( g_font_mgr . m_fixed_width_font ) ;
if ( ImGui : : InputText ( " #commandline " , InputBuf , IM_ARRAYSIZE ( InputBuf ) , ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory , & TextEditCallbackStub , ( void * ) this ) ) {
char * s = InputBuf ;
Strtrim ( s ) ;
if ( s [ 0 ] )
ExecCommand ( s ) ;
strcpy ( s , " " ) ;
reclaim_focus = true ;
}
ImGui : : PopFont ( ) ;
// Auto-focus on window apparition
ImGui : : SetItemDefaultFocus ( ) ;
if ( reclaim_focus ) {
ImGui : : SetKeyboardFocusHere ( - 1 ) ; // Auto focus previous widget
}
}
ImGui : : End ( ) ;
ImGui : : PopStyleColor ( style_pop_cnt ) ;
}
void MonitorWindow : : ToggleOpen ( void )
{
is_open = ! is_open ;
}
void MonitorWindow : : ExecCommand ( const char * command_line )
{
xemu_run_monitor_command ( command_line ) ;
// Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal.
HistoryPos = - 1 ;
for ( int i = History . Size - 1 ; i > = 0 ; i - - )
if ( Stricmp ( History [ i ] , command_line ) = = 0 )
{
free ( History [ i ] ) ;
History . erase ( History . begin ( ) + i ) ;
break ;
}
History . push_back ( Strdup ( command_line ) ) ;
// On commad input, we scroll to bottom even if AutoScroll==false
ScrollToBottom = true ;
}
int MonitorWindow : : TextEditCallbackStub ( ImGuiInputTextCallbackData * data ) // In C++11 you are better off using lambdas for this sort of forwarding callbacks
{
MonitorWindow * console = ( MonitorWindow * ) data - > UserData ;
return console - > TextEditCallback ( data ) ;
}
int MonitorWindow : : TextEditCallback ( ImGuiInputTextCallbackData * data )
{
switch ( data - > EventFlag )
{
case ImGuiInputTextFlags_CallbackHistory :
{
// Example of HISTORY
const int prev_history_pos = HistoryPos ;
if ( data - > EventKey = = ImGuiKey_UpArrow )
{
if ( HistoryPos = = - 1 )
HistoryPos = History . Size - 1 ;
else if ( HistoryPos > 0 )
HistoryPos - - ;
}
else if ( data - > EventKey = = ImGuiKey_DownArrow )
{
if ( HistoryPos ! = - 1 )
if ( + + HistoryPos > = History . Size )
HistoryPos = - 1 ;
}
// A better implementation would preserve the data on the current input line along with cursor position.
if ( prev_history_pos ! = HistoryPos )
{
const char * history_str = ( HistoryPos > = 0 ) ? History [ HistoryPos ] : " " ;
data - > DeleteChars ( 0 , data - > BufTextLen ) ;
data - > InsertChars ( 0 , history_str ) ;
}
}
}
return 0 ;
}
MonitorWindow monitor_window ;