1/////////////////////////////////////////////////////////////////////////////
2// Name:        emulator.cpp
3// Purpose:     Emulator wxWidgets sample
4// Author:      Julian Smart
5// Modified by:
6// Created:     04/01/98
7// RCS-ID:      $Id: emulator.cpp 35650 2005-09-23 12:56:45Z MR $
8// Copyright:   (c) Julian Smart
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx/wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27// for all others, include the necessary headers (this file is usually all you
28// need because it includes almost all "standard" wxWidgets headers)
29#ifndef WX_PRECOMP
30    #include "wx/wx.h"
31#endif
32
33#include "wx/confbase.h"
34#include "wx/fileconf.h"
35#include "wx/cmdline.h"
36#include "wx/image.h"
37#include "wx/file.h"
38
39#ifdef __WXX11__
40#include "wx/x11/reparent.h"
41#endif
42
43#include "emulator.h"
44
45// ----------------------------------------------------------------------------
46// resources
47// ----------------------------------------------------------------------------
48
49// the application icon (under Windows and OS/2 it is in resources)
50#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) || defined(__WXX11__)
51    #include "emulator.xpm"
52#endif
53
54// ----------------------------------------------------------------------------
55// event tables and other macros for wxWidgets
56// ----------------------------------------------------------------------------
57
58// the event tables connect the wxWidgets events with the functions (event
59// handlers) which process them. It can be also done at run-time, but for the
60// simple menu events like this the static method is much simpler.
61BEGIN_EVENT_TABLE(wxEmulatorFrame, wxFrame)
62    EVT_MENU(Emulator_Quit,  wxEmulatorFrame::OnQuit)
63    EVT_MENU(Emulator_About, wxEmulatorFrame::OnAbout)
64    EVT_CLOSE(wxEmulatorFrame::OnCloseWindow)
65END_EVENT_TABLE()
66
67// Create a new application object: this macro will allow wxWidgets to create
68// the application object during program execution (it's better than using a
69// static object for many reasons) and also declares the accessor function
70// wxGetApp() which will return the reference of the right type (i.e. wxEmulatorApp and
71// not wxApp)
72IMPLEMENT_APP(wxEmulatorApp)
73
74static const wxCmdLineEntryDesc sg_cmdLineDesc[] =
75{
76    { wxCMD_LINE_OPTION, _T("u"), _T("use-display"),   _T("display number to use (default 100)"), (wxCmdLineParamType)0, 0 },
77
78    { wxCMD_LINE_SWITCH, _T("h"), _T("help"),   _T("displays help on the command line parameters"), (wxCmdLineParamType)0, 0 },
79    { wxCMD_LINE_SWITCH, _T("v"), _T("version"),    _T("print version"), (wxCmdLineParamType)0, 0 },
80
81    { wxCMD_LINE_PARAM,  NULL, NULL, _T("config file 1"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
82
83    { wxCMD_LINE_NONE, NULL, NULL, NULL, (wxCmdLineParamType)0, 0 }
84};
85
86
87// ============================================================================
88// implementation
89// ============================================================================
90
91// ----------------------------------------------------------------------------
92// the application class
93// ----------------------------------------------------------------------------
94
95wxEmulatorApp::wxEmulatorApp()
96{
97    m_xnestWindow = NULL;
98    m_containerWindow = NULL;
99    m_displayNumber = wxT("100");
100    m_xnestPID = 0;
101
102}
103
104// 'Main program' equivalent: the program execution "starts" here
105bool wxEmulatorApp::OnInit()
106{
107#if wxUSE_LOG
108    wxLog::SetTimestamp(NULL);
109#endif // wxUSE_LOG
110    wxInitAllImageHandlers();
111
112    wxString currentDir = wxGetCwd();
113
114    // Use argv to get current app directory
115    m_appDir = wxFindAppPath(argv[0], currentDir, wxT("WXEMUDIR"));
116
117    // If the development version, go up a directory.
118#ifdef __WXMSW__
119    if ((m_appDir.Right(5).CmpNoCase(_T("DEBUG")) == 0) ||
120        (m_appDir.Right(11).CmpNoCase(_T("DEBUGSTABLE")) == 0) ||
121        (m_appDir.Right(7).CmpNoCase(_T("RELEASE")) == 0) ||
122        (m_appDir.Right(13).CmpNoCase(_T("RELEASESTABLE")) == 0)
123        )
124        m_appDir = wxPathOnly(m_appDir);
125#endif
126
127    // Parse the command-line parameters and options
128    wxCmdLineParser parser(sg_cmdLineDesc, argc, argv);
129    int res;
130    {
131        wxLogNull log;
132        res = parser.Parse();
133    }
134    if (res == -1 || res > 0 || parser.Found(wxT("h")))
135    {
136#ifdef __X__
137        wxLog::SetActiveTarget(new wxLogStderr);
138#endif
139        parser.Usage();
140        return false;
141    }
142    if (parser.Found(wxT("v")))
143    {
144#ifdef __X__
145        wxLog::SetActiveTarget(new wxLogStderr);
146#endif
147        wxString msg;
148        msg.Printf(wxT("wxWidgets PDA Emulator (c) Julian Smart, 2002 Version %.2f, %s"), wxEMULATOR_VERSION, __DATE__);
149        wxLogMessage(msg);
150        return false;
151    }
152    if (parser.Found(wxT("u"), & m_displayNumber))
153    {
154        // Should only be number, so strip out anything before
155        // and including a : character
156        if (m_displayNumber.Find(wxT(':')) != -1)
157        {
158            m_displayNumber = m_displayNumber.AfterFirst(wxT(':'));
159        }
160    }
161    if (parser.GetParamCount() == 0)
162    {
163        m_emulatorInfo.m_emulatorFilename = wxT("default.wxe");
164    }
165    else if (parser.GetParamCount() > 0)
166    {
167        m_emulatorInfo.m_emulatorFilename = parser.GetParam(0);
168    }
169
170    // Load the emulation info
171    if (!LoadEmulator(m_appDir))
172    {
173        //wxMessageBox(wxT("Sorry, could not load this emulator. Please check bitmaps are valid."));
174        return false;
175    }
176
177    // create the main application window
178    wxEmulatorFrame *frame = new wxEmulatorFrame(_T("wxEmulator"),
179                                 wxPoint(50, 50), wxSize(450, 340));
180
181#if wxUSE_STATUSBAR
182    frame->SetStatusText(m_emulatorInfo.m_emulatorTitle, 0);
183
184    wxString sizeStr;
185    sizeStr.Printf(wxT("Screen: %dx%d"), (int) m_emulatorInfo.m_emulatorScreenSize.x,
186            (int) m_emulatorInfo.m_emulatorScreenSize.y);
187    frame->SetStatusText(sizeStr, 1);
188#endif // wxUSE_STATUSBAR
189
190    m_containerWindow = new wxEmulatorContainer(frame, wxID_ANY);
191
192    frame->SetClientSize(m_emulatorInfo.m_emulatorDeviceSize.x,
193                         m_emulatorInfo.m_emulatorDeviceSize.y);
194
195    // and show it (the frames, unlike simple controls, are not shown when
196    // created initially)
197    frame->Show(true);
198
199#ifdef __WXX11__
200    m_xnestWindow = new wxAdoptedWindow;
201
202    wxString cmd;
203    cmd.Printf(wxT("Xnest :%s -geometry %dx%d"),
204        m_displayNumber.c_str(),
205        (int) m_emulatorInfo.m_emulatorScreenSize.x,
206        (int) m_emulatorInfo.m_emulatorScreenSize.y);
207
208    // Asynchronously executes Xnest
209    m_xnestPID = wxExecute(cmd);
210    if (0 == m_xnestPID)
211    {
212        frame->Destroy();
213        wxMessageBox(wxT("Sorry, could not run Xnest. Please check your PATH."));
214        return false;
215    }
216
217    wxReparenter reparenter;
218    if (!reparenter.WaitAndReparent(m_containerWindow, m_xnestWindow, wxT("Xnest")))
219    {
220        wxMessageBox(wxT("Sorry, could not reparent Xnest.."));
221        frame->Destroy();
222        return false;
223    }
224
225#endif
226    m_containerWindow->DoResize();
227
228    // success: wxApp::OnRun() will be called which will enter the main message
229    // loop and the application will run. If we returned false here, the
230    // application would exit immediately.
231    return true;
232}
233
234// Prepend the current program directory to the name
235wxString wxEmulatorApp::GetFullAppPath(const wxString& filename) const
236{
237    wxString path(m_appDir);
238    if (path.Last() != '\\' && path.Last() != '/' && filename[0] != '\\' && filename[0] != '/')
239#ifdef __X__
240        path += '/';
241#else
242        path += '\\';
243#endif
244    path += filename;
245
246    return path;
247}
248
249
250// Load the specified emulator.
251// For now, hard-wired. TODO: make this configurable
252bool wxEmulatorApp::LoadEmulator(const wxString& appDir)
253{
254    // Load config file and bitmaps
255    return m_emulatorInfo.Load(appDir);
256}
257
258// ----------------------------------------------------------------------------
259// main frame
260// ----------------------------------------------------------------------------
261
262// frame constructor
263wxEmulatorFrame::wxEmulatorFrame(const wxString& title,
264    const wxPoint& pos, const wxSize& size)
265    : wxFrame(NULL, wxID_ANY, title, pos, size)
266{
267    // set the frame icon
268    SetIcon(wxICON(emulator));
269
270#if wxUSE_MENUS
271    // create a menu bar
272    wxMenu *menuFile = new wxMenu;
273
274    // the "About" item should be in the help menu
275    wxMenu *helpMenu = new wxMenu;
276    helpMenu->Append(Emulator_About, _T("&About...\tF1"), _T("Show about dialog"));
277
278    menuFile->Append(Emulator_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
279
280    // now append the freshly created menu to the menu bar...
281    wxMenuBar *menuBar = new wxMenuBar();
282    menuBar->Append(menuFile, _T("&File"));
283    menuBar->Append(helpMenu, _T("&Help"));
284
285    // ... and attach this menu bar to the frame
286    SetMenuBar(menuBar);
287#endif // wxUSE_MENUS
288
289#if wxUSE_STATUSBAR
290    // create a status bar just for fun (by default with 1 pane only)
291    CreateStatusBar(2);
292#endif // wxUSE_STATUSBAR
293}
294
295
296// event handlers
297
298void wxEmulatorFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
299{
300    // true is to force the frame to close
301    Close(true);
302}
303
304void wxEmulatorFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
305{
306    wxString msg;
307    msg.Printf( _T("wxEmulator is an environment for testing embedded X11 apps.\n"));
308
309    wxMessageBox(msg, _T("About wxEmulator"), wxOK | wxICON_INFORMATION, this);
310}
311
312void wxEmulatorFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
313{
314#ifdef __WXX11__
315    if (wxGetApp().m_xnestWindow)
316    {
317        wxGetApp().m_xnestWindow->SetHandle((WXWindow) NULL);
318    }
319#endif
320    this->Destroy();
321    if (wxGetApp().m_xnestPID > 0)
322    {
323        wxKill(wxGetApp().m_xnestPID);
324        wxGetApp().m_xnestPID = 0;
325    }
326}
327
328IMPLEMENT_CLASS(wxEmulatorContainer, wxWindow)
329
330BEGIN_EVENT_TABLE(wxEmulatorContainer, wxWindow)
331    EVT_SIZE(wxEmulatorContainer::OnSize)
332    EVT_PAINT(wxEmulatorContainer::OnPaint)
333    EVT_ERASE_BACKGROUND(wxEmulatorContainer::OnEraseBackground)
334END_EVENT_TABLE()
335
336wxEmulatorContainer::wxEmulatorContainer(wxWindow* parent, wxWindowID id):
337    wxWindow(parent, id, wxDefaultPosition, wxDefaultSize)
338{
339}
340
341void wxEmulatorContainer::OnSize(wxSizeEvent& WXUNUSED(event))
342{
343    DoResize();
344}
345
346void wxEmulatorContainer::DoResize()
347{
348    wxSize sz = GetClientSize();
349    if (wxGetApp().m_xnestWindow
350#ifdef __WXX11__
351        && wxGetApp().m_xnestWindow->GetMainWindow()
352#endif
353        )
354    {
355        int deviceWidth = wxGetApp().m_emulatorInfo.m_emulatorDeviceSize.x;
356        int deviceHeight = wxGetApp().m_emulatorInfo.m_emulatorDeviceSize.y;
357
358        int x = wxMax(0, (int) ((sz.x - deviceWidth)/2.0));
359        int y = wxMax(0, (int) ((sz.y - deviceHeight)/2.0));
360
361        x += wxGetApp().m_emulatorInfo.m_emulatorScreenPosition.x;
362        y += wxGetApp().m_emulatorInfo.m_emulatorScreenPosition.y;
363
364        wxGetApp().m_xnestWindow->Move(x, y);
365    }
366    Refresh();
367}
368
369void wxEmulatorContainer::OnPaint(wxPaintEvent& WXUNUSED(event))
370{
371    wxPaintDC dc(this);
372
373    wxSize sz = GetClientSize();
374    if (wxGetApp().m_emulatorInfo.m_emulatorBackgroundBitmap.Ok())
375    {
376        int deviceWidth = wxGetApp().m_emulatorInfo.m_emulatorDeviceSize.x;
377        int deviceHeight = wxGetApp().m_emulatorInfo.m_emulatorDeviceSize.y;
378
379        int x = wxMax(0, (int) ((sz.x - deviceWidth)/2.0));
380        int y = wxMax(0, (int) ((sz.y - deviceHeight)/2.0));
381
382        dc.DrawBitmap(wxGetApp().m_emulatorInfo.m_emulatorBackgroundBitmap, x, y);
383    }
384}
385
386void wxEmulatorContainer::OnEraseBackground(wxEraseEvent& event)
387{
388    wxDC* dc wxDUMMY_INITIALIZE(NULL);
389
390    if (event.GetDC())
391    {
392        dc = event.GetDC();
393    }
394    else
395    {
396        dc = new wxClientDC(this);
397    }
398
399    dc->SetBackground(wxBrush(wxGetApp().m_emulatorInfo.m_emulatorBackgroundColour, wxSOLID));
400    dc->Clear();
401
402    if (!event.GetDC())
403        delete dc;
404}
405
406// Information about the emulator decorations
407
408void wxEmulatorInfo::Copy(const wxEmulatorInfo& info)
409{
410    m_emulatorFilename = info.m_emulatorFilename;
411    m_emulatorTitle = info.m_emulatorTitle;
412    m_emulatorDescription = info.m_emulatorDescription;
413    m_emulatorScreenPosition = info.m_emulatorScreenPosition;
414    m_emulatorScreenSize = info.m_emulatorScreenSize;
415    m_emulatorBackgroundBitmap = info.m_emulatorBackgroundBitmap;
416    m_emulatorBackgroundBitmapName = info.m_emulatorBackgroundBitmapName;
417    m_emulatorBackgroundColour = info.m_emulatorBackgroundColour;
418    m_emulatorDeviceSize = info.m_emulatorDeviceSize;
419}
420
421// Initialisation
422void wxEmulatorInfo::Init()
423{
424    m_emulatorDeviceSize = wxSize(260, 340);
425    m_emulatorScreenSize = wxSize(240, 320);
426}
427
428// Loads bitmaps
429bool wxEmulatorInfo::Load(const wxString& appDir)
430{
431    // Try to find absolute path
432    wxString absoluteConfigPath = m_emulatorFilename;
433    if ( !::wxIsAbsolutePath(absoluteConfigPath) )
434    {
435        wxString currDir = wxGetCwd();
436        absoluteConfigPath = currDir + wxString(wxFILE_SEP_PATH) + m_emulatorFilename;
437        if ( !wxFile::Exists(absoluteConfigPath) )
438        {
439            absoluteConfigPath = appDir + wxString(wxFILE_SEP_PATH)
440                + m_emulatorFilename;
441        }
442    }
443
444    if ( !wxFile::Exists(absoluteConfigPath) )
445    {
446        wxString str;
447        str.Printf( wxT("Could not find config file %s"),
448            absoluteConfigPath.c_str() );
449
450        wxMessageBox(str);
451        return false;
452    }
453
454    wxString rootPath = wxPathOnly(absoluteConfigPath);
455
456    {
457        wxFileConfig config(wxT("wxEmulator"), wxT("wxWidgets"),
458            absoluteConfigPath, wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
459
460        config.Read(wxT("/General/title"), & m_emulatorTitle);
461        config.Read(wxT("/General/description"), & m_emulatorDescription);
462        config.Read(wxT("/General/backgroundBitmap"), & m_emulatorBackgroundBitmapName);
463
464        wxString colString;
465        if (config.Read(wxT("/General/backgroundColour"), & colString) ||
466            config.Read(wxT("/General/backgroundColor"), & colString)
467           )
468        {
469            m_emulatorBackgroundColour = wxHexStringToColour(colString);
470        }
471
472        int x = 0, y = 0, w = 0, h = 0, dw = 0, dh = 0;
473        config.Read(wxT("/General/screenX"), & x);
474        config.Read(wxT("/General/screenY"), & y);
475        config.Read(wxT("/General/screenWidth"), & w);
476        config.Read(wxT("/General/screenHeight"), & h);
477        if (config.Read(wxT("/General/deviceWidth"), & dw) && config.Read(wxT("/General/deviceHeight"), & dh))
478        {
479            m_emulatorDeviceSize = wxSize(dw, dh);
480        }
481
482        m_emulatorScreenPosition = wxPoint(x, y);
483        m_emulatorScreenSize = wxSize(w, h);
484    }
485
486    if (!m_emulatorBackgroundBitmapName.empty())
487    {
488        wxString absoluteBackgroundBitmapName = rootPath + wxString(wxFILE_SEP_PATH) + m_emulatorBackgroundBitmapName;
489        if ( !wxFile::Exists(absoluteBackgroundBitmapName) )
490        {
491            wxString str;
492            str.Printf( wxT("Could not find bitmap %s"),
493                absoluteBackgroundBitmapName.c_str() );
494            wxMessageBox(str);
495            return false;
496        }
497
498        wxBitmapType type = wxDetermineImageType(m_emulatorBackgroundBitmapName);
499        if (type == wxBITMAP_TYPE_INVALID)
500            return false;
501
502        if (!m_emulatorBackgroundBitmap.LoadFile(m_emulatorBackgroundBitmapName, type))
503        {
504            wxString str;
505            str.Printf( wxT("Could not load bitmap file %s"),
506                m_emulatorBackgroundBitmapName.c_str() );
507            wxMessageBox(str);
508            return false;
509        }
510
511        m_emulatorDeviceSize = wxSize(m_emulatorBackgroundBitmap.GetWidth(),
512            m_emulatorBackgroundBitmap.GetHeight());
513    }
514    return true;
515}
516
517// Returns the image type, or -1, determined from the extension.
518wxBitmapType wxDetermineImageType(const wxString& filename)
519{
520    wxString path, name, ext;
521
522    wxSplitPath(filename, & path, & name, & ext);
523
524    ext.MakeLower();
525    if (ext == _T("jpg") || ext == _T("jpeg"))
526        return wxBITMAP_TYPE_JPEG;
527    if (ext == _T("gif"))
528        return wxBITMAP_TYPE_GIF;
529    if (ext == _T("bmp"))
530        return wxBITMAP_TYPE_BMP;
531    if (ext == _T("png"))
532        return wxBITMAP_TYPE_PNG;
533    if (ext == _T("pcx"))
534        return wxBITMAP_TYPE_PCX;
535    if (ext == _T("tif") || ext == _T("tiff"))
536        return wxBITMAP_TYPE_TIF;
537
538    return wxBITMAP_TYPE_INVALID;
539}
540
541// Convert a colour to a 6-digit hex string
542wxString wxColourToHexString(const wxColour& col)
543{
544    wxString hex;
545
546    hex += wxDecToHex(col.Red());
547    hex += wxDecToHex(col.Green());
548    hex += wxDecToHex(col.Blue());
549
550    return hex;
551}
552
553// Convert 6-digit hex string to a colour
554wxColour wxHexStringToColour(const wxString& hex)
555{
556    unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
557    unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
558    unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
559
560    return wxColour(r, g, b);
561}
562
563// Find the absolute path where this application has been run from.
564// argv0 is wxTheApp->argv[0]
565// cwd is the current working directory (at startup)
566// appVariableName is the name of a variable containing the directory for this app, e.g.
567// MYAPPDIR. This is checked first.
568
569wxString wxFindAppPath(const wxString& argv0, const wxString& cwd, const wxString& appVariableName)
570{
571    wxString str;
572
573    // Try appVariableName
574    if (!appVariableName.empty())
575    {
576        str = wxGetenv(appVariableName);
577        if (!str.empty())
578            return str;
579    }
580
581    if (wxIsAbsolutePath(argv0))
582        return wxPathOnly(argv0);
583    else
584    {
585        // Is it a relative path?
586        wxString currentDir(cwd);
587        if (!wxEndsWithPathSeparator(currentDir))
588            currentDir += wxFILE_SEP_PATH;
589
590        str = currentDir + argv0;
591        if ( wxFile::Exists(str) )
592            return wxPathOnly(str);
593    }
594
595    // OK, it's neither an absolute path nor a relative path.
596    // Search PATH.
597
598    wxPathList pathList;
599    pathList.AddEnvList(wxT("PATH"));
600    str = pathList.FindAbsoluteValidPath(argv0);
601    if (!str.empty())
602        return wxPathOnly(str);
603
604    // Failed
605    return wxEmptyString;
606}
607
608