source: git/src/aven.cc @ b0908ea

Last change on this file since b0908ea was b0908ea, checked in by Olly Betts <olly@…>, 7 weeks ago

Disable scaling for hidpi displays with GTK3

The OpenGL code needs work before this will work usefully, so just
disable for now (which simulates how things are when using GTK2).

  • Property mode set to 100644
File size: 13.5 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,2018 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_SEARCH_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 __WXGTK3__
181    // Currently wxGLCanvas doesn't work under Wayland, and the code segfaults.
182    // https://trac.wxwidgets.org/ticket/17702
183    // Setting GDK_BACKEND=x11 is the recommended workaround, and it seems to
184    // work to set it here.  GTK2 doesn't support Wayland, so doesn't need
185    // this.
186    setenv("GDK_BACKEND", "x11", 1);
187    // FIXME: The OpenGL code needs work before scaling on hidpi displays will
188    // work usefully, so for now disable such scaling (which simulates how
189    // things are when using GTK2).
190    setenv("GDK_SCALE", "1", 1);
191#endif
192#ifdef __WXMAC__
193    // MacOS passes a magic -psn_XXXX command line argument in argv[1] which
194    // wx ignores for us, but in wxApp::Initialize() which hasn't been
195    // called yet.  So we need to remove it ourselves.
196    if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
197        --argc;
198        memmove(argv + 1, argv + 2, argc * sizeof(char *));
199    }
200#endif
201    // Call msg_init() and start processing the command line first so that
202    // we can respond to --help and --version even without an X display.
203    msg_init(argv);
204    select_charset(CHARSET_UTF8);
205    /* Want --version and decent --help output, which cmdline does for us.
206     * wxCmdLine is much less good.
207     */
208    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
209    cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 0, 1);
210    getopt_first_response = cmdline_getopt();
211
212    utf8_argv = argv;
213
214#if wxUSE_UNICODE
215    wxWCharBuffer buf(wxConvFileName->cMB2WX(argv[0]));
216    wxChar * wargv[2];
217    if (buf) {
218        wargv[0] = wxStrdup(buf);
219    } else {
220        // Eep - couldn't convert the executable's name to wide characters!
221        wargv[0] = wxStrdup(APP_NAME);
222    }
223    wargv[1] = NULL;
224    int wargc = 1;
225    return wxEntry(wargc, wargv);
226#else
227    char *dummy_argv[2] = { argv[0], NULL };
228    int dummy_argc = 1;
229    return wxEntry(dummy_argc, dummy_argv);
230#endif
231}
232#endif
233
234bool Aven::OnInit()
235{
236    wxLog::SetActiveTarget(new MyLogWindow());
237
238    {
239        // Suppress message box warnings about messages not found.
240        wxLogNull logNo;
241        wxLocale *loc = new wxLocale();
242        loc->AddCatalogLookupPathPrefix(wmsg_cfgpth());
243        wxString msg_lang_str(msg_lang, wxConvUTF8);
244        const char *lang = msg_lang2 ? msg_lang2 : msg_lang;
245        wxString lang_str(lang, wxConvUTF8);
246        loc->Init(msg_lang_str, lang_str, msg_lang_str);
247        // The existence of the wxLocale object is enough - no need to keep a
248        // pointer to it!
249    }
250
251    const char* opt_survey = NULL;
252    bool print_and_exit = false;
253
254    while (true) {
255        int opt;
256        if (getopt_first_response) {
257            opt = getopt_first_response;
258            getopt_first_response = 0;
259        } else {
260            opt = cmdline_getopt();
261        }
262        if (opt == EOF) break;
263        if (opt == 's') {
264            if (opt_survey != NULL) {
265                // FIXME: Not a helpful error, but this is temporary until
266                // we actually hook up support for specifying multiple
267                // --survey options properly here.
268                cmdline_syntax();
269                exit(1);
270            }
271            opt_survey = optarg;
272        }
273        if (opt == 'p') {
274            print_and_exit = true;
275        }
276    }
277
278    if (print_and_exit && !utf8_argv[optind]) {
279        cmdline_syntax(); // FIXME : not a helpful error...
280        exit(1);
281    }
282
283    wxString fnm;
284    if (utf8_argv[optind]) {
285        fnm = wxString(utf8_argv[optind], wxConvUTF8);
286        if (fnm.empty() && *(utf8_argv[optind])) {
287            ReportError(wxT("File argument's filename has bad encoding"));
288            return false;
289        }
290    }
291
292    if (!GLACanvas::check_visual()) {
293        wxString m;
294        /* TRANSLATORS: %s will be replaced with "Aven" currently (and
295         * perhaps by "Survex" or other things in future). */
296        m.Printf(wmsg(/*This version of %s requires OpenGL to work, but it isn’t available.*/405), APP_NAME);
297        wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
298        exit(1);
299    }
300
301    wxImage::AddHandler(new wxPNGHandler);
302
303    // Obtain the screen geometry.
304#if wxUSE_DISPLAY
305    wxRect geom = wxDisplay().GetGeometry();
306#else
307    wxRect geom;
308    wxClientDisplayRect(&geom.x, &geom.y, &geom.width, &geom.height);
309#endif
310
311    wxPoint pos(wxDefaultPosition);
312    int width, height;
313    wxConfigBase::Get()->Read(wxT("width"), &width, 0);
314    if (width > 0) wxConfigBase::Get()->Read(wxT("height"), &height, 0);
315    // We used to persist full screen mode (-1 was maximized,
316    // -2 full screen), but people would get stuck in full
317    // screen mode, unsure how to exit.
318    bool maximized = (width <= -1);
319    if (width <= 0 || height <= 0) {
320        pos.x = geom.x;
321        pos.y = geom.y;
322        width = geom.width;
323        height = geom.height;
324
325        // Calculate a reasonable size for our window.
326        pos.x += width / 8;
327        pos.y += height / 8;
328        width = width * 3 / 4;
329        height = height * 3 / 4;
330    } else {
331        // Impose a minimum size for sanity, and make sure the window fits on
332        // the display (in case the current display is smaller than the one
333        // in use when the window size was saved).  (480x320) is about the
334        // smallest usable size for aven's window.
335        const int min_width = min(geom.width, 480);
336        const int min_height = min(geom.height, 320);
337        if (width < min_width || height < min_height) {
338            if (width < min_width) {
339                width = min_width;
340            }
341            if (height < min_height) {
342                height = min_height;
343            }
344            pos.x = geom.x + (geom.width - width) / 4;
345            pos.y = geom.y + (geom.height - height) / 4;
346        }
347    }
348
349    // Create the main window.
350    m_Frame = new MainFrm(APP_NAME, pos, wxSize(width, height));
351
352    // Select maximised if that's the saved state.
353    if (maximized) {
354        m_Frame->Maximize();
355    }
356
357    if (utf8_argv[optind]) {
358        if (!opt_survey) opt_survey = "";
359        m_Frame->OpenFile(fnm, wxString(opt_survey, wxConvUTF8));
360    }
361
362    if (print_and_exit) {
363        m_Frame->PrintAndExit();
364        return true;
365    }
366
367    m_Frame->Show(true);
368#ifdef _WIN32
369    m_Frame->SetFocus();
370#endif
371    return true;
372}
373
374wxPageSetupDialogData *
375Aven::GetPageSetupDialogData()
376{
377    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
378#ifdef __WXGTK__
379    // Fetch paper margins stored on disk.
380    int left, right, top, bottom;
381    wxConfigBase * cfg = wxConfigBase::Get();
382    // These default margins were chosen by looking at all the .ppd files
383    // on my machine.
384    cfg->Read(wxT("paper_margin_left"), &left, 7);
385    cfg->Read(wxT("paper_margin_right"), &right, 7);
386    cfg->Read(wxT("paper_margin_top"), &top, 14);
387    cfg->Read(wxT("paper_margin_bottom"), &bottom, 14);
388    m_pageSetupData->SetMarginTopLeft(wxPoint(left, top));
389    m_pageSetupData->SetMarginBottomRight(wxPoint(right, bottom));
390#endif
391    return m_pageSetupData;
392}
393
394void
395Aven::SetPageSetupDialogData(const wxPageSetupDialogData & psdd)
396{
397    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
398    *m_pageSetupData = psdd;
399#ifdef __WXGTK__
400    wxPoint topleft = psdd.GetMarginTopLeft();
401    wxPoint bottomright = psdd.GetMarginBottomRight();
402
403    // Store user specified paper margins on disk/in registry.
404    wxConfigBase * cfg = wxConfigBase::Get();
405    cfg->Write(wxT("paper_margin_left"), topleft.x);
406    cfg->Write(wxT("paper_margin_right"), bottomright.x);
407    cfg->Write(wxT("paper_margin_top"), topleft.y);
408    cfg->Write(wxT("paper_margin_bottom"), bottomright.y);
409    cfg->Flush();
410#endif
411}
412
413#ifdef __WXMAC__
414void
415Aven::MacOpenFiles(const wxArrayString & filenames)
416{
417    if (filenames.size() != 1) {
418        ReportError(wxT("Aven can only load one file at a time"));
419        return;
420    }
421    m_Frame->OpenFile(filenames[0], wxString());
422}
423
424void
425Aven::MacPrintFiles(const wxArrayString & filenames)
426{
427    if (filenames.size() != 1) {
428        ReportError(wxT("Aven can only print one file at a time"));
429        return;
430    }
431    m_Frame->OpenFile(filenames[0], wxString());
432    m_Frame->PrintAndExit();
433}
434#endif
435
436void Aven::ReportError(const wxString& msg)
437{
438    if (!m_Frame) {
439        wxMessageBox(msg, APP_NAME, wxOK | wxICON_ERROR);
440        return;
441    }
442    wxMessageDialog dlg(m_Frame, msg, APP_NAME, wxOK | wxICON_ERROR);
443    dlg.ShowModal();
444}
445
446const wxString &
447wmsg_cfgpth()
448{
449    static wxString path;
450    if (path.empty())
451        path = wxString(msg_cfgpth(), wxConvUTF8);
452    return path;
453}
454
455// called to report errors by message.c
456extern "C" void
457aven_v_report(int severity, const char *fnm, int line, int en, va_list ap)
458{
459    wxString m;
460    if (fnm) {
461        m = wxString(fnm, wxConvUTF8);
462        if (line) m += wxString::Format(wxT(":%d"), line);
463        m += wxT(": ");
464    }
465
466    if (severity == 0) {
467        m += wmsg(/*warning*/4);
468        m += wxT(": ");
469    }
470
471    char buf[1024];
472    vsnprintf(buf, sizeof(buf), msg(en), ap);
473    m += wxString(buf, wxConvUTF8);
474    if (wxTheApp == NULL) {
475        // We haven't initialised the Aven app object yet.
476        if (!wxInitialize()) {
477            fputs(buf, stderr);
478            PUTC('\n', stderr);
479            exit(1);
480        }
481        wxMessageBox(m, APP_NAME, wxOK | wxICON_ERROR);
482        wxUninitialize();
483    } else {
484        wxGetApp().ReportError(m);
485    }
486}
Note: See TracBrowser for help on using the repository browser.