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
#endif // OS_UNIX
#if OS_MACOSX
#include "lib/sysdep/os/osx/osx_atlas.h"
#endif
#if MSC_VERSION
#include <process.h>
#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);
#if OS_MACOSX
if (g_Shutdown == ShutdownType::RestartAsAtlas)
startNewAtlasProcess();
#else
if (g_Shutdown == ShutdownType::RestartAsAtlas)
ATLAS_RunIfOnCmdLine(args, true);
#endif
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);
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");
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2017 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -180,13 +180,6 @@ class AtlasDLLApp : public wxApp
{
public:
#ifdef __WXOSX__
virtual bool OSXIsGUIApplication()
{
return false;
}
#endif
virtual bool OnInit()
{
// _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.
*
* 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_F15: return SDLK_F15;
case WXK_BACK: return SDLK_BACKSPACE;
case WXK_DELETE: return SDLK_DELETE;
case WXK_NUMLOCK: return SDLK_NUMLOCKCLEAR;
case WXK_SCROLL: return SDLK_SCROLLLOCK;
// 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.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -132,9 +132,7 @@ private:
if (KeyScroll(evt, true))
return;
// Slight hack: Only pass 'special' keys; normal keys will generate a translated Char event instead
if (evt.GetKeyCode() >= 256)
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), true));
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), true));
evt.Skip();
}
@ -147,9 +145,7 @@ private:
if (KeyScroll(evt, false))
return;
// Slight hack: Only pass 'special' keys; normal keys will generate a translated Char event instead
if (evt.GetKeyCode() >= 256)
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), false));
POST_MESSAGE(GuiKeyEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey(), false));
evt.Skip();
}
@ -189,8 +185,8 @@ private:
}
else
{
// Slight hack: Only pass 'normal' keys; special keys will generate a KeyDown/KeyUp event instead
if (evt.GetKeyCode() < 256)
// Slight hack: Only pass 'alphanumeric' keys; special keys will generate a KeyDown/KeyUp event instead
if (evt.GetKeyCode() >= 33 && evt.GetKeyCode() <= 126)
POST_MESSAGE(GuiCharEvent, (GetSDLKeyFromWxKeyCode(evt.GetKeyCode()), evt.GetUnicodeKey()));
evt.Skip();
@ -315,14 +311,14 @@ enum
{
ID_Quit = 1,
ID_New,
ID_New,
ID_Open,
ID_Save,
ID_SaveAs,
ID_ImportHeightmap,
ID_Copy,
ID_Paste,
ID_Copy,
ID_Paste,
ID_Wireframe,
ID_MessageTrace,
@ -345,7 +341,7 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_CLOSE(ScenarioEditor::OnClose)
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_Save, ScenarioEditor::OnSave)
EVT_MENU(ID_SaveAs, ScenarioEditor::OnSaveAs)
@ -355,8 +351,8 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_MENU(ID_Quit, ScenarioEditor::OnQuit)
EVT_MENU(wxID_UNDO, ScenarioEditor::OnUndo)
EVT_MENU(wxID_REDO, ScenarioEditor::OnRedo)
EVT_MENU(ID_Copy, ScenarioEditor::OnCopy)
EVT_MENU(ID_Paste, ScenarioEditor::OnPaste)
EVT_MENU(ID_Copy, ScenarioEditor::OnCopy)
EVT_MENU(ID_Paste, ScenarioEditor::OnPaste)
EVT_MENU(ID_Wireframe, ScenarioEditor::OnWireframe)
EVT_MENU(ID_MessageTrace, ScenarioEditor::OnMessageTrace)
@ -439,7 +435,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
wxMenu *menuFile = new wxMenu;
menuBar->Append(menuFile, _("&File"));
{
menuFile->Append(ID_New, _("&New..."));
menuFile->Append(ID_New, _("&New..."));
menuFile->Append(ID_Open, _("&Open..."));
menuFile->Append(ID_Save, _("&Save"));
menuFile->Append(ID_SaveAs, _("Save &As..."));
@ -459,14 +455,14 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
{
menuEdit->Append(wxID_UNDO, _("&Undo"));
menuEdit->Append(wxID_REDO, _("&Redo"));
menuEdit->AppendSeparator();
menuEdit->Append(ID_Copy, _("&Copy"));
menuEdit->Enable(ID_Copy, false);
menuEdit->Append(ID_Paste, _("&Paste"));
menuEdit->Enable(ID_Paste, false);
menuEdit->AppendSeparator();
menuEdit->Append(ID_Copy, _("&Copy"));
menuEdit->Enable(ID_Copy, false);
menuEdit->Append(ID_Paste, _("&Paste"));
menuEdit->Enable(ID_Paste, false);
}
g_SelectedObjects.RegisterObserver(0, &ScenarioEditor::OnSelectedObjectsChange, this);
g_SelectedObjects.RegisterObserver(0, &ScenarioEditor::OnSelectedObjectsChange, this);
GetCommandProc().SetEditMenu(menuEdit);
GetCommandProc().Initialize();
@ -636,13 +632,13 @@ float ScenarioEditor::GetSpeedModifier()
void ScenarioEditor::OnClose(wxCloseEvent& event)
{
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)
{
event.Veto();
return;
}
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)
{
event.Veto();
return;
}
}
m_ToolManager.SetCurrentTool(_T(""));
@ -698,16 +694,16 @@ void ScenarioEditor::OnRedo(wxCommandEvent&)
void ScenarioEditor::OnCopy(wxCommandEvent& WXUNUSED(event))
{
if (GetToolManager().GetCurrentToolName() == _T("TransformObject"))
GetToolManager().GetCurrentTool()->OnCommand(_T("copy"), NULL);
if (GetToolManager().GetCurrentToolName() == _T("TransformObject"))
GetToolManager().GetCurrentTool()->OnCommand(_T("copy"), NULL);
}
void ScenarioEditor::OnPaste(wxCommandEvent& WXUNUSED(event))
{
if (GetToolManager().GetCurrentToolName() != _T("TransformObject"))
GetToolManager().SetCurrentTool(_T("TransformObject"), NULL);
if (GetToolManager().GetCurrentToolName() != _T("TransformObject"))
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;
qry.Post();
GetCommandProc().MarkAsSaved();
GetCommandProc().MarkAsSaved();
}
}
@ -861,7 +857,7 @@ void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event))
qPing qry;
qry.Post();
GetCommandProc().MarkAsSaved();
GetCommandProc().MarkAsSaved();
}
}
@ -978,7 +974,7 @@ void ScenarioEditor::OnDumpState(wxCommandEvent& event)
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)
@ -996,50 +992,50 @@ void ScenarioEditor::OnHelp(wxCommandEvent& event)
void ScenarioEditor::OnMenuOpen(wxMenuEvent& event)
{
// Ignore wxMSW system menu events, see https://trac.wildfiregames.com/ticket/5501
const wxMenu* menu = event.GetMenu();
if (!menu)
return;
// Ignore wxMSW system menu events, see https://trac.wildfiregames.com/ticket/5501
const wxMenu* menu = event.GetMenu();
if (!menu)
return;
// This could be done far more elegantly if wxMenuItem had changeable id.
wxMenu* pasteMenuItem = NULL;
menu->FindItem(ID_Paste, &pasteMenuItem);
// This could be done far more elegantly if wxMenuItem had changeable id.
wxMenu* pasteMenuItem = NULL;
menu->FindItem(ID_Paste, &pasteMenuItem);
GetMenuBar()->Enable(ID_Paste, false);
GetMenuBar()->Enable(ID_Paste, false);
if (!pasteMenuItem)
return;
if (!pasteMenuItem)
return;
wxString content;
if (wxTheClipboard->Open())
{
if (wxTheClipboard->IsSupported(wxDF_TEXT))
{
wxTextDataObject data;
wxTheClipboard->GetData(data);
content = data.GetText();
}
wxString content;
if (wxTheClipboard->Open())
{
if (wxTheClipboard->IsSupported(wxDF_TEXT))
{
wxTextDataObject data;
wxTheClipboard->GetData(data);
content = data.GetText();
}
wxTheClipboard->Close();
}
wxTheClipboard->Close();
}
if (content.empty())
return;
if (content.empty())
return;
wxInputStream* is = new wxStringInputStream(content);
wxXmlDocument doc;
{
wxLogNull stopComplaining;
static_cast<void>(stopComplaining);
if (!doc.Load(*is))
return;
}
wxInputStream* is = new wxStringInputStream(content);
wxXmlDocument doc;
{
wxLogNull stopComplaining;
static_cast<void>(stopComplaining);
if (!doc.Load(*is))
return;
}
wxXmlNode* root = doc.GetRoot();
if (!root || root->GetName() != wxT("Entities"))
return;
wxXmlNode* root = doc.GetRoot();
if (!root || root->GetName() != wxT("Entities"))
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.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -198,16 +198,20 @@ MESSAGEHANDLER(GuiKeyEvent)
MESSAGEHANDLER(GuiCharEvent)
{
// wxWidgets has special Char events but SDL doesn't, so convert it to
// a keydown+keyup sequence. (We do the conversion here instead of on
// the wx side to avoid nondeterministic behaviour caused by async messaging.)
// Simulate special 'text input' events in the SDL
// This isn't quite compatible with WXWidget's handling,
// so to avoid trouble we only send 'letter-like' ASCII input.
SDL_Event_ ev = { { 0 } };
ev.ev.type = SDL_KEYDOWN;
ev.ev.key.keysym.sym = (SDL_Keycode)(int)msg->sdlkey;
ev.ev.type = SDL_TEXTEDITING;
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);
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);
}