1
0
forked from 0ad/0ad

Fix issues relating to SDL and wxWidgets interaction in Atlas.

This fixes the transfer of key inputs from WxWidgets to SDL, making it
possible to type in the in-game GUI from Atlas.

Also fixes whitespace issues in some Atlas files.

The following improvements are OSX specific:
- fixes an SDL assertion related to unused subsystems in Atlas.
- Remove the 'osxguiapplication' override. This fixes the editor
starting up in the background and not accepting input when launched from
in-game.
- To prevent an issue with sdl/wxwidgets conflict when running from
inside the game, actually boot a new instance (see #2427)


Reported by: wik (Many thanks for your investigations)
Tested by: trompetin17, Stan
Fixes #2427
Fixes #2846

Differential Revision: https://code.wildfiregames.com/D2788
This was SVN commit r23926.
This commit is contained in:
wraitii 2020-08-03 12:23:16 +00:00
parent 375c319639
commit 01118c1196
8 changed files with 182 additions and 90 deletions

View File

@ -0,0 +1,33 @@
/* Copyright (C) 2020 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef OSX_ATLAS_H
#define OSX_ATLAS_H
/**
* Runs a new pyrogenesis process with the -editor argument.
* Necessary because SDL and WxWidgets conflict.
*/
void startNewAtlasProcess();
#endif // OSX_ATLAS_H

View File

@ -0,0 +1,54 @@
/* Copyright (C) 2020 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <AvailabilityMacros.h> // MAC_OS_X_VERSION_MIN_REQUIRED
#import <AppKit/AppKit.h>
#import "osx_atlas.h"
#include <vector>
#include "lib/types.h"
#include "ps/CStr.h"
extern std::vector<CStr> g_modsLoaded;
void startNewAtlasProcess()
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *args = [[NSMutableArray alloc] init];
[args addObject:@"--editor"];
// Pass mods on the command line.
for (const CStr& mod : g_modsLoaded)
{
std::string arg = std::string("-mod=") + mod;
[args addObject:[[NSString alloc] initWithUTF8String:arg.c_str()]];
}
// Apple documents this as (deprecated) NSWorkspaceLaunchConfigurationKey, but that's not available in early SDKs.
NSDictionary<NSString*, id> *params = @{ NSWorkspaceLaunchConfigurationArguments: args };
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:[[NSRunningApplication currentApplication] executableURL] options:NSWorkspaceLaunchNewInstance configuration:params error:nil];
[pool drain];
}

View File

@ -86,6 +86,10 @@ that of Atlas depending on commandline parameters.
#include <unistd.h> // geteuid #include <unistd.h> // geteuid
#endif // OS_UNIX #endif // OS_UNIX
#if OS_MACOSX
#include "lib/sysdep/os/osx/osx_atlas.h"
#endif
#if MSC_VERSION #if MSC_VERSION
#include <process.h> #include <process.h>
#define getpid _getpid // Use the non-deprecated function name #define getpid _getpid // Use the non-deprecated function name
@ -719,8 +723,13 @@ static void RunGameOrAtlas(int argc, const char* argv[])
} while (g_Shutdown == ShutdownType::Restart); } while (g_Shutdown == ShutdownType::Restart);
#if OS_MACOSX
if (g_Shutdown == ShutdownType::RestartAsAtlas)
startNewAtlasProcess();
#else
if (g_Shutdown == ShutdownType::RestartAsAtlas) if (g_Shutdown == ShutdownType::RestartAsAtlas)
ATLAS_RunIfOnCmdLine(args, true); ATLAS_RunIfOnCmdLine(args, true);
#endif
CXeromyces::Terminate(); CXeromyces::Terminate();
} }

View File

@ -300,7 +300,8 @@ static void ReportSDL(const ScriptInterface& scriptInterface, JS::HandleValue se
snprintf(version, ARRAY_SIZE(version), "%d.%d.%d", runtime.major, runtime.minor, runtime.patch); snprintf(version, ARRAY_SIZE(version), "%d.%d.%d", runtime.major, runtime.minor, runtime.patch);
scriptInterface.SetProperty(settings, "sdl_runtime_version", version); scriptInterface.SetProperty(settings, "sdl_runtime_version", version);
const char* backend = GetSDLSubsystem(g_VideoMode.GetWindow()); // This is null in atlas (and further the call triggers an assertion).
const char* backend = g_VideoMode.GetWindow() ? GetSDLSubsystem(g_VideoMode.GetWindow()) : "none";
scriptInterface.SetProperty(settings, "sdl_video_backend", backend ? backend : "unknown"); scriptInterface.SetProperty(settings, "sdl_video_backend", backend ? backend : "unknown");
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2017 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -180,13 +180,6 @@ class AtlasDLLApp : public wxApp
{ {
public: public:
#ifdef __WXOSX__
virtual bool OSXIsGUIApplication()
{
return false;
}
#endif
virtual bool OnInit() virtual bool OnInit()
{ {
// _CrtSetBreakAlloc(5632); // _CrtSetBreakAlloc(5632);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -77,6 +77,8 @@ int GetSDLKeyFromWxKeyCode(int wxkey)
case WXK_F14: return SDLK_F14; case WXK_F14: return SDLK_F14;
case WXK_F15: return SDLK_F15; case WXK_F15: return SDLK_F15;
case WXK_BACK: return SDLK_BACKSPACE;
case WXK_DELETE: return SDLK_DELETE;
case WXK_NUMLOCK: return SDLK_NUMLOCKCLEAR; case WXK_NUMLOCK: return SDLK_NUMLOCKCLEAR;
case WXK_SCROLL: return SDLK_SCROLLLOCK; case WXK_SCROLL: return SDLK_SCROLLLOCK;
// case WXK_: return SDLK_CAPSLOCK; // case WXK_: return SDLK_CAPSLOCK;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -132,9 +132,7 @@ private:
if (KeyScroll(evt, true)) if (KeyScroll(evt, true))
return; return;
// Slight hack: Only pass 'special' keys; normal keys will generate a translated Char event instead POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), true));
if (evt.GetKeyCode() >= 256)
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), true));
evt.Skip(); evt.Skip();
} }
@ -147,9 +145,7 @@ private:
if (KeyScroll(evt, false)) if (KeyScroll(evt, false))
return; return;
// Slight hack: Only pass 'special' keys; normal keys will generate a translated Char event instead POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), false));
if (evt.GetKeyCode() >= 256)
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), false));
evt.Skip(); evt.Skip();
} }
@ -189,8 +185,8 @@ private:
} }
else else
{ {
// Slight hack: Only pass 'normal' keys; special keys will generate a KeyDown/KeyUp event instead // Slight hack: Only pass 'alphanumeric' keys; special keys will generate a KeyDown/KeyUp event instead
if (evt.GetKeyCode() < 256) if (evt.GetKeyCode() >= 33 && evt.GetKeyCode() <= 126)
POST_MESSAGE(GuiCharEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey())); POST_MESSAGE(GuiCharEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey()));
evt.Skip(); evt.Skip();
@ -315,14 +311,14 @@ enum
{ {
ID_Quit = 1, ID_Quit = 1,
ID_New, ID_New,
ID_Open, ID_Open,
ID_Save, ID_Save,
ID_SaveAs, ID_SaveAs,
ID_ImportHeightmap, ID_ImportHeightmap,
ID_Copy, ID_Copy,
ID_Paste, ID_Paste,
ID_Wireframe, ID_Wireframe,
ID_MessageTrace, ID_MessageTrace,
@ -345,7 +341,7 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_CLOSE(ScenarioEditor::OnClose) EVT_CLOSE(ScenarioEditor::OnClose)
EVT_TIMER(wxID_ANY, ScenarioEditor::OnTimer) EVT_TIMER(wxID_ANY, ScenarioEditor::OnTimer)
EVT_MENU(ID_New, ScenarioEditor::OnNew) EVT_MENU(ID_New, ScenarioEditor::OnNew)
EVT_MENU(ID_Open, ScenarioEditor::OnOpen) EVT_MENU(ID_Open, ScenarioEditor::OnOpen)
EVT_MENU(ID_Save, ScenarioEditor::OnSave) EVT_MENU(ID_Save, ScenarioEditor::OnSave)
EVT_MENU(ID_SaveAs, ScenarioEditor::OnSaveAs) EVT_MENU(ID_SaveAs, ScenarioEditor::OnSaveAs)
@ -355,8 +351,8 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_MENU(ID_Quit, ScenarioEditor::OnQuit) EVT_MENU(ID_Quit, ScenarioEditor::OnQuit)
EVT_MENU(wxID_UNDO, ScenarioEditor::OnUndo) EVT_MENU(wxID_UNDO, ScenarioEditor::OnUndo)
EVT_MENU(wxID_REDO, ScenarioEditor::OnRedo) EVT_MENU(wxID_REDO, ScenarioEditor::OnRedo)
EVT_MENU(ID_Copy, ScenarioEditor::OnCopy) EVT_MENU(ID_Copy, ScenarioEditor::OnCopy)
EVT_MENU(ID_Paste, ScenarioEditor::OnPaste) EVT_MENU(ID_Paste, ScenarioEditor::OnPaste)
EVT_MENU(ID_Wireframe, ScenarioEditor::OnWireframe) EVT_MENU(ID_Wireframe, ScenarioEditor::OnWireframe)
EVT_MENU(ID_MessageTrace, ScenarioEditor::OnMessageTrace) EVT_MENU(ID_MessageTrace, ScenarioEditor::OnMessageTrace)
@ -439,7 +435,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
wxMenu *menuFile = new wxMenu; wxMenu *menuFile = new wxMenu;
menuBar->Append(menuFile, _("&File")); menuBar->Append(menuFile, _("&File"));
{ {
menuFile->Append(ID_New, _("&New...")); menuFile->Append(ID_New, _("&New..."));
menuFile->Append(ID_Open, _("&Open...")); menuFile->Append(ID_Open, _("&Open..."));
menuFile->Append(ID_Save, _("&Save")); menuFile->Append(ID_Save, _("&Save"));
menuFile->Append(ID_SaveAs, _("Save &As...")); menuFile->Append(ID_SaveAs, _("Save &As..."));
@ -459,14 +455,14 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
{ {
menuEdit->Append(wxID_UNDO, _("&Undo")); menuEdit->Append(wxID_UNDO, _("&Undo"));
menuEdit->Append(wxID_REDO, _("&Redo")); menuEdit->Append(wxID_REDO, _("&Redo"));
menuEdit->AppendSeparator(); menuEdit->AppendSeparator();
menuEdit->Append(ID_Copy, _("&Copy")); menuEdit->Append(ID_Copy, _("&Copy"));
menuEdit->Enable(ID_Copy, false); menuEdit->Enable(ID_Copy, false);
menuEdit->Append(ID_Paste, _("&Paste")); menuEdit->Append(ID_Paste, _("&Paste"));
menuEdit->Enable(ID_Paste, false); menuEdit->Enable(ID_Paste, false);
} }
g_SelectedObjects.RegisterObserver(0, &ScenarioEditor::OnSelectedObjectsChange, this); g_SelectedObjects.RegisterObserver(0, &ScenarioEditor::OnSelectedObjectsChange, this);
GetCommandProc().SetEditMenu(menuEdit); GetCommandProc().SetEditMenu(menuEdit);
GetCommandProc().Initialize(); GetCommandProc().Initialize();
@ -636,13 +632,13 @@ float ScenarioEditor::GetSpeedModifier()
void ScenarioEditor::OnClose(wxCloseEvent& event) void ScenarioEditor::OnClose(wxCloseEvent& event)
{ {
if (event.CanVeto() && GetCommandProc().IsDirty()) if (event.CanVeto() && GetCommandProc().IsDirty())
{ {
if (wxMessageBox(_T("You have unsaved changes. Are you sure you want to quit?"), _T("Discard unsaved changes?"), wxICON_QUESTION | wxYES_NO) != wxYES) if (wxMessageBox(_T("You have unsaved changes. Are you sure you want to quit?"), _T("Discard unsaved changes?"), wxICON_QUESTION | wxYES_NO) != wxYES)
{ {
event.Veto(); event.Veto();
return; return;
} }
} }
m_ToolManager.SetCurrentTool(_T("")); m_ToolManager.SetCurrentTool(_T(""));
@ -698,16 +694,16 @@ void ScenarioEditor::OnRedo(wxCommandEvent&)
void ScenarioEditor::OnCopy(wxCommandEvent& WXUNUSED(event)) void ScenarioEditor::OnCopy(wxCommandEvent& WXUNUSED(event))
{ {
if (GetToolManager().GetCurrentToolName() == _T("TransformObject")) if (GetToolManager().GetCurrentToolName() == _T("TransformObject"))
GetToolManager().GetCurrentTool()->OnCommand(_T("copy"), NULL); GetToolManager().GetCurrentTool()->OnCommand(_T("copy"), NULL);
} }
void ScenarioEditor::OnPaste(wxCommandEvent& WXUNUSED(event)) void ScenarioEditor::OnPaste(wxCommandEvent& WXUNUSED(event))
{ {
if (GetToolManager().GetCurrentToolName() != _T("TransformObject")) if (GetToolManager().GetCurrentToolName() != _T("TransformObject"))
GetToolManager().SetCurrentTool(_T("TransformObject"), NULL); GetToolManager().SetCurrentTool(_T("TransformObject"), NULL);
GetToolManager().GetCurrentTool()->OnCommand(_T("paste"), NULL); GetToolManager().GetCurrentTool()->OnCommand(_T("paste"), NULL);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -837,7 +833,7 @@ void ScenarioEditor::OnSave(wxCommandEvent& event)
qPing qry; qPing qry;
qry.Post(); qry.Post();
GetCommandProc().MarkAsSaved(); GetCommandProc().MarkAsSaved();
} }
} }
@ -861,7 +857,7 @@ void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event))
qPing qry; qPing qry;
qry.Post(); qry.Post();
GetCommandProc().MarkAsSaved(); GetCommandProc().MarkAsSaved();
} }
} }
@ -978,7 +974,7 @@ void ScenarioEditor::OnDumpState(wxCommandEvent& event)
void ScenarioEditor::OnSelectedObjectsChange(const std::vector<ObjectID>& selectedObjects) void ScenarioEditor::OnSelectedObjectsChange(const std::vector<ObjectID>& selectedObjects)
{ {
GetMenuBar()->Enable(ID_Copy, !selectedObjects.empty()); GetMenuBar()->Enable(ID_Copy, !selectedObjects.empty());
} }
void ScenarioEditor::OnHelp(wxCommandEvent& event) void ScenarioEditor::OnHelp(wxCommandEvent& event)
@ -996,50 +992,50 @@ void ScenarioEditor::OnHelp(wxCommandEvent& event)
void ScenarioEditor::OnMenuOpen(wxMenuEvent& event) void ScenarioEditor::OnMenuOpen(wxMenuEvent& event)
{ {
// Ignore wxMSW system menu events, see https://trac.wildfiregames.com/ticket/5501 // Ignore wxMSW system menu events, see https://trac.wildfiregames.com/ticket/5501
const wxMenu* menu = event.GetMenu(); const wxMenu* menu = event.GetMenu();
if (!menu) if (!menu)
return; return;
// This could be done far more elegantly if wxMenuItem had changeable id. // This could be done far more elegantly if wxMenuItem had changeable id.
wxMenu* pasteMenuItem = NULL; wxMenu* pasteMenuItem = NULL;
menu->FindItem(ID_Paste, &pasteMenuItem); menu->FindItem(ID_Paste, &pasteMenuItem);
GetMenuBar()->Enable(ID_Paste, false); GetMenuBar()->Enable(ID_Paste, false);
if (!pasteMenuItem) if (!pasteMenuItem)
return; return;
wxString content; wxString content;
if (wxTheClipboard->Open()) if (wxTheClipboard->Open())
{ {
if (wxTheClipboard->IsSupported(wxDF_TEXT)) if (wxTheClipboard->IsSupported(wxDF_TEXT))
{ {
wxTextDataObject data; wxTextDataObject data;
wxTheClipboard->GetData(data); wxTheClipboard->GetData(data);
content = data.GetText(); content = data.GetText();
} }
wxTheClipboard->Close(); wxTheClipboard->Close();
} }
if (content.empty()) if (content.empty())
return; return;
wxInputStream* is = new wxStringInputStream(content); wxInputStream* is = new wxStringInputStream(content);
wxXmlDocument doc; wxXmlDocument doc;
{ {
wxLogNull stopComplaining; wxLogNull stopComplaining;
static_cast<void>(stopComplaining); static_cast<void>(stopComplaining);
if (!doc.Load(*is)) if (!doc.Load(*is))
return; return;
} }
wxXmlNode* root = doc.GetRoot(); wxXmlNode* root = doc.GetRoot();
if (!root || root->GetName() != wxT("Entities")) if (!root || root->GetName() != wxT("Entities"))
return; return;
GetMenuBar()->Enable(ID_Paste, true); GetMenuBar()->Enable(ID_Paste, true);
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games. /* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -198,16 +198,20 @@ MESSAGEHANDLER(GuiKeyEvent)
MESSAGEHANDLER(GuiCharEvent) MESSAGEHANDLER(GuiCharEvent)
{ {
// wxWidgets has special Char events but SDL doesn't, so convert it to // Simulate special 'text input' events in the SDL
// a keydown+keyup sequence. (We do the conversion here instead of on // This isn't quite compatible with WXWidget's handling,
// the wx side to avoid nondeterministic behaviour caused by async messaging.) // so to avoid trouble we only send 'letter-like' ASCII input.
SDL_Event_ ev = { { 0 } }; SDL_Event_ ev = { { 0 } };
ev.ev.type = SDL_KEYDOWN; ev.ev.type = SDL_TEXTEDITING;
ev.ev.key.keysym.sym = (SDL_Keycode)(int)msg->sdlkey; ev.ev.text.type = SDL_TEXTEDITING;
ev.ev.text.text[0] = (char)msg->sdlkey;
ev.ev.text.text[1] = (char)0;
in_dispatch_event(&ev); in_dispatch_event(&ev);
ev.ev.type = SDL_KEYUP; ev.ev.type = SDL_TEXTINPUT;
ev.ev.text.type = SDL_TEXTINPUT;
ev.ev.text.text[0] = (char)msg->sdlkey;
ev.ev.text.text[1] = (char)0;
in_dispatch_event(&ev); in_dispatch_event(&ev);
} }