1 /////////////////////////////////////////////////////////////////////////////
2 // Purpose:     A console to help debug/use wxLua
3 // Author:      John Labenski, J Winwood
4 // Created:     14/11/2001
5 // Copyright:   (c) 2012 John Labenski
6 // Copyright:   (c) 2001-2002 Lomtick Software. All rights reserved.
7 // Licence:     wxWidgets licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 #include <wx/wxprec.h>
11 
12 #ifdef __STRICT_ANSI__
13 #undef __STRICT_ANSI__
14 #include <cstdio>
15 #define __STRICT_ANSI__
16 #else
17 #include <cstdio>
18 #endif
19 
20 #ifdef __BORLANDC__
21     #pragma hdrstop
22 #endif
23 
24 #ifndef WX_PRECOMP
25     #include <wx/wx.h>
26 #endif
27 
28 #if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__)
29     #include "art/wxlua.xpm"
30 #endif
31 
32 #include <wx/splitter.h>
33 #include <wx/toolbar.h>
34 #include <wx/filename.h>
35 #include <wx/numdlg.h>
36 #include <wx/artprov.h>
37 #include <wx/dynlib.h>
38 
39 #include "wxlua/wxlua.h"
40 #include "wxlconsole.h"
41 
42 // ----------------------------------------------------------------------------
43 // wxLuaConsole
44 // ----------------------------------------------------------------------------
45 
46 wxLuaConsole* wxLuaConsole::sm_wxluaConsole = NULL;
47 
BEGIN_EVENT_TABLE(wxLuaConsole,wxFrame)48 BEGIN_EVENT_TABLE(wxLuaConsole, wxFrame)
49     EVT_CLOSE (          wxLuaConsole::OnCloseWindow)
50     EVT_MENU  (wxID_ANY, wxLuaConsole::OnMenu)
51 END_EVENT_TABLE()
52 
53 wxLuaConsole::wxLuaConsole(wxWindow* parent, wxWindowID id, const wxString& title,
54                            const wxPoint& pos, const wxSize& size,
55                            long style, const wxString& name)
56              :wxFrame(parent, id, title, pos, size, style, name),
57               m_exit_when_closed(false)
58 {
59     m_max_lines = 2000;
60     m_saveFilename = wxT("log.txt");
61     m_saveFilename .Normalize();
62 
63     SetIcon(wxICON(LUA));
64 
65     wxToolBar* tb = CreateToolBar();
66 
67     tb->AddTool(wxID_NEW,    wxT("Clear window"), wxArtProvider::GetBitmap(wxART_NEW,       wxART_TOOLBAR), wxT("Clear console window"), wxITEM_NORMAL);
68     tb->AddTool(wxID_SAVEAS, wxT("Save output"),  wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_TOOLBAR), wxT("Save contents to file..."), wxITEM_NORMAL);
69     tb->AddTool(wxID_COPY,   wxT("Copy text"),    wxArtProvider::GetBitmap(wxART_COPY,      wxART_TOOLBAR), wxT("Copy contents to clipboard"), wxITEM_NORMAL);
70     tb->AddTool(ID_WXLUACONSOLE_SCROLLBACK_LINES, wxT("Scrollback"), wxArtProvider::GetBitmap(wxART_LIST_VIEW, wxART_TOOLBAR), wxT("Set the number of scrollback lines..."), wxITEM_NORMAL);
71     //tb->AddTool(ID_WXLUACONSOLE_BACKTRACE, wxT("Backtrace"), wxArtProvider::GetBitmap(wxART_QUESTION, wxART_TOOLBAR), wxT("Show the current Lua stack..."), wxITEM_NORMAL);
72     tb->Realize();
73 
74     m_textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
75                                 wxDefaultPosition, wxDefaultSize,
76                                 wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_DONTWRAP);
77     wxFont monoFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); // monospace
78     m_textCtrl->SetFont(monoFont);
79 
80     // Only set it to this if it wasn't already set, typically there will only be one of these.
81     if (sm_wxluaConsole == NULL)
82         sm_wxluaConsole = this;
83 }
84 
~wxLuaConsole()85 wxLuaConsole::~wxLuaConsole()
86 {
87     if (sm_wxluaConsole == this)
88         sm_wxluaConsole = NULL;
89 }
90 
Destroy()91 bool wxLuaConsole::Destroy()
92 {
93     if (sm_wxluaConsole == this)
94         sm_wxluaConsole = NULL;
95 
96     return wxFrame::Destroy();
97 }
98 
99 // static
GetConsole(bool create_on_demand)100 wxLuaConsole* wxLuaConsole::GetConsole(bool create_on_demand)
101 {
102     if (!create_on_demand || (sm_wxluaConsole != NULL))
103         return sm_wxluaConsole;
104 
105     new wxLuaConsole(NULL, ID_WXLUACONSOLE);
106     return sm_wxluaConsole;
107 }
108 
109 // static
HasConsole()110 bool wxLuaConsole::HasConsole()
111 {
112     return (sm_wxluaConsole != NULL) && !sm_wxluaConsole->IsBeingDeleted();
113 }
114 
115 
OnCloseWindow(wxCloseEvent &)116 void wxLuaConsole::OnCloseWindow(wxCloseEvent&)
117 {
118     // Must NULL the console so nobody will try to still use it.
119     if (sm_wxluaConsole == this)
120         sm_wxluaConsole = NULL;
121 
122     Destroy();
123     if (m_exit_when_closed)
124         wxExit();
125 }
126 
OnMenu(wxCommandEvent & event)127 void wxLuaConsole::OnMenu(wxCommandEvent& event)
128 {
129     switch (event.GetId())
130     {
131         case wxID_NEW :
132         {
133             m_textCtrl->Clear();
134             break;
135         }
136         case wxID_SAVEAS :
137         {
138             wxString filename = wxFileSelector(wxT("Select file to save output to"),
139                                                m_saveFilename.GetPath(),
140                                                m_saveFilename.GetFullName(),
141                                                wxT("txt"),
142                                                wxT("Text files (*.txt)|*.txt|All files (*.*)|*.*"),
143                                                wxFD_SAVE|wxFD_OVERWRITE_PROMPT,
144                                                this);
145 
146             if (!filename.IsEmpty())
147             {
148                 m_saveFilename = wxFileName(filename);
149                 m_textCtrl->SaveFile(filename);
150             }
151             break;
152         }
153         case wxID_COPY :
154         {
155             long from = 0, to = 0;
156             m_textCtrl->GetSelection(&from, &to);
157             m_textCtrl->SetSelection(-1, -1);
158             m_textCtrl->Copy();
159             m_textCtrl->SetSelection(from, to);
160             break;
161         }
162         case ID_WXLUACONSOLE_SCROLLBACK_LINES :
163         {
164             long lines = wxGetNumberFromUser(wxT("Set the number of printed lines to remember, 0 to 10000.\nSet to 0 for infinite history."),
165                                              wxT("Lines : "),
166                                              wxT("Set Number of Scrollback Lines"),
167                                              m_max_lines, 0, 10000,
168                                              this);
169             if (lines >= 0)
170                 SetMaxLines(lines);
171 
172             break;
173         }
174         case ID_WXLUACONSOLE_BACKTRACE :
175         {
176             if (m_luaState.IsOk())
177             {
178                 DisplayStack(m_luaState);
179                 //wxLuaStackDialog dlg(m_wxlState, this);
180                 //dlg.ShowModal();
181             }
182 
183             break;
184         }
185         default : break;
186     }
187 }
188 
AppendText(const wxString & msg)189 void wxLuaConsole::AppendText(const wxString& msg)
190 {
191     m_textCtrl->Freeze();
192 
193     // Probably the best we can do to maintain the cursor pos while appending
194     // The wxStyledTextCtrl can do a much better job...
195     long pos          = m_textCtrl->GetInsertionPoint();
196     int  num_lines    = m_textCtrl->GetNumberOfLines();
197     long pos_near_end = m_textCtrl->XYToPosition(0, wxMax(0, num_lines - 5));
198     bool is_near_end  = (pos >= pos_near_end);
199 
200     m_textCtrl->AppendText(msg);
201     m_textCtrl->SetInsertionPoint(is_near_end ? m_textCtrl->GetLastPosition() : pos);
202 
203     m_textCtrl->Thaw();
204 
205     SetMaxLines(m_max_lines);
206 }
AppendTextWithAttr(const wxString & msg,const wxTextAttr & attr)207 void wxLuaConsole::AppendTextWithAttr(const wxString& msg, const wxTextAttr& attr)
208 {
209     wxTextAttr oldAttr = m_textCtrl->GetDefaultStyle();
210 
211     m_textCtrl->SetDefaultStyle(attr);
212     AppendText(msg);
213     m_textCtrl->SetDefaultStyle(oldAttr);
214 
215     SetMaxLines(m_max_lines);
216 }
217 
SetMaxLines(int max_lines)218 bool wxLuaConsole::SetMaxLines(int max_lines)
219 {
220     m_max_lines = max_lines;
221 
222     int num_lines = m_textCtrl->GetNumberOfLines();
223     if ((m_max_lines <= 0) || (num_lines < m_max_lines))
224         return false;
225 
226     long pos = m_textCtrl->GetInsertionPoint();
227     long remove_pos = m_textCtrl->XYToPosition(0, num_lines - m_max_lines);
228 
229     m_textCtrl->Freeze();
230     m_textCtrl->Remove(0, remove_pos);
231     m_textCtrl->SetInsertionPoint(wxMax(0, pos-remove_pos));
232     m_textCtrl->ShowPosition(wxMax(0, pos-remove_pos));
233     m_textCtrl->Thaw();
234 
235     return true;
236 }
237 
DisplayStack(const wxLuaState & wxlState)238 void wxLuaConsole::DisplayStack(const wxLuaState& wxlState)
239 {
240     wxCHECK_RET(wxlState.Ok(), wxT("Invalid wxLuaState"));
241     int       nIndex   = 0;
242     lua_Debug luaDebug = INIT_LUA_DEBUG;
243     wxString  buffer;
244 
245     lua_State* L = wxlState.GetLuaState();
246 
247     while (lua_getstack(L, nIndex, &luaDebug) != 0)
248     {
249         if (lua_getinfo(L, "Sln", &luaDebug))
250         {
251             wxString what    (luaDebug.what     ? lua2wx(luaDebug.what)     : wxString(wxT("?")));
252             wxString nameWhat(luaDebug.namewhat ? lua2wx(luaDebug.namewhat) : wxString(wxT("?")));
253             wxString name    (luaDebug.name     ? lua2wx(luaDebug.name)     : wxString(wxT("?")));
254 
255             buffer += wxString::Format(wxT("[%d] %s '%s' '%s' (line %d)\n    Line %d src='%s'\n"),
256                                        nIndex, what.c_str(), nameWhat.c_str(), name.c_str(), luaDebug.linedefined,
257                                        luaDebug.currentline, lua2wx(luaDebug.short_src).c_str());
258         }
259         nIndex++;
260     }
261 
262     if (!buffer.empty())
263     {
264         AppendText(wxT("\n-----------------------------------------------------------")
265                    wxT("\n- Backtrace")
266                    wxT("\n-----------------------------------------------------------\n") +
267                    buffer +
268                    wxT("\n-----------------------------------------------------------\n\n"));
269     }
270 }
271 
272 // ---------------------------------------------------------------------------
273 // Functions
274 // ---------------------------------------------------------------------------
275 
276 #ifdef __WXMSW__
277 
278 #include <iostream>
279 
280 #ifndef wxDL_INIT_FUNC // not in wx < 2.9
281     #define wxDL_INIT_FUNC(pfx, name, dynlib) \
282         pfx ## name = (name ## _t)(dynlib).RawGetSymbol(wxT(#name))
283 #endif // wxDL_INIT_FUNC
284 
285 // Code from http://dslweb.nwnexus.com/~ast/dload/guicon.htm
286 // Andrew Tucker, no license, assumed to be public domain.
wxlua_RedirectIOToDosConsole(bool alloc_new_if_needed,short max_console_lines)287 void wxlua_RedirectIOToDosConsole(bool alloc_new_if_needed, short max_console_lines)
288 {
289     int  hConHandle = 0;
290     wxIntPtr lStdHandle = 0;
291     CONSOLE_SCREEN_BUFFER_INFO coninfo;
292     memset(&coninfo, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
293     FILE *fp = 0; // we don't close this, let the OS close it when the app exits
294 
295     wxDynamicLibrary kernel;
296     // Dynamically load kernel32 because AttachConsole() is not supported pre-XP
297     BOOL attached_ok = kernel.Load(wxT("kernel32.dll"));
298 
299     if (attached_ok)
300     {
301         // Try to attach to the parent process if it's a console, i.e. we're run from a DOS prompt.
302         // The code below is equivalent to calling this code:
303         //   BOOL attached_ok = AttachConsole( ATTACH_PARENT_PROCESS );
304 
305         typedef BOOL (WINAPI *AttachConsole_t)(DWORD dwProcessId);
306         AttachConsole_t wxDL_INIT_FUNC(pfn, AttachConsole, kernel);
307 
308         if (pfnAttachConsole)
309             attached_ok = pfnAttachConsole( ATTACH_PARENT_PROCESS );
310         else
311             attached_ok = 0;
312     }
313 
314     if (attached_ok == 0) // failed attaching
315     {
316         // we tried to attach, but failed don't alloc a new one
317         if (!alloc_new_if_needed)
318             return;
319 
320         // Unable to attach, allocate a console for this app
321         AllocConsole();
322     }
323 
324     // set the screen buffer to be big enough to let us scroll text
325     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
326     coninfo.dwSize.Y = (WORD)max_console_lines;
327     SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
328     // redirect unbuffered STDOUT to the console
329     lStdHandle = (wxIntPtr)GetStdHandle(STD_OUTPUT_HANDLE);
330     hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
331     fp = _fdopen( hConHandle, "w" );
332     *stdout = *fp;
333     setvbuf( stdout, NULL, _IONBF, 0 );
334     // redirect unbuffered STDIN to the console
335     lStdHandle = (wxIntPtr)GetStdHandle(STD_INPUT_HANDLE);
336     hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
337     fp = _fdopen( hConHandle, "r" );
338     *stdin = *fp;
339     setvbuf( stdin, NULL, _IONBF, 0 );
340     // redirect unbuffered STDERR to the console
341     lStdHandle = (wxIntPtr)GetStdHandle(STD_ERROR_HANDLE);
342     hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
343     fp = _fdopen( hConHandle, "w" );
344     *stderr = *fp;
345     setvbuf( stderr, NULL, _IONBF, 0 );
346     // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
347     // point to console as well
348     std::ios::sync_with_stdio();
349 }
350 
351 #else // !__WXMSW__
352 
wxlua_RedirectIOToDosConsole(bool,short)353 void wxlua_RedirectIOToDosConsole(bool , short )
354 {
355     // Nothing to do since these OSes already do the right thing.
356 }
357 
358 #endif // __WXMSW__
359 
360 
361