diff --git a/source/lib/sysdep/os/osx/osx_atlas.h b/source/lib/sysdep/os/osx/osx_atlas.h new file mode 100644 index 0000000000..4cf5204109 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_atlas.h @@ -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 diff --git a/source/lib/sysdep/os/osx/osx_atlas.mm b/source/lib/sysdep/os/osx/osx_atlas.mm new file mode 100644 index 0000000000..ccd7c405a9 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_atlas.mm @@ -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 // MAC_OS_X_VERSION_MIN_REQUIRED +#import + +#import "osx_atlas.h" + +#include +#include "lib/types.h" +#include "ps/CStr.h" + +extern std::vector 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 *params = @{ NSWorkspaceLaunchConfigurationArguments: args }; + + [[NSWorkspace sharedWorkspace] launchApplicationAtURL:[[NSRunningApplication currentApplication] executableURL] options:NSWorkspaceLaunchNewInstance configuration:params error:nil]; + + [pool drain]; +} diff --git a/source/main.cpp b/source/main.cpp index ee3e08f117..ac5101167b 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -86,6 +86,10 @@ that of Atlas depending on commandline parameters. #include // geteuid #endif // OS_UNIX +#if OS_MACOSX +#include "lib/sysdep/os/osx/osx_atlas.h" +#endif + #if MSC_VERSION #include #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(); } diff --git a/source/ps/GameSetup/HWDetect.cpp b/source/ps/GameSetup/HWDetect.cpp index ffa8b6c8eb..cfcd12cc8c 100644 --- a/source/ps/GameSetup/HWDetect.cpp +++ b/source/ps/GameSetup/HWDetect.cpp @@ -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"); } diff --git a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp index ea96226fe4..9cbabf42cb 100644 --- a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp +++ b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp @@ -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); diff --git a/source/tools/atlas/AtlasUI/Misc/KeyMap.cpp b/source/tools/atlas/AtlasUI/Misc/KeyMap.cpp index f8e118b6f5..d69f9a786b 100644 --- a/source/tools/atlas/AtlasUI/Misc/KeyMap.cpp +++ b/source/tools/atlas/AtlasUI/Misc/KeyMap.cpp @@ -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; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 35d8d7018e..4d2d2021a3 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -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& 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(stopComplaining); - if (!doc.Load(*is)) - return; - } + wxInputStream* is = new wxStringInputStream(content); + wxXmlDocument doc; + { + wxLogNull stopComplaining; + static_cast(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); } diff --git a/source/tools/atlas/GameInterface/Handlers/MiscHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MiscHandlers.cpp index 3006aab53a..ee559756cf 100644 --- a/source/tools/atlas/GameInterface/Handlers/MiscHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MiscHandlers.cpp @@ -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); }