source: git/src/aven.cc @ 5b9d78d

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectwalls-datawalls-data-hanging-as-warning
Last change on this file since 5b9d78d was caa5fda, checked in by Olly Betts <olly@…>, 6 years ago

Refactor start-up check for OpenGL

Rather than using the apparently totally undocumented wxGLApp class,
use the documented wxGLCanvas::IsDisplaySupported?() static method
which allows moving this OpenGL-specific code into the same source
file as the other OpenGL-specific code, means we can merge the two
divergent versions of the list of attributes we ask for, and also
likely fixes handling of the case when double buffering isn't available.

  • Property mode set to 100644
File size: 12.8 KB
Line 
1//
2//  aven.cc
3//
4//  Main class for Aven.
5//
6//  Copyright (C) 2001 Mark R. Shinwell.
7//  Copyright (C) 2002,2003,2004,2005,2006,2011,2013,2014,2015,2016,2017 Olly Betts
8//
9//  This program is free software; you can redistribute it and/or modify
10//  it under the terms of the GNU General Public License as published by
11//  the Free Software Foundation; either version 2 of the License, or
12//  (at your option) any later version.
13//
14//  This program is distributed in the hope that it will be useful,
15//  but WITHOUT ANY WARRANTY; without even the implied warranty of
16//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17//  GNU General Public License for more details.
18//
19//  You should have received a copy of the GNU General Public License
20//  along with this program; if not, write to the Free Software
21//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22//
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#define MSG_SETUP_PROJ_SERACH_PATH 1
29
30#include "aven.h"
31#include "log.h"
32#include "gla.h"
33#include "mainfrm.h"
34
35#include "cmdline.h"
36#include "message.h"
37#include "useful.h"
38
39#include <assert.h>
40#include <stdio.h>
41
42#include <wx/confbase.h>
43#include <wx/image.h>
44#if wxUSE_DISPLAY
45// wxDisplay was added in wx 2.5; but it may not be built for mingw (because
46// the header seems to be missing).
47#include <wx/display.h>
48#endif
49
50#ifdef __WXMSW__
51#include <windows.h>
52#endif
53
54static const struct option long_opts[] = {
55    /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
56    {"survey", required_argument, 0, 's'},
57    {"print", no_argument, 0, 'p'},
58    {"help", no_argument, 0, HLP_HELP},
59    {"version", no_argument, 0, HLP_VERSION},
60    {0, 0, 0, 0}
61};
62
63#define short_opts "s:p"
64
65static struct help_msg help[] = {
66    /*                          <-- */
67    /* TRANSLATORS: --help output for --survey option.
68     *
69     * "this" has been added to English translation */
70    {HLP_ENCODELONG(0),       /*only load the sub-survey with this prefix*/199, 0},
71    /* TRANSLATORS: --help output for aven --print option */
72    {HLP_ENCODELONG(1),       /*print and exit (requires a 3d file)*/119, 0},
73    {0, 0, 0}
74};
75
76#ifdef __WXMSW__
77IMPLEMENT_APP(Aven)
78#else
79IMPLEMENT_APP_NO_MAIN(Aven)
80IMPLEMENT_WX_THEME_SUPPORT
81#endif
82
83Aven::Aven() :
84    m_Frame(NULL), m_pageSetupData(NULL)
85{
86    wxFont::SetDefaultEncoding(wxFONTENCODING_UTF8);
87}
88
89Aven::~Aven()
90{
91    delete m_pageSetupData;
92}
93
94static int getopt_first_response = 0;
95
96static char ** utf8_argv;
97
98#ifdef __WXMSW__
99bool Aven::Initialize(int& my_argc, wxChar **my_argv)
100{
101    const wxChar * cmd_line = GetCommandLineW();
102
103    // Horrible bodge to handle therion's assumptions about the "Process"
104    // file association.
105    if (cmd_line) {
106        // None of these are valid aven command line options, so this is not
107        // going to be triggered accidentally.
108        const wxChar * p = wxStrstr(cmd_line,
109                                    wxT("aven.exe\" --quiet --log --output="));
110        if (p) {
111            // Just change the command name in the command line string - that
112            // way the quoting should match what the C runtime expects.
113            wxString cmd(cmd_line, p - cmd_line);
114            cmd += "cavern";
115            cmd += p + 4;
116            exit(wxExecute(cmd, wxEXEC_SYNC));
117        }
118    }
119
120    int utf8_argc;
121    {
122        // wxWidgets doesn't split up the command line in the standard way, so
123        // redo it ourselves using the standard API function.
124        //
125        // Warning: The returned array from this has no terminating NULL
126        // element.
127        wxChar ** new_argv = NULL;
128        if (cmd_line)
129            new_argv = CommandLineToArgvW(cmd_line, &utf8_argc);
130        bool failed = (new_argv == NULL);
131        if (failed) {
132            wxChar * p;
133            FormatMessage(
134                    FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
135                    NULL,
136                    GetLastError(),
137                    0,
138                    (LPWSTR)&p,
139                    4096,
140                    NULL);
141            wxString m = "CommandLineToArgvW failed: ";
142            m += p;
143            wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
144            LocalFree(p);
145            utf8_argc = my_argc;
146            new_argv = my_argv;
147        }
148
149        // Convert wide characters to UTF-8.
150        utf8_argv = new char * [utf8_argc + 1];
151        for (int i = 0; i < utf8_argc; ++i){
152            utf8_argv[i] = strdup(wxString(new_argv[i]).utf8_str());
153        }
154        utf8_argv[utf8_argc] = NULL;
155
156        if (!failed) LocalFree(new_argv);
157    }
158
159    msg_init(utf8_argv);
160    select_charset(CHARSET_UTF8);
161    /* Want --version and decent --help output, which cmdline does for us.
162     * wxCmdLine is much less good.
163     */
164    /* TRANSLATORS: Here "survey" is a "cave map" rather than list of questions
165     * - it should be translated to the terminology that cavers using the
166     * language would use.
167     *
168     * Part of aven --help */
169    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
170    cmdline_init(utf8_argc, utf8_argv, short_opts, long_opts, NULL, help, 0, 1);
171    getopt_first_response = cmdline_getopt();
172
173    // The argc and argv arguments don't actually get used here.
174    int dummy_argc = 0;
175    return wxApp::Initialize(dummy_argc, NULL);
176}
177#else
178int main(int argc, char **argv)
179{
180#ifdef __WXMAC__
181    // MacOS passes a magic -psn_XXXX command line argument in argv[1] which
182    // wx ignores for us, but in wxApp::Initialize() which hasn't been
183    // called yet.  So we need to remove it ourselves.
184    if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
185        --argc;
186        memmove(argv + 1, argv + 2, argc * sizeof(char *));
187    }
188#endif
189    // Call msg_init() and start processing the command line first so that
190    // we can respond to --help and --version even without an X display.
191    msg_init(argv);
192    select_charset(CHARSET_UTF8);
193    /* Want --version and decent --help output, which cmdline does for us.
194     * wxCmdLine is much less good.
195     */
196    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
197    cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 0, 1);
198    getopt_first_response = cmdline_getopt();
199
200    utf8_argv = argv;
201
202#if wxUSE_UNICODE
203    wxWCharBuffer buf(wxConvFileName->cMB2WX(argv[0]));
204    wxChar * wargv[2];
205    if (buf) {
206        wargv[0] = wxStrdup(buf);
207    } else {
208        // Eep - couldn't convert the executable's name to wide characters!
209        wargv[0] = wxStrdup(APP_NAME);
210    }
211    wargv[1] = NULL;
212    int wargc = 1;
213    return wxEntry(wargc, wargv);
214#else
215    char *dummy_argv[2] = { argv[0], NULL };
216    int dummy_argc = 1;
217    return wxEntry(dummy_argc, dummy_argv);
218#endif
219}
220#endif
221
222bool Aven::OnInit()
223{
224    wxLog::SetActiveTarget(new MyLogWindow());
225
226    {
227        // Suppress message box warnings about messages not found.
228        wxLogNull logNo;
229        wxLocale *loc = new wxLocale();
230        loc->AddCatalogLookupPathPrefix(wmsg_cfgpth());
231        wxString msg_lang_str(msg_lang, wxConvUTF8);
232        const char *lang = msg_lang2 ? msg_lang2 : msg_lang;
233        wxString lang_str(lang, wxConvUTF8);
234#if wxCHECK_VERSION(2,9,0)
235        loc->Init(msg_lang_str, lang_str, msg_lang_str);
236#else
237        loc->Init(msg_lang_str, lang_str, msg_lang_str, true, true);
238#endif
239        // The existence of the wxLocale object is enough - no need to keep a
240        // pointer to it!
241    }
242
243    wxString survey;
244    bool print_and_exit = false;
245
246    while (true) {
247        int opt;
248        if (getopt_first_response) {
249            opt = getopt_first_response;
250            getopt_first_response = 0;
251        } else {
252            opt = cmdline_getopt();
253        }
254        if (opt == EOF) break;
255        if (opt == 's') {
256            survey = wxString(optarg, wxConvUTF8);
257        }
258        if (opt == 'p') {
259            print_and_exit = true;
260        }
261    }
262
263    if (print_and_exit && !utf8_argv[optind]) {
264        cmdline_syntax(); // FIXME : not a helpful error...
265        exit(1);
266    }
267
268    wxString fnm;
269    if (utf8_argv[optind]) {
270        fnm = wxString(utf8_argv[optind], wxConvUTF8);
271        if (fnm.empty() && *(utf8_argv[optind])) {
272            ReportError(wxT("File argument's filename has bad encoding"));
273            return false;
274        }
275    }
276
277    if (!GLACanvas::check_visual()) {
278        wxString m;
279        /* TRANSLATORS: %s will be replaced with "Aven" currently (and
280         * perhaps by "Survex" or other things in future). */
281        m.Printf(wmsg(/*This version of %s requires OpenGL to work, but it isn’t available.*/405), APP_NAME);
282        wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
283        exit(1);
284    }
285
286    wxImage::AddHandler(new wxPNGHandler);
287
288    // Obtain the screen geometry.
289#if wxUSE_DISPLAY
290    wxRect geom = wxDisplay().GetGeometry();
291#else
292    wxRect geom;
293    wxClientDisplayRect(&geom.x, &geom.y, &geom.width, &geom.height);
294#endif
295
296    wxPoint pos(wxDefaultPosition);
297    int width, height;
298    wxConfigBase::Get()->Read(wxT("width"), &width, 0);
299    if (width > 0) wxConfigBase::Get()->Read(wxT("height"), &height, 0);
300    // We used to persist full screen mode (-1 was maximized,
301    // -2 full screen), but people would get stuck in full
302    // screen mode, unsure how to exit.
303    bool maximized = (width <= -1);
304    if (width <= 0 || height <= 0) {
305        pos.x = geom.x;
306        pos.y = geom.y;
307        width = geom.width;
308        height = geom.height;
309
310        // Calculate a reasonable size for our window.
311        pos.x += width / 8;
312        pos.y += height / 8;
313        width = width * 3 / 4;
314        height = height * 3 / 4;
315    } else {
316        // Impose a minimum size for sanity, and make sure the window fits on
317        // the display (in case the current display is smaller than the one
318        // in use when the window size was saved).  (480x320) is about the
319        // smallest usable size for aven's window.
320        const int min_width = min(geom.width, 480);
321        const int min_height = min(geom.height, 320);
322        if (width < min_width || height < min_height) {
323            if (width < min_width) {
324                width = min_width;
325            }
326            if (height < min_height) {
327                height = min_height;
328            }
329            pos.x = geom.x + (geom.width - width) / 4;
330            pos.y = geom.y + (geom.height - height) / 4;
331        }
332    }
333
334    // Create the main window.
335    m_Frame = new MainFrm(APP_NAME, pos, wxSize(width, height));
336
337    // Select maximised if that's the saved state.
338    if (maximized) {
339        m_Frame->Maximize();
340    }
341
342    if (utf8_argv[optind]) {
343        m_Frame->OpenFile(fnm, survey);
344    }
345
346    if (print_and_exit) {
347        m_Frame->PrintAndExit();
348        return true;
349    }
350
351    m_Frame->Show(true);
352#ifdef _WIN32
353    m_Frame->SetFocus();
354#endif
355    return true;
356}
357
358wxPageSetupDialogData *
359Aven::GetPageSetupDialogData()
360{
361    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
362#ifdef __WXGTK__
363    // Fetch paper margins stored on disk.
364    int left, right, top, bottom;
365    wxConfigBase * cfg = wxConfigBase::Get();
366    // These default margins were chosen by looking at all the .ppd files
367    // on my machine.
368    cfg->Read(wxT("paper_margin_left"), &left, 7);
369    cfg->Read(wxT("paper_margin_right"), &right, 7);
370    cfg->Read(wxT("paper_margin_top"), &top, 14);
371    cfg->Read(wxT("paper_margin_bottom"), &bottom, 14);
372    m_pageSetupData->SetMarginTopLeft(wxPoint(left, top));
373    m_pageSetupData->SetMarginBottomRight(wxPoint(right, bottom));
374#endif
375    return m_pageSetupData;
376}
377
378void
379Aven::SetPageSetupDialogData(const wxPageSetupDialogData & psdd)
380{
381    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
382    *m_pageSetupData = psdd;
383#ifdef __WXGTK__
384    wxPoint topleft = psdd.GetMarginTopLeft();
385    wxPoint bottomright = psdd.GetMarginBottomRight();
386
387    // Store user specified paper margins on disk/in registry.
388    wxConfigBase * cfg = wxConfigBase::Get();
389    cfg->Write(wxT("paper_margin_left"), topleft.x);
390    cfg->Write(wxT("paper_margin_right"), bottomright.x);
391    cfg->Write(wxT("paper_margin_top"), topleft.y);
392    cfg->Write(wxT("paper_margin_bottom"), bottomright.y);
393    cfg->Flush();
394#endif
395}
396
397#ifdef __WXMAC__
398void
399Aven::MacOpenFiles(const wxArrayString & filenames)
400{
401    if (filenames.size() != 1) {
402        ReportError(wxT("Aven can only load one file at a time"));
403        return;
404    }
405    m_Frame->OpenFile(filenames[0], wxString());
406}
407
408void
409Aven::MacPrintFiles(const wxArrayString & filenames)
410{
411    if (filenames.size() != 1) {
412        ReportError(wxT("Aven can only print one file at a time"));
413        return;
414    }
415    m_Frame->OpenFile(filenames[0], wxString());
416    m_Frame->PrintAndExit();
417}
418#endif
419
420void Aven::ReportError(const wxString& msg)
421{
422    if (!m_Frame) {
423        wxMessageBox(msg, APP_NAME, wxOK | wxICON_ERROR);
424        return;
425    }
426    AvenAllowOnTop ontop(m_Frame);
427    wxMessageDialog dlg(m_Frame, msg, APP_NAME, wxOK | wxICON_ERROR);
428    dlg.ShowModal();
429}
430
431const wxString &
432wmsg_cfgpth()
433{
434    static wxString path;
435    if (path.empty())
436        path = wxString(msg_cfgpth(), wxConvUTF8);
437    return path;
438}
439
440// called to report errors by message.c
441extern "C" void
442aven_v_report(int severity, const char *fnm, int line, int en, va_list ap)
443{
444    wxString m;
445    if (fnm) {
446        m = wxString(fnm, wxConvUTF8);
447        if (line) m += wxString::Format(wxT(":%d"), line);
448        m += wxT(": ");
449    }
450
451    if (severity == 0) {
452        m += wmsg(/*warning*/4);
453        m += wxT(": ");
454    }
455
456    char buf[1024];
457    vsnprintf(buf, sizeof(buf), msg(en), ap);
458    m += wxString(buf, wxConvUTF8);
459    if (wxTheApp == NULL) {
460        // We haven't initialised the Aven app object yet.
461        if (!wxInitialize()) {
462            fputs(buf, stderr);
463            PUTC('\n', stderr);
464            exit(1);
465        }
466        wxMessageBox(m, APP_NAME, wxOK | wxICON_ERROR);
467        wxUninitialize();
468    } else {
469        wxGetApp().ReportError(m);
470    }
471}
Note: See TracBrowser for help on using the repository browser.