Compare commits

...

12 commits

Author SHA1 Message Date
Cayleb-Ordo
e75dd47d57 WIP
Signed-off-by: Cayleb-Ordo <simon@fentzl.de>
2026-05-24 12:30:32 +02:00
Cayleb-Ordo
2d54b6cf23 Add CMake Configuration for ActorEditor
Name the project AtlasMisc as the atlas Object is shared.
Use the `target_sources` to add sources based on their targets.

Not working:
- Linking to wxWidgets find_package target
  - Terrain.obj : `error LNK2019: Verweis auf nicht aufgel├Âstes externes Symbol ""int const wxEVT_NULL" (?wxEVT_NULL@@3HB)" in Funktion ""void __cdecl dynamic initializer for 'private: static struct wxEventTableEntry const * const TerrainSidebar::sm_eventTableEntries''(void)" (??__E?sm_eventTableEntries@TerrainSidebar@@0QBUwxEventTableEntry@@B@@YAXXZ)".`

Working:
- Using find_package extra variables to find most of the windows ext. libs
- excluding SDL2 and Boost, which use the custom find_prebuild_library function
2026-05-24 12:30:32 +02:00
Cayleb-Ordo
b8f27d89e3 Add the doxygen cmake file to the CMake Project
Make the docs its own target instead of a project. Dont add the
new docs target to ALL. Add an option to disable generating the
docs.
Update doxygen_awesome to v2.4.2.
Update README to reflect new changes.
2026-05-24 12:30:32 +02:00
Cayleb-Ordo
7e0a78d650 Add a module to fetch and setup Windows prebuild libraries
This module downloads the given svn revision based on the current
architecture selected. It also sets up the `CMAKE_PREFIX_PATH`.
Add new option to disable fetching, which assumes that the libraries
are already in the correct folders.
2026-05-24 12:30:32 +02:00
Cayleb-Ordo
8bbf6476a6 Add Root CMakeLists.txt and custom cmake modules
CMake is de facto the industrie standard for building C/C++ Code.
It is cross platform, supports presets and packaging for
Linux Distributions can be made with CPack. This also eliminates
the dependency to build and patch premake. Furthermore a lot of
IDE's are supported.

Add a BuildFlags CMake module with an INTERFACE library encapsulating
relevant build Flags and definitions. To provide support for multi
configuration generators use generator expressions as much as possible.
Add a (currently) empty 0ad-Functions module for later usage.
2026-05-24 12:30:32 +02:00
Vladislav Belov
1250ca1dac
Replaces FALLTHROUGH macro by attribute
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
It was forgotten during removing the FALLTHROUGH macro in
b41ca5ad78.
2026-05-22 23:25:31 +02:00
Vladislav Belov
a0ecfbaa33
Skips UNIFORM buffers for GLES
Currently we don't support UNIFORM buffers for GLES.

Refs 54701868da
2026-05-22 23:14:30 +02:00
phosit
01bd743b30
Correct LoadModal to continue a saved campaign
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
The `closePageCallback` isn't forwarted to the function calling it. Now
it's possible again to continue a saved campaign.
The error was introduced in 76b6725272.
2026-05-21 21:29:56 +02:00
Ralph Sennhauser
a0bb103390
Unwrap SDL_Event
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
Since C++11 a C typedef'ed union can be forward declared, so the wrapper
is no longer needed.

While at it switch signatures to refs and convert C style casts.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-05-20 19:44:52 +02:00
phosit
34ab0f3938
Disconnect before deleting the session
The session was deleted from `m_Sessions` before disconnecting. Since
e7a583adc0 that also deleted the session.
2026-05-17 17:20:37 +02:00
phosit
a95579a046
Fix UI when connecting to a server fails
When the connection fails, it wasn't possible to close the progress
window.
Now `PollNetworkClient` also resolves the previous promise.
2026-05-17 17:20:37 +02:00
guerringuerrin
32e5520507 Add hotkeys for opening GUI pages across the game
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
Add registerGlobalGuiPageHotkeys() to
common/functions_utility.js to selectively register
GUI page hotkeys
Allow active GUI pages to close using their corresponding hotkey
Move page_hotkeys.xml from gui/hotkeys/ to gui/
Update page_hotkeys.xml references in MainMenuItems and MenuButtons
Add default tipScrolling fallback in TipsPage when no initData is provided
2026-05-16 20:53:54 +02:00
111 changed files with 1791 additions and 530 deletions

157
CMakeLists.txt Normal file
View file

@ -0,0 +1,157 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
project(0ad VERSION 0.29.0)
# Available Options
option(android "Use non-working Android cross-compiling mode")
option(build-docs "Enable building the doxygen documentation(requires Network access)")
option(coverage "Enable code coverage data collection (GCC only)")
option(gles "Use non-working OpenGL ES 2.0 mode")
option(jenkins-tests "Configure CxxTest to use the XmlPrinter runner which produces Jenkins-compatible output")
option(minimal-flags "Only set compiler/linker flags that are really needed. Has no effect on Windows builds")
option(sanitize-address "Enable ASAN if available")
option(sanitize-thread "Enable TSAN if available")
option(sanitize-undefined-behaviour "Enable UBSAN if available")
option(with-system-cxxtest "Search standard paths for cxxtest, instead of using bundled copy")
option(with-lto "Enable Link Time Optimization (LTO)")
option(with-system-mozjs "Search standard paths for libmozjs115, instead of using bundled copy")
option(with-system-nvtt "Search standard paths for nvidia-texture-tools library, instead of using bundled copy")
option(with-valgrind "Enable Valgrind support (non-Windows only)")
option(without-audio "Disable use of OpenAL/Ogg/Vorbis APIs")
option(without-atlas "Disable Atlas scenario/map editor and ActorEditor")
option(without-dap-interface "Disable Dap interface project")
option(without-lobby "Disable the use of gloox and the multiplayer lobby")
option(without-miniupnpc "Disable use of miniupnpc for port forwarding")
option(without-nvtt "Disable use of NVTT")
option(without-pch "Disable generation and usage of precompiled headers")
option(without-tests "Disable generation of test projects")
# Windows specific options
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
option(fetch-prebuild-libs "Fetch prebuild libraries from SVN. Defaults to ON." ON)
endif()
# OS X specific options
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
option(macosx-version-min "Set minimum required version of the OS X API, the build will possibly fail if an older SDK is used, while newer API functions will be weakly linked (i.e. resolved at runtime)")
option(sysroot "Set compiler system root path, used for building against a non-system SDK. For example /usr/local becomes SYSROOT/user/local")
endif()
# Set the default build type if not specified (Only for single configuration generators)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Install options
set(bindir "" CACHE STRING "Directory for executables (typically '/usr/games'); default is to be relocatable")
set(datadir "" CACHE STRING "Directory for data files (typically '/usr/share/games/0ad'); default is ../data/ relative to executable")
set(libdir "" CACHE STRING "Directory for libraries (typically '/usr/lib/games/0ad'); default is ./ relative to executable")
# +++++++++++++++++++++ General Setup ++++++++++++++++++++
# Default Cache variables
# Append to the Modulepath. Allows to make custom cmake modules.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Set default Arch
set(ARCH "x86" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64")
set(MACOS_ARCH "x86_64" CACHE STRING "Mac OS specific Architecture. Possible values are arm64 and x86_64")
option(link_execinfo "")
option(mozjs_is_debug_build "")
# Detect CPU architecture (simplistic). The user can target an architecture by setting '-DARCH=arch', but the game still selects some know value.
if(android)
set(ARCH "arm" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64" FORCE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
if(NOT CMAKE_VS_PLATFORM_NAME)
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64" OR ENV{PROCESSOR_ARCHITEW6432} STREQUAL "AMD64")
set(ARCH "amd64" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64" FORCE)
endif()
else()
if(CMAKE_VS_PLATFORM_NAME MATCHES "x64")
set(ARCH "amd64" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64" FORCE)
endif()
endif()
else()
# could be parsed from the same command as premakes version
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "(x86_64|x64)")
set(MACOS_ARCH "x86_64" CACHE STRING "Mac OS specific Architecture. Possible values are arm64 and x86_64" FORCE)
elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
set(MACOS_ARCH "arm64" CACHE STRING "Mac OS specific Architecture. Possible values are arm64 and x86_64" FORCE)
endif()
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "(x86_64|x64)")
set(ARCH "amd64" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64" FORCE)
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "(aarch64|aarch64_be)")
set(ARCH "aarch64" CACHE STRING "CPU Architecture. Possible values are arm, aarch64, x86, amd64, e2k, ppc64, loong64, riscv64" FORCE)
else()
message(WARNING "Cannot determine architecture from GCC, assuming x86")
endif()
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
if(ARCH MATCHES "amd64")
set(0AD_EXT_LIBDIR ${CMAKE_SOURCE_DIR}/libraries/win64 CACHE STRING "Extern libraries directory of 0ad. Can be referenced in other CMakeLists.txt files.")
else()
set(0AD_EXT_LIBDIR ${CMAKE_SOURCE_DIR}/libraries/win32 CACHE STRING "Extern libraries directory of 0ad. Can be referenced in other CMakeLists.txt files.")
endif()
endif()
# On Windows check if wxWidgets is available, if not disable atlas and emit warning. This is because there are currently no prebuilt binaries provided.
if(NOT without-atlas AND CMAKE_SYSTEM_NAME STREQUAL "Windows")
if(NOT EXISTS "${0AD_EXT_LIBDIR}/wxwidgets/include/wx/wx.h")
message(STATUS "wxWidgets not found, disabling atlas")
set(without-atlas ON CACHE BOOL "Disable Atlas scenario/map editor and ActorEditor" FORCE)
endif()
endif()
# Test whether we need to link libexecinfo. This is mostly the case on musl systems, as well as on BSD systems : only glibc provides the
# backtrace symbols we require in the libc, for other libcs we use the libexecinfo library.
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(link_execinfo ON CACHE BOOL "" FORCE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
include(CheckCSourceCompiles)
file(READ ${CMAKE_SOURCE_DIR}/build/premake/tests/execinfo.c content)
check_c_source_compiles([[${content}]] LINK_EXECINFO)
if(NOT LINK_EXECINFO)
set(link_execinfo ON CACHE BOOL "" FORCE)
else()
set(link_execinfo OFF CACHE BOOL "" FORCE)
endif()
unset(LINK_EXECINFO)
unset(content)
endif()
# Test whether system mozjs is built with --enable-debug. The pc file doesn't specify the required -DDEBUG needed in that case
# Currently only working on bash shell!
if(with-system-mozjs)
execute_process(
COMMAND bash "-c" "${CMAKE_C_COMPILER} $(pkg-config mozjs-128 --cflags) ${CMAKE_SOURCE_DIR}/build/premake/tests/mozdebug.c -o /dev/null"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE errorCode
OUTPUT_QUIET
ERROR_QUIET
)
if(NOT errorCode EQUAL 0)
set(mozjs_is_debug_build ON CACHE BOOL "" FORCE)
else()
set(mozjs_is_debug_build OFF CACHE BOOL "" FORCE)
endif()
endif()
# Include the BuildFlags target only once after all setup is finished
include(0ad-BuildFlags)
# +++++++++++++++++++++ Windows specific ++++++++++++++++++++
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(SetupWindowsLibs)
endif()
# Add subprojects
if(NOT without-atlas)
add_subdirectory(${CMAKE_SOURCE_DIR}/source/tools/atlas/)
endif()
# Add doxygen target
if(build-docs)
add_subdirectory(${CMAKE_SOURCE_DIR}/docs/doxygen)
endif()

View file

@ -232,6 +232,12 @@ summary = "Ctrl+Tab" ; Toggle in-game summary
lobby = "Alt+L" ; Show the multiplayer lobby in a dialog window.
structree = "Alt+Shift+T" ; Show structure tree
civinfo = "Alt+Shift+H" ; Show civilization info
catafalque = "Alt+Shift+F" ; Show catafalque overview
mapbrowser = "Alt+Shift+M" ; Show map browser
manual = "Alt+Shift+U" ; Show manual
tips = "Alt+Shift+I" ; Show tips and tricks
options = "Alt+Shift+O" ; Show options
hotkeys = "Alt+Shift+Y" ; Show hotkeys
; > CLIPBOARD CONTROLS
copy = "Ctrl+C" ; Copy to clipboard
@ -388,9 +394,6 @@ defense_tower = "Space+W"
market = "Space+M"
dock = "Space+D"
[hotkey.gamesetup]
mapbrowser.open = "M"
[hotkey.session]
kill = Delete, Backspace ; Destroy selected units
stop = "H" ; Stop the current action

View file

@ -247,6 +247,7 @@ var g_CampaignMenu;
async function init(initData)
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]);
let run = initData?.filename || CampaignRun.getCurrentRunFilename();
try
{

View file

@ -43,7 +43,8 @@ class LoadModal extends AutoWatcher
[Engine.openRequest]: { "page": "page_pregame.xml" }
});
Engine.GetGUIObjectByName('deleteGameButton').onPress = () => this.deleteSelectedRun();
Engine.GetGUIObjectByName('startButton').onPress = () => this.startSelectedRun();
Engine.GetGUIObjectByName('startButton').onPress = () =>
this.startSelectedRun(closePageCallback);
this.noCampaignsText = Engine.GetGUIObjectByName("noCampaignsText");
@ -58,7 +59,8 @@ class LoadModal extends AutoWatcher
Engine.GetGUIObjectByName('runDescription').caption = this.currentRuns[this.selectedRun].getLabel();
};
this.runSelection.onMouseLeftDoubleClickItem = () => this.startSelectedRun();
this.runSelection.onMouseLeftDoubleClickItem = () =>
this.startSelectedRun(closePageCallback);
this._ready = true;
}
@ -143,5 +145,6 @@ var g_LoadModal;
function init()
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]);
return new Promise(closePageCallback => { g_LoadModal = new LoadModal(closePageCallback); });
}

View file

@ -45,6 +45,7 @@ function init(campaign_template_data)
{
return new Promise(closePageCallback =>
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]);
g_NewCampaignModal = new NewCampaignModal(campaign_template_data, closePageCallback);
});
}

View file

@ -75,6 +75,7 @@ var g_CampaignSetupPage;
function init()
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "manual", "tips", "mapbrowser"]);
return new Promise(closePageCallback =>
{
g_CampaignSetupPage = new CampaignSetupPage(closePageCallback);

View file

@ -325,4 +325,23 @@ function toPascalCase(str)
.split('_')
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
.join('');
}
/**
* Registers global hotkeys for opening GUI pages.
*
* Each hotkey opens a child page following the naming convention:
* page_${hotkey}.xml
*/
function registerGlobalGuiPageHotkeys(hotkeys)
{
for (const key of hotkeys)
{
const guiPage = `gui/page_${key}.xml`;
if (!Engine.FileExists(guiPage))
{
warn(`Skipping global hotkey '${key}': Missing GUI page '${guiPage}'.`);
continue;
}
Engine.SetGlobalHotkey(key, "Press", () => Engine.OpenChildPage(`page_${key}.xml`));
}
}

View file

@ -32,6 +32,8 @@ class NetMessages
while (true)
{
const message = await Engine.PollNetworkClient();
if (!message)
return;
log("Net message: " + uneval(message));

View file

@ -11,7 +11,7 @@ GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButt
}
this.button.tooltip = colorizeHotkey(this.HotkeyTooltip, this.HotkeyConfig);
Engine.SetGlobalHotkey(this.HotkeyConfig, "Press", this.onPress.bind(this));
this.button.hotkey = this.HotkeyConfig;
}
onSettingsLoaded()
@ -31,15 +31,20 @@ GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButt
onPress()
{
this.setupWindow.pages.MapBrowserPage.openPage(this.enabled);
const page = this.setupWindow.pages.MapBrowserPage;
if (!page.mapBrowserPage.hidden)
page.submitMapSelection();
else
page.openPage(this.enabled);
}
};
GameSettingControls.MapBrowser.prototype.HotkeyConfig =
"gamesetup.mapbrowser.open";
"mapbrowser";
GameSettingControls.MapBrowser.prototype.Caption =
translate("Browse Maps");
GameSettingControls.MapBrowser.prototype.HotkeyTooltip =
translate("Press %(hotkey)s to view the list of available maps.");
translate("%(hotkey)s: View the list of available maps.");

View file

@ -43,10 +43,15 @@ var g_SetupWindow;
function init(initData, hotloadData)
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "manual", "tips"]);
return Promise.race([new Promise(closePageCallback =>
{
g_SetupWindow = new SetupWindow(initData, hotloadData, closePageCallback);
}), ...(g_IsNetworked ? [g_SetupWindow.controls.netMessages.pollPendingMessages()] : [])]);
}), new Promise((_, reject) =>
{
if (g_IsNetworked)
g_SetupWindow.controls.netMessages.pollPendingMessages().catch(reject);
})]);
}
function getHotloadData()

View file

@ -72,7 +72,9 @@ async function waitOnEvent(loadSavedGame, joinFromLobby)
}));
if (tickResult === cancelTag)
break;
const result = await onTick(loadSavedGame);
const result = await cancelOr(onTick(loadSavedGame));
if (result === cancelTag)
break;
if (typeof result === "object")
return result;
if (result)
@ -240,7 +242,7 @@ function getConnectionFailReason(reason)
function reportConnectionFail(reason)
{
messageBox(
return messageBox(
400, 200,
(translate("Failed to connect to the server.")
) + "\n\n" + getConnectionFailReason(reason),
@ -253,6 +255,8 @@ async function pollAndHandleNetworkClient(loadSavedGame)
while (true)
{
const message = await Engine.PollNetworkClient();
if (!message)
return true;
if (!g_IsConnecting)
continue;
@ -268,7 +272,7 @@ async function pollAndHandleNetworkClient(loadSavedGame)
switch (message.status)
{
case "failed":
reportConnectionFail(message.reason, false);
await reportConnectionFail(message.reason, false);
return true;
default:
@ -326,7 +330,7 @@ async function pollAndHandleNetworkClient(loadSavedGame)
switch (message.status)
{
case "failed":
reportConnectionFail(message.reason, false);
await reportConnectionFail(message.reason, false);
return true;
default:

View file

@ -193,6 +193,7 @@ function init()
// suppress the warning.
/* eslint-disable-next-line no-new */
new HotkeysPage(new HotkeyMetadata(), closePageCallback);
Engine.SetGlobalHotkey("hotkeys", "Press", closePageCallback);
});
}

View file

@ -106,6 +106,38 @@
"mousegrabtoggle": {
"name": "Toggle mouse grab",
"desc": "Constrain to window boundaries or release the mouse."
},
"structree": {
"name": "Open Structure Tree window",
"desc": "Open Structure Tree window."
},
"civinfo": {
"name": "Open Civilization Overview window",
"desc": "Open Civilization Overview window."
},
"catafalque": {
"name": "Open Catafalque Overview",
"desc": "Open Catafalque Overview window."
},
"mapbrowser": {
"name": "Open Map Browser window",
"desc": "Open Map Browser window."
},
"manual": {
"name": "Open the 0 A.D. Game Manual",
"desc": "Open the 0 A.D. Game Manual."
},
"tips": {
"name": "Open Tips and Tricks window",
"desc": "Open Tips and Tricks window."
},
"hotkeys": {
"name": "Open Hotkeys window",
"desc": "Open Hotkeys window."
},
"options": {
"name": "Open Options window",
"desc": "Open Options window."
}
}
}

View file

@ -39,14 +39,6 @@
"name": "Toggle MP lobby",
"desc": "Show the multiplayer lobby in a dialog window."
},
"structree": {
"name": "Toggle structure tree",
"desc": "Toggle the structure tree."
},
"civinfo": {
"name": "Toggle civ info",
"desc": "Toggle the civilization info."
},
"session.gui.menu.toggle": {
"name": "Toggle in-game menu",
"desc": "Toggle in-game menu."

View file

@ -36,6 +36,7 @@ async function init(attribs)
if (g_Settings)
return new Promise(closePageCallback =>
{
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]);
g_LobbyHandler = new LobbyHandler(closePageCallback, attribs && attribs.dialog);
});

View file

@ -11,5 +11,6 @@ function init()
return new Promise(closePageCallback =>
{
Engine.GetGUIObjectByName("closeButton").onPress = closePageCallback;
Engine.SetGlobalHotkey("manual", "Press", closePageCallback);
});
}
}

View file

@ -10,5 +10,9 @@ function init()
const browser = new MapBrowser(cache, filters);
browser.openPage(false);
browser.controls.MapFiltering.select("default", "skirmish");
return new Promise(closePageCallback => { browser.registerClosePageHandler(closePageCallback); });
}
Engine.SetGlobalHotkey("mapbrowser", "Press", () => browser.closePage());
return new Promise(closePageCallback =>
{
browser.registerClosePageHandler(closePageCallback);
});
}

View file

@ -234,7 +234,9 @@ async function init(data, hotloadData)
g_Options = Engine.ReadJSONFile("gui/options/options.json");
translateObjectKeys(g_Options, ["label", "tooltip"]);
deepfreeze(g_Options);
Engine.SetGlobalHotkey("options", "Press", () =>
Engine.GetGUIObjectByName("closeButton").onPress()
);
placeTabButtons(
g_Options,
false,

View file

@ -5,7 +5,8 @@ export const mainMenuItems = [
"submenu": [
{
"caption": translate("Manual"),
"tooltip": translate("Open the 0 A.D. Game Manual."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Open the 0 A.D. Game Manual."), "manual"),
"hotkey": "manual",
"onPress": () =>
{
Engine.OpenChildPage("page_manual.xml");
@ -41,7 +42,8 @@ export const mainMenuItems = [
},
{
"caption": translate("Tips and Tricks"),
"tooltip": translate("Discover simple tips, tricks, and game mechanics."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Discover simple tips, tricks, and game mechanics."), "tips"),
"hotkey": "tips",
"onPress": Engine.OpenChildPage.bind(null, "page_tips.xml", {
"tipScrolling": true
})
@ -66,7 +68,8 @@ export const mainMenuItems = [
},
{
"caption": translate("Catafalque Overview"),
"tooltip": translate("Compare the bonuses of catafalques featured in 0 A.D."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Compare the bonuses of catafalques featured in 0 A.D."), "catafalque"),
"hotkey": "catafalque",
"onPress": () =>
{
Engine.OpenChildPage("page_catafalque.xml");
@ -74,7 +77,8 @@ export const mainMenuItems = [
},
{
"caption": translate("Map Overview"),
"tooltip": translate("View the different maps featured in 0 A.D."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: View the different maps featured in 0 A.D."), "mapbrowser"),
"hotkey": "mapbrowser",
"onPress": () =>
{
Engine.OpenChildPage("page_mapbrowser.xml");
@ -295,7 +299,8 @@ export const mainMenuItems = [
"submenu": [
{
"caption": translate("Options"),
"tooltip": translate("Adjust game settings."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Adjust game settings."), "options"),
"hotkey": "options",
"onPress": async() =>
{
fireConfigChangeHandlers(await Engine.OpenChildPage("page_options.xml"));
@ -303,10 +308,11 @@ export const mainMenuItems = [
},
{
"caption": translate("Hotkeys"),
"tooltip": translate("Adjust hotkeys."),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Adjust hotkeys."), "hotkeys"),
"hotkey": "hotkeys",
"onPress": () =>
{
Engine.OpenChildPage("hotkeys/page_hotkeys.xml");
Engine.OpenChildPage("page_hotkeys.xml");
}
},
{

View file

@ -1,4 +1,8 @@
function init(data = {})
{
return new Promise(closePageCallback => { g_Page = new CatafalquePage(closePageCallback); });
return new Promise(closePageCallback =>
{
g_Page = new CatafalquePage(closePageCallback);
Engine.SetGlobalHotkey("catafalque", "Press", closePageCallback);
});
}

View file

@ -2,6 +2,8 @@ class TipsPage
{
constructor(initData, hotloadData, closePageCallback)
{
initData = { "tipScrolling": true, ...initData };
this.closePageCallback = closePageCallback;
this.tipDisplay = new TipDisplay(initData, hotloadData);
this.closeButton = new CloseButton(this);

View file

@ -5,6 +5,7 @@ function init(initData, hotloadData)
return new Promise(closePageCallback =>
{
g_TipsPage = new TipsPage(initData, hotloadData, closePageCallback);
Engine.SetGlobalHotkey("tips", "Press", closePageCallback);
});
}

View file

@ -77,7 +77,7 @@ async function init(data)
"page": "page_pregame.xml"
} };
}
registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]);
initHotkeyTooltips();
displayReplayList();

View file

@ -13,7 +13,16 @@ MenuButtons.prototype.Manual = class
{
this.button = button;
this.button.caption = translate(translate("Manual"));
this.button.hotkey = "manual";
this.pauseControl = pauseControl;
registerHotkeyChangeHandler(this.rebuild.bind(this));
}
rebuild()
{
this.button.tooltip = sprintf(translate("%(hotkey)s: Open the 0 A.D. game manual."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}
async onPress()
@ -88,7 +97,7 @@ MenuButtons.prototype.Summary = class
rebuild()
{
this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the summary screen."), {
this.button.tooltip = sprintf(translate("%(hotkey)s: Open the summary screen."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}
@ -139,7 +148,7 @@ MenuButtons.prototype.Lobby = class
rebuild()
{
this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the multiplayer lobby page without leaving the game."), {
this.button.tooltip = sprintf(translate("%(hotkey)s: Open the multiplayer lobby page without leaving the game."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}
@ -159,7 +168,16 @@ MenuButtons.prototype.Options = class
{
this.button = button;
this.button.caption = translate("Options");
this.button.hotkey = "options";
this.pauseControl = pauseControl;
registerHotkeyChangeHandler(this.rebuild.bind(this));
}
rebuild()
{
this.button.tooltip = sprintf(translate("%(hotkey)s: Adjust game settings."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}
async onPress()
@ -178,7 +196,16 @@ MenuButtons.prototype.Hotkeys = class
{
this.button = button;
this.button.caption = translate("Hotkeys");
this.button.hotkey = "hotkeys";
this.pauseControl = pauseControl;
registerHotkeyChangeHandler(this.rebuild.bind(this));
}
rebuild()
{
this.button.tooltip = sprintf(translate("%(hotkey)s: Adjust hotkeys."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}
async onPress()
@ -186,7 +213,7 @@ MenuButtons.prototype.Hotkeys = class
closeOpenDialogs();
this.pauseControl.implicitPause();
await Engine.OpenChildPage("hotkeys/page_hotkeys.xml");
await Engine.OpenChildPage("page_hotkeys.xml");
resumeGame();
}
};
@ -211,7 +238,7 @@ MenuButtons.prototype.Pause = class
{
this.button.enabled = this.pauseControl.canPause(true);
this.button.caption = this.pauseControl.explicitPause ? translate("Resume") : translate("Pause");
this.button.tooltip = sprintf(translate("Press %(hotkey)s to pause or resume the game."), {
this.button.tooltip = sprintf(translate("%(hotkey)s: Pause or resume the game."), {
"hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey),
});
}

View file

@ -425,6 +425,8 @@ async function handleNetMessages()
while (true)
{
const msg = await Engine.PollNetworkClient();
if (!msg)
return;
log("Net message: " + uneval(msg));

View file

@ -316,21 +316,38 @@ async function init(initData, hotloadData)
updatePlayerData();
initializeMusic(); // before changing the perspective
Engine.SetBoundingBoxDebugOverlay(false);
Engine.SetGlobalHotkey("catafalque", "Press", async() =>
{
closeOpenDialogs();
g_PauseControl.implicitPause();
await Engine.OpenChildPage("page_catafalque.xml");
resumeGame();
});
Engine.SetGlobalHotkey("tips", "Press", async() =>
{
closeOpenDialogs();
g_PauseControl.implicitPause();
await Engine.OpenChildPage("page_tips.xml");
resumeGame();
});
const promise = Promise.race([g_IsNetworked ? handleNetMessages() : new Promise(() => {}),
new Promise(closePageCallback =>
{
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates.bind(undefined,
closePageCallback));
g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closePageCallback);
g_NetworkStatusOverlay = new NetworkStatusOverlay(closePageCallback);
g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closePageCallback);
g_QuitConfirmationReplay = new QuitConfirmationReplay(closePageCallback);
// TODO: use event instead
onSimulationUpdate(closePageCallback);
Engine.GetGUIObjectByName("session").onSimulationUpdate =
onSimulationUpdate.bind(undefined, closePageCallback);
})]);
const promise = Promise.race([new Promise((_, reject) =>
{
if (g_IsNetworked)
handleNetMessages().catch(reject);
}), new Promise(closePageCallback =>
{
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates.bind(undefined,
closePageCallback));
g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closePageCallback);
g_NetworkStatusOverlay = new NetworkStatusOverlay(closePageCallback);
g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closePageCallback);
g_QuitConfirmationReplay = new QuitConfirmationReplay(closePageCallback);
// TODO: use event instead
onSimulationUpdate(closePageCallback);
Engine.GetGUIObjectByName("session").onSimulationUpdate =
onSimulationUpdate.bind(undefined, closePageCallback);
})]);
for (const handler of g_PlayersInitHandlers)
handler();

View file

@ -96,7 +96,7 @@ if _OPTIONS["android"] then
elseif os.istarget("windows") then
if os.getenv("HOSTTYPE") then
arch = os.getenv("HOSTTYPE")
elseif os.getenv("PROCESSOR_ARCHITECTURE") == "amd64" or os.getenv("PROCESSOR_ARCHITEW6432") == "amd64" then
elseif os.getenv("PROCESSOR_ARCHITECTURE") == "AMD64" or os.getenv("PROCESSOR_ARCHITEW6432") == "AMD64" then
arch = "amd64"
end
else

303
cmake/0ad-BuildFlags.cmake Normal file
View file

@ -0,0 +1,303 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
# Add an interface library with common set of build/linker flags and definitions.
set(BUILD_FLAGS_TARGET BuildFlags)
add_library(${BUILD_FLAGS_TARGET} INTERFACE)
# Enable and require C++20 standard.
target_compile_features(${BUILD_FLAGS_TARGET} INTERFACE cxx_std_20)
set_target_properties(${BUILD_FLAGS_TARGET}
PROPERTIES
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
# Settings for build types
if(with-lto)
include(CheckIPOSupported)
check_ipo_supported(RESULT lto_supported OUTPUT lto_supported_error)
if(lto_supported)
set_target_properties(${BUILD_FLAGS_TARGET}
PROPERTIES
INTERPROCEDURAL_OPTIMIZATION TRUE
)
else()
message(WARNING "IPO / LTO not supported: <${lto_supported_error}>")
endif()
endif()
target_compile_definitions(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CONFIG:Release>:NDEBUG>
$<$<CONFIG:Release>:CONFIG_FINAL=1>
$<$<CONFIG:Debug>:DEBUG>
)
if(sanitize-address)
target_compile_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=address)
target_link_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=address)
endif()
if(sanitize-thread)
target_compile_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=thread)
target_link_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=thread)
endif()
if(sanitize-undefined-behaviour)
target_compile_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=undefined)
target_link_options(${BUILD_FLAGS_TARGET} INTERFACE -fsanitize=undefined)
endif()
if(mozjs_is_debug_build)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE DEBUG)
endif()
if(gles)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_GLES=1)
endif()
if(without-audio)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_AUDIO=0)
endif()
if(without-nvtt)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_NVTT=0)
endif()
if(without-lobby)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_LOBBY=0)
endif()
if(without-miniupnpc)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_MINIUPNPC=0)
endif()
if(without-dap-interface)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE CONFIG2_DAP_INTERFACE=0)
endif()
# hide warnings caused by library includes (Nothing to do for gcc/clang)
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:MSVC>:/external:W0>
)
# various platform-specific build flags
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:MSVC>:/MP>
# Since KB4088875 Windows 7 has a soft requirement for SSE2.
# Windows 8+ and Firefox ESR52 make it hard requirement.
# Finally since VS2012 it's enabled implicitely when not set.
$<$<CXX_COMPILER_ID:MSVC>:/arch:SSE2>
# use native wchar_t type (not typedef to unsigned short)
$<$<CXX_COMPILER_ID:MSVC>:/Zc:wchar_t>
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
# SpiderMonkey only supports building with MSVC on a best-effort basis,
# and the traditional MSVC preprocessor is incompatible with some headers.
# Use the modern, standard-compliant MSVC preprocessor instead.
$<$<CXX_COMPILER_ID:MSVC>:/Zc:preprocessor>
# enable most of the standard warnings
$<$<CXX_COMPILER_ID:MSVC>:/W4>
# FIXME: conversion warnings, should add -Wconversion to gcc and clang flags as well
$<$<CXX_COMPILER_ID:MSVC>:/wd4267>
$<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:MSVC>>:/O2>
)
# disable LNK4221 warning, to avoid spending energy ordering projects in linker invocations
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
"/ignore:4221"
)
# mozilla 115 linked list destructor in debug build
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE "__PRETTY_FUNCTION__=__FUNCSIG__")
# disable Windows debug heap, since it makes malloc/free hugely slower when running inside a debugger
set_target_properties(${BUILD_FLAGS_TARGET}
PROPERTIES
VS_DEBUGGER_ENVIRONMENT "_NO_DEBUG_HEAP=1"
)
elseif(UNIX)
if(NOT minimal-flags)
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
# most of the standard warnings
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wall>
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wextra>
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wpedantic>
# $<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wconversion> # FIXME: should seriously consider fixing so this warning can be enabled.
# add some other useful warnings that need to be enabled explicitly
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wunused-parameter> # (useful for finding some multiply-included header files)
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wredundant-decls>
# $<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wformat=2> # (useful sometimes, but a bit noisy, so skip it by default)
# $<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wcast-qual> # (useful for checking const-correctness, but a bit noisy, so skip it by default)
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wnon-virtual-dtor> # (sometimes noisy but finds real bugs)
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wundef> # (useful for finding macro name typos)
# disable some warnings that currently trigger
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wno-missing-field-initializers> # (this is common in external headers we can't fix)
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wno-reorder> # order of initialization list in constructors (lots of noise)
# enable security features (stack checking etc) that shouldn't have a significant effect on performance and can catch bugs
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fstack-protector-strong>
# always enable strict aliasing (useful in debug builds because of the warnings)
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fstrict-aliasing>
# don't omit frame pointers (for now), because performance will be impacted negatively by the way this breaks profilers more than it will be impacted positively by the optimisation
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fno-omit-frame-pointer>
# FORTIFY_SOURCE needs optimizations to be enabled
$<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang,AppleClang,GNU>>:-U_FORTIFY_SOURCE> # (avoid redefinition warning if already defined)
$<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang,AppleClang,GNU>>:-D_FORTIFY_SOURCE=2>
)
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<PLATFORM_ID:Darwin>:-multiply_defined>
$<$<PLATFORM_ID:Darwin>:suppress>
)
if(NOT without-pch)
# do something (?) so that ccache can handle compilation with PCH enabled (ccache 3.1+ also requires CCACHE_SLOPPINESS=time_macros for this to work)
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fpch-preprocess>
)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_NAME MATCHES ".*BSD*")
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fPIC>
)
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-Wl,--no-undefined"
"-Wl,--as-needed"
"-Wl,-z,relro"
)
endif()
if(ARCH STREQUAL "x86")
# To support intrinsics like __sync_bool_compare_and_swap on x86 we need to set -march to something that supports them (i686).
# We use pentium3 to also enable other features like mmx and sse, while tuning for generic to have good performance on every supported CPU.
# Note that all these features are already supported on amd64.
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-march=pentium3>
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-mtune=generic>
# This allows x86 operating systems to handle the 2GB+ INTERFACE mod.
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-mtune=generic-D_FILE_OFFSET_BITS=64>
)
endif()
if(ARCH STREQUAL "arm")
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
# disable warnings about va_list ABI change and use compile-time flags for futher configuration.
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-Wno-psabi>
# Android uses softfp, so we should too.
$<$<AND:$<CXX_COMPILER_ID:Clang,AppleClang,GNU>,$<BOOL:${android}>>:-mfloat-abi=softfp>
)
endif()
endif()
if(coverage)
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-fprofile-arcs>
$<$<CXX_COMPILER_ID:Clang,AppleClang,GNU>:-ftest-coverage>
)
target_link_libraries(${BUILD_FLAGS_TARGET} gcov)
endif()
# MacOS 10.12 only supports intel processors with SSE 4.1, so enable that.
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND ARCH STREQUAL "amd64")
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-msse4.1"
)
endif()
# Check if SDK path should be used
if(sysroot)
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-isysroot ${sysroot}"
)
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-Wl,-syslibroot, ${sysroot}"
)
endif()
# On OS X, sometimes we need to specify the minimum API version to use
if(macosx-version-min)
# clang and llvm-gcc look at mmacosx-version-min to determine link target and CRT version, and use it to set the macosx_version_min linker flag
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-mmacosx-version-min=${macosx-version-min}"
)
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
"-mmacosx-version-min=${macosx-version-min}"
)
endif()
# Only libc++ is supported on MacOS
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
-stdlib=libc++
)
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
-stdlib=libc++
)
endif()
# Hide symbols in dynamic shared objects by default, for efficiency and for equivalence with Windows
# - they should be exported explicitly with __attribute__ ((visibility ("default")))
target_compile_options(${BUILD_FLAGS_TARGET}
INTERFACE
-fvisibility=hidden
)
if(bindir)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE INSTALLED_BINDIR=${bindir})
endif()
if(datadir)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE INSTALLED_DATADIR=${datadir})
endif()
if(libdir)
target_compile_definitions(${BUILD_FLAGS_TARGET} INTERFACE INSTALLED_LIBDIR=${libdir})
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME MATCHES ".*BSD*")
if(libdir)
# To use our local shared libraries, they need to be found in the runtime dynamic linker path. Add their path to -rpath.
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
-Wl,-rpath,${libdir}
)
else()
# On FreeBSD we need to allow use of $ORIGIN
if(CMAKE_SYSTEM_NAME MATCHES ".*BSD*")
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
-Wl,-z,origin
)
endif()
# Adding the executable path and taking care of correct escaping
if(NOT CMAKE_GENERATOR STREQUAL "Ninja") # Checks if any Makefile generator is selected
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
-Wl,-rpath,'$$ORIGIN'
)
elseif(CMAKE_EXTRA_GENERATOR STREQUAL "CodeBlocks")
target_link_options(${BUILD_FLAGS_TARGET}
INTERFACE
-Wl,-R\\\\$$$ORIGIN
)
endif()
endif()
endif()
endif()

56
cmake/0ad-Functions.cmake Normal file
View file

@ -0,0 +1,56 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
# 0AD Specific Functions
# Add Precompiled Headers. If no target is given, this macro will fail with a FATAL_ERROR.
# rationale: we need one PCH per static lib, since one global header would increase dependencies. To that end, we can either include them as
# "projectdir/precompiled.h", or add "source/PCH/projectdir" to the include path and put the PCH there. The latter is better because many
# projects contain several dirs and it's unclear where there the PCH should be stored. This way is also a bit easier to use in that
# source files always include "precompiled.h".
# Notes:
# * Visual Assist manages to use the project include path and can correctly open these files from the IDE.
# * precompiled.cpp (needed to "Create" the PCH) also goes in the abovementioned dir.
# * using CMakes precompiled Header is not possible, as it does not allow for a custom name like used here.
function(add_pch)
set(single_args TARGET PCH_DIR)
cmake_parse_arguments(args "" "${single_args}" "" ${ARGN})
if(NOT args_TARGET)
message(FATAL_ERROR "add_pch: Missing target!!")
endif()
include(PrecompiledHeader)
if(NOT args_PCH_DIR)
get_target_property(source_root ${args_TARGET} SOURCE_DIR)
set(args_PCH_DIR ${source_root}/pch/${args_TARGET})
endif()
# Put the project-specific PCH directory at the start of the include path, so '#include "precompiled.h"' will look in there first
target_include_directories(${args_TARGET}
BEFORE PRIVATE
${args_PCH_DIR}/
)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
set(pch_header "precompiled.h")
elseif(CMAKE_GENERATOR MATCHES "Xcode")
set(pch_header "../${args_PCH_DIR}/precompiled.h")
else()
set(pch_header "${args_PCH_DIR}/precompiled.h")
endif()
precompile_header(${args_TARGET} ${pch_header} ${args_PCH_DIR}/precompiled.cpp)
target_sources(${args_TARGET}
PRIVATE
${args_PCH_DIR}/precompiled.cpp
${args_PCH_DIR}/precompiled.h
)
target_compile_definitions(${args_TARGET}
PRIVATE
CONFIG_ENABLE_PCH=1
)
endfunction(add_pch)
function(test_generator_expression EXPRESSION)
add_custom_target(genexdebug
COMMAND ${CMAKE_COMMAND} -E echo "${EXPRESSION}"
)
endfunction(test_generator_expression)

View file

@ -0,0 +1,54 @@
function(find_prebuild_library _name)
include(FindPackageHandleStandardArgs)
set(multi_args COMPONENTS PATHS)
set(single_args INC_PATH VERSION)
cmake_parse_arguments(args "${multi_args}" "${single_args}" "" ${ARGN})
string(TOLOWER ${_name} libname)
string(TOUPPER ${_name} target_name)
# ++++++++ Handle COMPONENTS +++++++++++++++++++++
if(args_COMPONENTS)
endif()
# ++++++++ Find relevant elements ++++++++++++++++
find_library(Lib${_name} NAMES ${libname} ${libname}${args_VERSION})
if(NOT args_INC_PATH)
find_path(Lib${_name}_inc NAMES ${libname}.h PATHS ${0AD_EXT_LIBDIR}/${_name}/include/)
# recursive add all paths beneth included...
else()
set(Lib${_name}_inc ${args_INC_PATH})
endif()
if(NOT Lib${_name}_inc)
set(Lib${_name}_inc ${0AD_EXT_LIBDIR}/${_name}/include/)
endif()
find_package_handle_standard_args(Lib${_name} REQUIRED_VARS Lib${_name})
message(STATUS "${Lib${_name}} - ${Lib${_name}_inc} - ${Lib${_name}_FOUND} - ${target_name}::${target_name}")
if (Lib${_name}_FOUND)
mark_as_advanced(
Lib${_name}
Lib${_name}_inc
)
endif()
if(Lib${_name} MATCHES "LibBoost")
add_library(${target_name}::headers INTERFACE IMPORTED)
set_target_properties(${target_name}::headers PROPERTIES
IMPORTED_LOCATION ${Lib${_name}_inc}
INTERFACE_INCLUDE_DIRECTORIES ${Lib${_name}_inc}
)
elseif(Lib${_name} MATCHES "SDL2")
add_library(${target_name}::${target_name} UNKNOWN IMPORTED)
set_target_properties(${target_name}::${target_name} PROPERTIES
IMPORTED_LOCATION ${Lib${_name}}
INTERFACE_INCLUDE_DIRECTORIES ${Lib${_name}_inc}/SDL
)
elseif (Lib${_name} AND NOT TARGET ${target_name}::${target_name})
add_library(${target_name}::${target_name} UNKNOWN IMPORTED)
set_target_properties(${target_name}::${target_name} PROPERTIES
IMPORTED_LOCATION ${Lib${_name}}
INTERFACE_INCLUDE_DIRECTORIES ${Lib${_name}_inc}
)
endif()
endfunction()

View file

@ -0,0 +1,59 @@
function(precompile_header _target _header _source)
set(single_args FORCE_INCLUDE)
cmake_parse_arguments(args "" "${single_args}" "" ${ARGN})
get_target_property(target_compiler_FLAGS ${_target} COMPILE_OPTIONS)
get_target_property(target_bindir ${_target} BINARY_DIR)
set(gcc_out ${target_bindir}/${_header}.gch)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message(STATUS "Precompiled: ${_target} ${_header} ${_source}")
# message(STATUS "Precompiled: ${_target} ${_header} ${_source}")
# target_compile_options(${_target}
# PRIVATE
# /Yu${_header}
# )
# set_source_files_properties(${_source}/precompiled.cpp PROPERTIES
# COMPILE_FLAGS
# /Yc${_header}
# )
# if(${args_FORCE_INCLUDE})
# set_source_files_properties(${_source}/precompiled.cpp
# PROPERTIES
# COMPILE_FLAGS
# /FI${_header}
# )
# endif()
# get_source_file_property(tmp ${_source}/precompiled.cpp COMPILE_FLAGS)
# message(STATUS "Property: ${tmp}")
# include(CMakePrintHelpers)
# cmake_print_properties(TARGETS ${_target}
# PROPERTIES
# # INCLUDE_DIRECTORIES
# # LINK_LIBRARIES
# COMPILE_OPTIONS
# # COMPILE_DEFINITIONS
# # WIN32_EXECUTABLE
# # MACOSX_BUNDLE
# # IMPORTED_LOCATION
# # INTERFACE_INCLUDE_DIRECTORIES
# # CXX_EXTENSIONS
# # CXX_STANDARD_REQUIRED
# )
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|AppleClang")
message(STATUS "C++")
add_custom_command(
OUTPUT "${gcc_out}"
COMMAND ${CMAKE_CXX_COMPILER} "${target_compiler_FLAGS}" -x c++-header -o "${gcc_out}" -c "${_header}"
DEPENDS "${_header}"
COMMENT "Precompiling ${_header} for ${_target} (C++)"
)
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU|AppleClang")
message(STATUS "C")
add_custom_command(
OUTPUT "${gcc_out}"
COMMAND ${CMAKE_C_COMPILER} "${target_compiler_FLAGS}" -x c-header -o "${gcc_out}" -c "${_header}"
DEPENDS "${_header}"
COMMENT "Precompiling ${_header} for ${_target} (C)"
)
endif()
endfunction()

View file

@ -0,0 +1,33 @@
# Checks out the SVN Revision of windows libraries. Additionally sets up the environment for cmake.
# To update the libraries change the 'SVN_REVISION'. Important: the '-r' in the Revision must be retained.
message(STATUS "Fetching Windows prebuild Libraries for ${ARCH}")
if(fetch-prebuild-libs)
include(FetchContent)
if(ARCH STREQUAL "amd64")
set(REPO_NAME windows-libs-amd64)
else()
set(REPO_NAME windows-libs)
endif()
FetchContent_Populate(
prebuild_libs
SVN_REPOSITORY https://svn.wildfiregames.com/public/${REPO_NAME}/trunk
SVN_REVISION -r28278
SOURCE_DIR ${0AD_EXT_LIBDIR}
)
endif()
message(STATUS "Copy dependencies' binaries to 'binaries/system/' and adding to 'CMAKE_PREFIX_PATH'")
set(DIR_LIST cpp-httplib enet fcollada freetype gloox iconv icu libcurl libpng libsodium libxml2 microsoft miniupnpc nvtt openal sdl2 spidermonkey vorbis zlib)
foreach(dir ${DIR_LIST})
file(COPY ${0AD_EXT_LIBDIR}/${dir}/bin DESTINATION ${CMAKE_SOURCE_DIR}/binaries/system)
list(APPEND CMAKE_PREFIX_PATH ${0AD_EXT_LIBDIR}/${dir})
endforeach()
# Add the whole libraries directory to CMAKE_PREFIX_PATH. May be redundand but needed for wxWidgets
list(APPEND CMAKE_PREFIX_PATH ${0AD_EXT_LIBDIR})
# Add libraries not set during binary copy
list(APPEND CMAKE_PREFIX_PATH ${0AD_EXT_LIBDIR}/fmt)
list(APPEND CMAKE_PREFIX_PATH ${0AD_EXT_LIBDIR}/libzip)
list(APPEND CMAKE_PREFIX_PATH ${0AD_EXT_LIBDIR}/cxxtest-4.4)
message(STATUS "Copy build tools to 'build/bin'")
file(COPY ${0AD_EXT_LIBDIR}/cxxtest-4.4/bin DESTINATION ${CMAKE_SOURCE_DIR}/build/bin)

View file

@ -6,7 +6,11 @@
## Building the Doxygen documentation
To generate the Doxygen documentation: run "cmake -S . -B output && cmake --build output".
To generate the Doxygen documentation:
```console
cmake -S 0ad/rootdir -B output && cmake --build output --target docs
```
If you build the documentation with cmake, the output is located in the folder html inside your
specified build directory.

View file

@ -1,64 +1,66 @@
cmake_minimum_required(VERSION 3.18.4...3.28.0)
project(Pyrogenesis DESCRIPTION "Pyrogenesis, a RTS Engine" LANGUAGES NONE)
cmake_minimum_required(VERSION 3.25.1...4.0.0)
# Check if Doxygen and graphviz are installed.
find_package(Doxygen 1.9.1 REQUIRED dot)
if(DOXYGEN_FOUND)
include(FetchContent)
include(FetchContent)
FetchContent_Declare(doxygen_awesome_css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG v2.3.3
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/styling
)
FetchContent_MakeAvailable(doxygen_awesome_css)
message(STATUS "Fetching doxygen_awesome_css")
FetchContent_Declare(doxygen_awesome_css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG v2.4.2
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/styling
)
FetchContent_MakeAvailable(doxygen_awesome_css)
# Get current Branch Name to set it as the Project Number.
find_package(Git)
if(Git_FOUND)
set(ENV{GIT_DISCOVERY_ACROSS_FILESYSTEM} 1)
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --is-inside-work-tree OUTPUT_VARIABLE IS_GIT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(IS_GIT)
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE CURRENT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
endif()
# Get current Branch Name to set it as the Project Number.
find_package(Git)
if(Git_FOUND)
set(ENV{GIT_DISCOVERY_ACROSS_FILESYSTEM} 1)
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --is-inside-work-tree OUTPUT_VARIABLE IS_GIT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(IS_GIT)
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE CURRENT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
endif()
# Doxygen Configuration.
if(CURRENT_BRANCH)
set(DOXYGEN_PROJECT_NUMBER ${CURRENT_BRANCH})
else()
set(DOXYGEN_PROJECT_NUMBER main)
endif()
set(DOXYGEN_PROJECT_LOGO ${CMAKE_CURRENT_SOURCE_DIR}/pyrogenesis.png)
set(DOXYGEN_TAB_SIZE 4)
set(DOXYGEN_EXCLUDE_PATTERNS */.svn* */tests/test_*)
set(DOXYGEN_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../source)
set(DOXYGEN_EXAMPLE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../source)
set(DOXYGEN_EXCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../../source/tools ${CMAKE_CURRENT_SOURCE_DIR}/../../source/third_party)
set(DOXYGEN_GENERATE_TREEVIEW YES)
set(DOXYGEN_HTML_EXTRA_STYLESHEET ${doxygen_awesome_css_SOURCE_DIR}/doxygen-awesome.css ${CMAKE_CURRENT_SOURCE_DIR}/style.css)
set(DOXYGEN_JAVADOC_AUTOBRIEF YES)
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_PRIVATE YES)
set(DOXYGEN_EXTRACT_STATIC YES)
set(DOXYGEN_EXTRACT_ANON_NSPACES YES)
set(DOXYGEN_SHOW_DIRECTORIES YES)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
set(DOXYGEN_MACRO_EXPANSION YES)
set(DOXYGEN_EXPAND_ONLY_PREDEF YES)
set(DOXYGEN_GENERATE_TODOLIST NO)
set(DOXYGEN_PREDEFINED "UNUSED(x)=x" "METHODDEF(x)=static x" "GLOBAL(x)=x")
set(DOXYGEN_EXPAND_AS_DEFINED DEFAULT_COMPONENT_ALLOCATOR DEFAULT_SCRIPT_WRAPPER DEFAULT_INTERFACE_WRAPPER DEFAULT_MESSAGE_IMPL MESSAGE INTERFACE COMPONENT GUISTDTYPE)
set(DOXYGEN_WARN_LOGFILE doxygen.log)
# Doxygen Configuration.
set(DOXYGEN_PROJECT_NAME "Pyrogenesis")
set(DOXYGEN_PROJECT_BRIEF "Pyrogenesis, a RTS Engine")
if(CURRENT_BRANCH)
set(DOXYGEN_PROJECT_NUMBER ${CURRENT_BRANCH})
else()
set(DOXYGEN_PROJECT_NUMBER main)
endif()
set(DOXYGEN_PROJECT_LOGO ${CMAKE_CURRENT_SOURCE_DIR}/pyrogenesis.png)
set(DOXYGEN_TAB_SIZE 4)
set(DOXYGEN_EXCLUDE_PATTERNS */.svn* */tests/test_*)
set(DOXYGEN_INCLUDE_PATH ${CMAKE_SOURCE_DIR}/source)
set(DOXYGEN_EXAMPLE_PATH ${CMAKE_SOURCE_DIR}/source)
set(DOXYGEN_EXCLUDE ${CMAKE_SOURCE_DIR}/source/tools ${CMAKE_SOURCE_DIR}/source/third_party)
set(DOXYGEN_GENERATE_TREEVIEW YES)
set(DOXYGEN_HTML_EXTRA_STYLESHEET ${doxygen_awesome_css_SOURCE_DIR}/doxygen-awesome.css ${CMAKE_CURRENT_SOURCE_DIR}/style.css)
set(DOXYGEN_JAVADOC_AUTOBRIEF YES)
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_PRIVATE YES)
set(DOXYGEN_EXTRACT_STATIC YES)
set(DOXYGEN_EXTRACT_ANON_NSPACES YES)
set(DOXYGEN_SHOW_DIRECTORIES YES)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
set(DOXYGEN_MACRO_EXPANSION YES)
set(DOXYGEN_EXPAND_ONLY_PREDEF YES)
set(DOXYGEN_GENERATE_TODOLIST NO)
set(DOXYGEN_PREDEFINED "UNUSED(x)=x" "METHODDEF(x)=static x" "GLOBAL(x)=x")
set(DOXYGEN_EXPAND_AS_DEFINED DEFAULT_COMPONENT_ALLOCATOR DEFAULT_SCRIPT_WRAPPER DEFAULT_INTERFACE_WRAPPER DEFAULT_MESSAGE_IMPL MESSAGE INTERFACE COMPONENT GUISTDTYPE)
set(DOXYGEN_WARN_LOGFILE doxygen.log)
doxygen_add_docs(${CMAKE_PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/../../source
${CMAKE_CURRENT_SOURCE_DIR}/mainpage.dox
${CMAKE_CURRENT_SOURCE_DIR}/../../LICENSE.md
ALL)
doxygen_add_docs(docs
${CMAKE_SOURCE_DIR}/source
${CMAKE_CURRENT_SOURCE_DIR}/mainpage.dox
${CMAKE_SOURCE_DIR}/LICENSE.md
COMMENT "Creating Doxygen for the engine."
)
else()
message(SEND_ERROR "Make sure Doxygen is installed and usable")
message(SEND_ERROR "Make sure Doxygen is installed and usable")
endif()

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -42,6 +42,7 @@
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/system/Component.h"
#include <SDL_events.h>
#include <algorithm>
#include <cmath>
#include <functional>
@ -654,13 +655,13 @@ void CCameraController::FocusHeight(bool smooth)
m_PosY.Add(diff);
}
InReaction CCameraController::HandleEvent(const SDL_Event_* ev)
InReaction CCameraController::HandleEvent(const SDL_Event& ev)
{
switch (ev->ev.type)
switch (ev.type)
{
case SDL_HOTKEYPRESS:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
@ -676,7 +677,7 @@ InReaction CCameraController::HandleEvent(const SDL_Event_* ev)
case SDL_HOTKEYDOWN:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
// Mouse wheel must be treated using events instead of polling,
// because SDL auto-generates a sequence of mousedown/mouseup events

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -30,6 +30,7 @@ class CCamera;
class CConfigDBHook;
class CMatrix3D;
class CVector3D;
union SDL_Event;
class CCameraController : public ICameraController
{
@ -40,7 +41,7 @@ public:
void LoadConfig() override;
InReaction HandleEvent(const SDL_Event_* ev) override;
InReaction HandleEvent(const SDL_Event& ev) override;
CVector3D GetCameraPivot() const override;
CVector3D GetCameraPosition() const override;

View file

@ -53,6 +53,7 @@
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include <SDL_events.h>
#include <memory>
#include <string>
@ -350,7 +351,7 @@ entity_id_t CGameView::GetFollowedEntity()
return m->CameraController->GetFollowedEntity();
}
InReaction game_view_handler(const SDL_Event_* ev)
InReaction game_view_handler(const SDL_Event& ev)
{
// put any events that must be processed even if inactive here
if (!g_app_has_focus || !g_Game || !g_Game->IsGameStarted() || g_Game->GetView()->GetCinema()->IsPlaying())
@ -361,13 +362,13 @@ InReaction game_view_handler(const SDL_Event_* ev)
return pView->HandleEvent(ev);
}
InReaction CGameView::HandleEvent(const SDL_Event_* ev)
InReaction CGameView::HandleEvent(const SDL_Event& ev)
{
switch(ev->ev.type)
switch(ev.type)
{
case SDL_HOTKEYPRESS:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
if (hotkey == "wireframe")
{

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -31,8 +31,8 @@ class CObjectManager;
class CVector3D;
namespace Renderer::Backend { class IDevice; }
namespace Renderer::Backend { class IDeviceCommandContext; }
struct SDL_Event_;
struct SViewPort;
union SDL_Event;
class CGameView : private Scene
{
@ -58,7 +58,7 @@ public:
void Render(Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
void RenderOverlays(Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
InReaction HandleEvent(const SDL_Event_* ev);
InReaction HandleEvent(const SDL_Event& ev);
CVector3D GetCameraPivot() const;
CVector3D GetCameraPosition() const;
@ -98,6 +98,6 @@ private:
CGameViewImpl* m;
};
extern InReaction game_view_handler(const SDL_Event_* ev);
extern InReaction game_view_handler(const SDL_Event& ev);
#endif // INCLUDED_GAMEVIEW

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,8 +24,8 @@
class CCamera;
class CVector3D;
struct SDL_Event_;
struct SViewPort;
union SDL_Event;
/**
* @interface ICameraController defines a camera controller interface. The camera object
@ -43,7 +43,7 @@ public:
virtual void LoadConfig() = 0;
virtual InReaction HandleEvent(const SDL_Event_* ev) = 0;
virtual InReaction HandleEvent(const SDL_Event& ev) = 0;
virtual CVector3D GetCameraPivot() const = 0;
virtual CVector3D GetCameraPosition() const = 0;

View file

@ -108,18 +108,18 @@ CGUI::CGUI(ScriptContext& context)
CGUI::~CGUI()
{
if (g_NetClient)
g_NetClient->Unregister(*m_ScriptInterface);
g_NetClient->Unregister(m_ScriptInterface.get());
}
InReaction CGUI::HandleEvent(const SDL_Event_* ev)
InReaction CGUI::HandleEvent(const SDL_Event& ev)
{
InReaction ret = IN_PASS;
if (ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_HOTKEYPRESS || ev->ev.type == SDL_HOTKEYUP)
if (ev.type == SDL_HOTKEYDOWN || ev.type == SDL_HOTKEYPRESS || ev.type == SDL_HOTKEYUP)
{
const char* hotkey = static_cast<const char*>(ev->ev.user.data1);
const char* hotkey = static_cast<const char*>(ev.user.data1);
const CStr& eventName = ev->ev.type == SDL_HOTKEYPRESS ? EventNamePress : ev->ev.type == SDL_HOTKEYDOWN ? EventNameKeyDown : EventNameRelease;
const CStr& eventName = ev.type == SDL_HOTKEYPRESS ? EventNamePress : ev.type == SDL_HOTKEYDOWN ? EventNameKeyDown : EventNameRelease;
if (m_GlobalHotkeys.find(hotkey) != m_GlobalHotkeys.end() && m_GlobalHotkeys[hotkey].find(eventName) != m_GlobalHotkeys[hotkey].end())
{
@ -138,35 +138,35 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
if (!obj->IsEnabled())
continue;
if (ev->ev.type == SDL_HOTKEYPRESS)
if (ev.type == SDL_HOTKEYPRESS)
ret = obj->SendEvent(GUIM_PRESSED, EventNamePress);
else if (ev->ev.type == SDL_HOTKEYDOWN)
else if (ev.type == SDL_HOTKEYDOWN)
ret = obj->SendEvent(GUIM_KEYDOWN, EventNameKeyDown);
else
ret = obj->SendEvent(GUIM_RELEASED, EventNameRelease);
}
}
else if (ev->ev.type == SDL_MOUSEMOTION)
else if (ev.type == SDL_MOUSEMOTION)
{
// Yes the mouse position is stored as float to avoid
// constant conversions when operating in a
// float-based environment.
m_MousePos = CVector2D((float)ev->ev.motion.x / g_VideoMode.GetScale(), (float)ev->ev.motion.y / g_VideoMode.GetScale());
m_MousePos = CVector2D(static_cast<float>(ev.motion.x) / g_VideoMode.GetScale(), static_cast<float>(ev.motion.y) / g_VideoMode.GetScale());
SGUIMessage msg(GUIM_MOUSE_MOTION);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::HandleMessage, msg);
}
// Update m_MouseButtons. (BUTTONUP is handled later.)
else if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
else if (ev.type == SDL_MOUSEBUTTONDOWN)
{
switch (ev->ev.button.button)
switch (ev.button.button)
{
case SDL_BUTTON_LEFT:
case SDL_BUTTON_RIGHT:
case SDL_BUTTON_MIDDLE:
m_MouseButtons |= Bit<unsigned int>(ev->ev.button.button);
m_MouseButtons |= Bit<unsigned int>(ev.button.button);
break;
default:
break;
@ -175,9 +175,9 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
// Update m_MousePos (for delayed mouse button events)
CVector2D oldMousePos = m_MousePos;
if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP)
{
m_MousePos = CVector2D((float)ev->ev.button.x / g_VideoMode.GetScale(), (float)ev->ev.button.y / g_VideoMode.GetScale());
m_MousePos = CVector2D(static_cast<float>(ev.button.x) / g_VideoMode.GetScale(), static_cast<float>(ev.button.y) / g_VideoMode.GetScale());
}
// Allow the focused object to pre-empt regular GUI events.
@ -195,9 +195,9 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
// update their own data and send messages accordingly
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
if (ev.type == SDL_MOUSEBUTTONDOWN)
{
switch (ev->ev.button.button)
switch (ev.button.button)
{
case SDL_BUTTON_LEFT:
// Focus the clicked object (or focus none if nothing clicked on)
@ -216,21 +216,21 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
break;
}
}
else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest)
else if (ev.type == SDL_MOUSEWHEEL && pNearest)
{
if (ev->ev.wheel.y < 0)
if (ev.wheel.y < 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_DOWN, EventNameMouseWheelDown);
else if (ev->ev.wheel.y > 0)
else if (ev.wheel.y > 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_UP, EventNameMouseWheelUp);
if (ev->ev.wheel.x < 0)
if (ev.wheel.x < 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_LEFT, EventNameMouseWheelLeft);
else if (ev->ev.wheel.x > 0)
else if (ev.wheel.x > 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_RIGHT, EventNameMouseWheelRight);
}
else if (ev->ev.type == SDL_MOUSEBUTTONUP)
else if (ev.type == SDL_MOUSEBUTTONUP)
{
switch (ev->ev.button.button)
switch (ev.button.button)
{
case SDL_BUTTON_LEFT:
if (pNearest)
@ -267,14 +267,14 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
// BUTTONUP's effect on m_MouseButtons is handled after
// everything else, so that e.g. 'press' handlers (activated
// on button up) see which mouse button had been pressed.
if (ev->ev.type == SDL_MOUSEBUTTONUP)
if (ev.type == SDL_MOUSEBUTTONUP)
{
switch (ev->ev.button.button)
switch (ev.button.button)
{
case SDL_BUTTON_LEFT:
case SDL_BUTTON_RIGHT:
case SDL_BUTTON_MIDDLE:
m_MouseButtons &= ~Bit<unsigned int>(ev->ev.button.button);
m_MouseButtons &= ~Bit<unsigned int>(ev.button.button);
break;
default:
break;
@ -282,15 +282,15 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
}
// Restore m_MousePos (for delayed mouse button events)
if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP)
m_MousePos = oldMousePos;
// Let GUI items handle keys after everything else, e.g. for input boxes.
if (ret == IN_PASS && GetFocusedObject())
{
if (ev->ev.type == SDL_KEYUP || ev->ev.type == SDL_KEYDOWN ||
ev->ev.type == SDL_HOTKEYUP || ev->ev.type == SDL_HOTKEYDOWN ||
ev->ev.type == SDL_TEXTINPUT || ev->ev.type == SDL_TEXTEDITING)
if (ev.type == SDL_KEYUP || ev.type == SDL_KEYDOWN ||
ev.type == SDL_HOTKEYUP || ev.type == SDL_HOTKEYDOWN ||
ev.type == SDL_TEXTINPUT || ev.type == SDL_TEXTEDITING)
ret = GetFocusedObject()->ManuallyHandleKeys(ev);
// else will return IN_PASS because we never used the button.
}

View file

@ -67,8 +67,8 @@ class XMBElement;
namespace JS { class HandleValueArray; }
namespace JS { class Value; }
namespace js { class BaseProxyHandler; }
struct SDL_Event_;
struct SGUIImageEffects;
union SDL_Event;
extern const double SELECT_DBLCLICK_RATE;
@ -132,11 +132,11 @@ public:
void DrawSprite(const CGUISpriteInstance& Sprite, CCanvas2D& canvas, const CRect& Rect, const CRect& Clipping = CRect());
/**
* The replacement of Process(), handles an SDL_Event_
* The replacement of Process(), handles an SDL_Event
*
* @param ev SDL Event, like mouse/keyboard input
*/
InReaction HandleEvent(const SDL_Event_* ev);
InReaction HandleEvent(const SDL_Event& ev);
/**
* Load a GUI XML file into the GUI.

View file

@ -44,6 +44,7 @@
#include "scriptinterface/StructuredClone.h"
#include "simulation2/system/Component.h"
#include <SDL_events.h>
#include <algorithm>
#include <iterator>
#include <js/Equality.h>
@ -81,7 +82,7 @@ CGUIManager* g_GUI = nullptr;
// called from main loop when (input) events are received.
// event is passed to other handlers if false is returned.
// trampoline: we don't want to make the HandleEvent implementation static
InReaction gui_handler(const SDL_Event_* ev)
InReaction gui_handler(const SDL_Event& ev)
{
if (!g_GUI)
return IN_PASS;
@ -370,7 +371,7 @@ Status CGUIManager::ReloadAllPages()
return INFO::OK;
}
InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
InReaction CGUIManager::HandleEvent(const SDL_Event& ev)
{
// We want scripts to have access to the raw input events, so they can do complex
// processing when necessary (e.g. for unit selection and camera movement).
@ -386,7 +387,7 @@ InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
ScriptRequest rq(*top()->GetScriptInterface());
JS::RootedValue global(rq.cx, rq.globalValue());
if (ScriptFunction::Call(rq, global, "handleInputBeforeGui", handled, *ev, top()->FindObjectUnderMouse()))
if (ScriptFunction::Call(rq, global, "handleInputBeforeGui", handled, ev, top()->FindObjectUnderMouse()))
if (handled)
return IN_HANDLED;
}
@ -404,7 +405,7 @@ InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
JS::RootedValue global(rq.cx, rq.globalValue());
PROFILE("handleInputAfterGui");
if (ScriptFunction::Call(rq, global, "handleInputAfterGui", handled, *ev))
if (ScriptFunction::Call(rq, global, "handleInputAfterGui", handled, ev))
if (handled)
return IN_HANDLED;
}

View file

@ -43,7 +43,7 @@ class ScriptContext;
namespace JS { class HandleValueArray; }
namespace JS { class Value; }
namespace PS { template <typename T, size_t N> class StaticVector; }
struct SDL_Event_;
union SDL_Event;
/**
* External interface to the GUI system.
@ -99,7 +99,7 @@ public:
/**
* Pass input events to the currently active GUI page.
*/
InReaction HandleEvent(const SDL_Event_* ev);
InReaction HandleEvent(const SDL_Event& ev);
/**
* See CGUI::SendEventToAll; applies to the currently active page.
@ -242,6 +242,6 @@ private:
extern CGUIManager* g_GUI;
extern InReaction gui_handler(const SDL_Event_* ev);
extern InReaction gui_handler(const SDL_Event& ev);
#endif // INCLUDED_GUIMANAGER

View file

@ -49,7 +49,7 @@ class JSTracer;
class XMBData;
class XMBElement;
namespace JS { class HandleValueArray; }
struct SDL_Event_;
union SDL_Event;
#define GUI_OBJECT(obj) \
public: \
@ -285,17 +285,17 @@ protected:
virtual void Draw(CCanvas2D& canvas) = 0;
/**
* Some objects need to be able to pre-emptively process SDL_Event_.
* Some objects need to be able to pre-emptively process SDL_Event.
*
* Only the object with focus will have this function called.
*
* Returns either IN_PASS or IN_HANDLED. If IN_HANDLED, then
* the event won't be passed on and processed by other handlers.
*/
virtual InReaction PreemptEvent(const SDL_Event_*) { return IN_PASS; }
virtual InReaction PreemptEvent(const SDL_Event&) { return IN_PASS; }
/**
* Some objects need to handle the text-related SDL_Event_ manually.
* Some objects need to handle the text-related SDL_Event manually.
* For instance the input box.
*
* Only the object with focus will have this function called.
@ -304,7 +304,7 @@ protected:
* the key won't be passed on and processed by other handlers.
* This is used for keys that the GUI uses.
*/
virtual InReaction ManuallyHandleKeys(const SDL_Event_*) { return IN_PASS; }
virtual InReaction ManuallyHandleKeys(const SDL_Event&) { return IN_PASS; }
/**
* Applies the given style to the object.

View file

@ -272,14 +272,14 @@ void CDropDown::HandleMessage(SGUIMessage& Message)
SetupText();
}
InReaction CDropDown::ManuallyHandleKeys(const SDL_Event_* ev)
InReaction CDropDown::ManuallyHandleKeys(const SDL_Event& ev)
{
InReaction result = IN_PASS;
bool update_highlight = false;
if (ev->ev.type == SDL_KEYDOWN)
if (ev.type == SDL_KEYDOWN)
{
int szChar = ev->ev.key.keysym.sym;
int szChar = ev.key.keysym.sym;
switch (szChar)
{

View file

@ -42,6 +42,7 @@ GUI Object - Drop Down (list)
#include <string>
class CGUI;
union SDL_Event;
/**
* Drop Down
@ -67,7 +68,7 @@ public:
/**
* Handle events manually to catch keyboard inputting.
*/
virtual InReaction ManuallyHandleKeys(const SDL_Event_* ev);
virtual InReaction ManuallyHandleKeys(const SDL_Event& ev);
/**
* Draws the Button

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -105,9 +105,9 @@ void CHotkeyPicker::HandleMessage(SGUIMessage& Message)
}
}
InReaction CHotkeyPicker::PreemptEvent(const SDL_Event_* ev)
InReaction CHotkeyPicker::PreemptEvent(const SDL_Event& ev)
{
switch (ev->ev.type)
switch (ev.type)
{
// Handle the same mouse events that hotkeys handle
case SDL_MOUSEBUTTONDOWN:
@ -116,27 +116,27 @@ InReaction CHotkeyPicker::PreemptEvent(const SDL_Event_* ev)
{
SDL_Scancode scancode;
if (ev->ev.type != SDL_MOUSEWHEEL)
if (ev.type != SDL_MOUSEWHEEL)
{
// Wait a little bit -> this gets triggered when clicking on a button,
// but after the button click is processed, thus immediately triggering...
if (timer_Time()-m_LastKeyChange < 0.2)
return IN_HANDLED;
// This is from hotkeyHandler - not sure what it does in all honesty.
if(ev->ev.button.button >= SDL_BUTTON_X1)
scancode = static_cast<SDL_Scancode>(MOUSE_BASE + (int)ev->ev.button.button + 2);
if(ev.button.button >= SDL_BUTTON_X1)
scancode = static_cast<SDL_Scancode>(MOUSE_BASE + static_cast<int>(ev.button.button) + 2);
else
scancode = static_cast<SDL_Scancode>(MOUSE_BASE + (int)ev->ev.button.button);
scancode = static_cast<SDL_Scancode>(MOUSE_BASE + static_cast<int>(ev.button.button));
}
else
{
if (ev->ev.wheel.y > 0)
if (ev.wheel.y > 0)
scancode = static_cast<SDL_Scancode>(MOUSE_WHEELUP);
else if (ev->ev.wheel.y < 0)
else if (ev.wheel.y < 0)
scancode = static_cast<SDL_Scancode>(MOUSE_WHEELDOWN);
else if (ev->ev.wheel.x > 0)
else if (ev.wheel.x > 0)
scancode = static_cast<SDL_Scancode>(MOUSE_X2);
else if (ev->ev.wheel.x < 0)
else if (ev.wheel.x < 0)
scancode = static_cast<SDL_Scancode>(MOUSE_X1);
else
return IN_HANDLED;
@ -153,7 +153,7 @@ InReaction CHotkeyPicker::PreemptEvent(const SDL_Event_* ev)
case SDL_KEYDOWN:
case SDL_KEYUP:
{
SDL_Scancode scancode = ev->ev.key.keysym.scancode;
SDL_Scancode scancode = ev.key.keysym.scancode;
// Don't handle caps-lock, it doesn't really work in-game and it's a weird hotkey.
if (scancode == SDL_SCANCODE_CAPSLOCK)
@ -168,7 +168,7 @@ InReaction CHotkeyPicker::PreemptEvent(const SDL_Event_* ev)
else if (scancode == SDL_SCANCODE_LGUI || scancode == SDL_SCANCODE_RGUI)
scancode = static_cast<SDL_Scancode>(UNIFIED_SUPER);
if (ev->ev.type == SDL_KEYDOWN)
if (ev.type == SDL_KEYDOWN)
{
std::vector<Key>::const_iterator it = \
std::find_if(m_KeysPressed.begin(), m_KeysPressed.end(), [&scancode](Key& k) { return k.code == scancode; });

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,6 +27,7 @@
#include <vector>
class CGUI;
union SDL_Event;
/**
* When in focus, returns all currently pressed keys.
@ -52,7 +53,7 @@ public:
virtual void HandleMessage(SGUIMessage& Message);
// Pre-empt events: this is our sole purpose.
virtual InReaction PreemptEvent(const SDL_Event_* ev);
virtual InReaction PreemptEvent(const SDL_Event& ev);
struct Key
{

View file

@ -113,7 +113,7 @@ void CInput::ClearComposedText()
m_iComposedPos = 0;
}
InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
InReaction CInput::ManuallyHandleKeys(const SDL_Event& ev)
{
ENSURE(m_iBufferPos != -1);
@ -121,7 +121,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
// (Messages don't currently need to be sent)
CStrW& caption = m_Caption.GetMutable();
switch (ev->ev.type)
switch (ev.type)
{
case SDL_HOTKEYDOWN:
{
@ -138,7 +138,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
return IN_PASS;
// Text has been committed, either single key presses or through an IME
std::wstring text = wstring_from_utf8(ev->ev.text.text);
std::wstring text = wstring_from_utf8(ev.text.text);
// Check max length
if (m_MaxLength != 0 && caption.length() + text.length() > static_cast<size_t>(m_MaxLength))
@ -178,7 +178,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
// Text is being composed with an IME
// TODO: indicate this by e.g. underlining the uncommitted text
const char* rawText = ev->ev.edit.text;
const char* rawText = ev.edit.text;
int rawLength = strlen(rawText);
std::wstring wtext = wstring_from_utf8(rawText);
@ -196,7 +196,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
ClearComposedText();
}
m_ComposingText = ev->ev.edit.start != 0 || rawLength != 0;
m_ComposingText = ev.edit.start != 0 || rawLength != 0;
if (m_ComposingText)
{
caption.insert(m_iInsertPos, wtext);
@ -204,7 +204,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
// The text buffer is limited to SDL_TEXTEDITINGEVENT_TEXT_SIZE bytes, yet start
// increases without limit, so don't let it advance beyond the composed text length
m_iComposedLength = wtext.length();
m_iComposedPos = ev->ev.edit.start < m_iComposedLength ? ev->ev.edit.start : m_iComposedLength;
m_iComposedPos = ev.edit.start < m_iComposedLength ? ev.edit.start : m_iComposedLength;
m_iBufferPos = m_iInsertPos + m_iComposedPos;
// TODO: composed text selection - what does ev.edit.length do?
@ -225,7 +225,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
// Since the GUI framework doesn't handle to set settings
// in Unicode (CStrW), we'll simply retrieve the actual
// pointer and edit that.
SDL_Keycode keyCode = ev->ev.key.keysym.sym;
SDL_Keycode keyCode = ev.key.keysym.sym;
// We have a probably printable key - we should return HANDLED so it can't trigger hotkeys.
// However, if Ctrl/Meta modifiers are active, just pass it through instead,
@ -242,7 +242,7 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
if (m_ComposingText)
return IN_HANDLED;
if (ev->ev.type == SDL_KEYDOWN)
if (ev.type == SDL_KEYDOWN)
{
ManuallyImmutableHandleKeyDownEvent(keyCode);
ManuallyMutableHandleKeyDownEvent(keyCode);
@ -619,11 +619,11 @@ void CInput::SetupGeneratedPlaceholderText()
m_GeneratedPlaceholderTextValid = true;
}
InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event& ev)
{
bool shiftKeyPressed = g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT];
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
// Get direct access to silently mutate m_Caption.
// (Messages don't currently need to be sent)
@ -1161,12 +1161,12 @@ void CInput::HandleMessage(SGUIMessage& Message)
if (m_ComposingText)
{
// Simulate a final text editing event to clear the composition
SDL_Event_ evt;
evt.ev.type = SDL_TEXTEDITING;
evt.ev.edit.length = 0;
evt.ev.edit.start = 0;
evt.ev.edit.text[0] = 0;
ManuallyHandleKeys(&evt);
SDL_Event ev{};
ev.type = SDL_TEXTEDITING;
ev.edit.length = 0;
ev.edit.start = 0;
ev.edit.text[0] = '\0';
ManuallyHandleKeys(ev);
}
SDL_StopTextInput();

View file

@ -36,7 +36,7 @@
class CCanvas2D;
class CGUI;
struct SDL_Event_;
union SDL_Event;
/**
* Text field where you can input and edit the text.
@ -80,7 +80,7 @@ protected:
/**
* Handle events manually to catch keyboard inputting.
*/
virtual InReaction ManuallyHandleKeys(const SDL_Event_* ev);
virtual InReaction ManuallyHandleKeys(const SDL_Event& ev);
/**
* Handle events manually to catch keys which change the text.
@ -95,7 +95,7 @@ protected:
/**
* Handle hotkey events (called by ManuallyHandleKeys)
*/
virtual InReaction ManuallyHandleHotkeyEvent(const SDL_Event_* ev);
virtual InReaction ManuallyHandleHotkeyEvent(const SDL_Event& ev);
/**
* @see IGUIObject#HandleSizeChanged()

View file

@ -260,13 +260,13 @@ void CList::HandleMessage(SGUIMessage& Message)
IGUITextOwner::HandleMessage(Message);
}
InReaction CList::ManuallyHandleKeys(const SDL_Event_* ev)
InReaction CList::ManuallyHandleKeys(const SDL_Event& ev)
{
InReaction result = IN_PASS;
if (ev->ev.type == SDL_KEYDOWN)
if (ev.type == SDL_KEYDOWN)
{
int szChar = ev->ev.key.keysym.sym;
int szChar = ev.key.keysym.sym;
switch (szChar)
{

View file

@ -35,6 +35,7 @@
class CCanvas2D;
class CGUI;
class CGUIString;
union SDL_Event;
/**
* Create a list of elements, where one can be selected
@ -88,7 +89,7 @@ protected:
/**
* Handle events manually to catch keyboard inputting.
*/
virtual InReaction ManuallyHandleKeys(const SDL_Event_* ev);
virtual InReaction ManuallyHandleKeys(const SDL_Event& ev);
/**
* Draws the List box

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -55,11 +55,11 @@ struct CColor;
// ignore JS_SetProperty return value, because errors should be impossible
// and we can't do anything useful in the case of errors anyway
template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::MutableHandleValue ret, SDL_Event_ const& val)
template<> void Script::ToJSVal<SDL_Event>(const ScriptRequest& rq, JS::MutableHandleValue ret, SDL_Event const& ev)
{
const char* typeName;
switch (val.ev.type)
switch (ev.type)
{
case SDL_WINDOWEVENT: typeName = "windowevent"; break;
case SDL_KEYDOWN: typeName = "keydown"; break;
@ -85,13 +85,13 @@ template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::Mutable
SET(obj, "type", typeName);
switch (val.ev.type)
switch (ev.type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
{
// SET(obj, "which", (int)val.ev.key.which); // (not in wsdl.h)
// SET(obj, "state", (int)val.ev.key.state); // (not in wsdl.h)
// SET(obj, "which", static_cast<int>(ev.key.which)); // (not in wsdl.h)
// SET(obj, "state", static_cast<int>(ev.key.state)); // (not in wsdl.h)
JS::RootedObject keysym(rq.cx, JS_NewPlainObject(rq.cx));
if (!keysym)
@ -102,9 +102,9 @@ template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::Mutable
JS::RootedValue keysymVal(rq.cx, JS::ObjectValue(*keysym));
JS_SetProperty(rq.cx, obj, "keysym", keysymVal);
// SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h)
SET(keysym, "sym", (int)val.ev.key.keysym.sym);
// SET(keysym, "mod", (int)val.ev.key.keysym.mod); // (not in wsdl.h)
// SET(keysym, "scancode", static_cast<int>(ev.key.keysym.scancode)); // (not in wsdl.h)
SET(keysym, "sym", static_cast<int>(ev.key.keysym.sym));
// SET(keysym, "mod", static_cast<int>(ev.key.keysym.mod)); // (not in wsdl.h)
{
SET(keysym, "unicode", JS::UndefinedHandleValue);
}
@ -115,23 +115,23 @@ template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::Mutable
}
case SDL_MOUSEMOTION:
{
// SET(obj, "which", (int)val.ev.motion.which); // (not in wsdl.h)
// SET(obj, "state", (int)val.ev.motion.state); // (not in wsdl.h)
SET(obj, "x", (int)val.ev.motion.x);
SET(obj, "y", (int)val.ev.motion.y);
// SET(obj, "xrel", (int)val.ev.motion.xrel); // (not in wsdl.h)
// SET(obj, "yrel", (int)val.ev.motion.yrel); // (not in wsdl.h)
// SET(obj, "which", static_cast<int>(ev.motion.which)); // (not in wsdl.h)
// SET(obj, "state", static_cast<int>(ev.motion.state)); // (not in wsdl.h)
SET(obj, "x", static_cast<int>(ev.motion.x));
SET(obj, "y", static_cast<int>(ev.motion.y));
// SET(obj, "xrel", static_cast<int>(ev.motion.xrel)); // (not in wsdl.h)
// SET(obj, "yrel", static_cast<int>(ev.motion.yrel)); // (not in wsdl.h)
break;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
// SET(obj, "which", (int)val.ev.button.which); // (not in wsdl.h)
SET(obj, "button", (int)val.ev.button.button);
SET(obj, "state", (int)val.ev.button.state);
SET(obj, "x", (int)val.ev.button.x);
SET(obj, "y", (int)val.ev.button.y);
SET(obj, "clicks", (int)val.ev.button.clicks);
// SET(obj, "which", static_cast<int>(ev.button.which)); // (not in wsdl.h)
SET(obj, "button", static_cast<int>(ev.button.button));
SET(obj, "state", static_cast<int>(ev.button.state));
SET(obj, "x", static_cast<int>(ev.button.x));
SET(obj, "y", static_cast<int>(ev.button.y));
SET(obj, "clicks", static_cast<int>(ev.button.clicks));
break;
}
case SDL_HOTKEYPRESS:
@ -140,7 +140,7 @@ template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::Mutable
case SDL_HOTKEYPRESS_SILENT:
case SDL_HOTKEYUP_SILENT:
{
SET(obj, "hotkey", static_cast<const char*>(val.ev.user.data1));
SET(obj, "hotkey", static_cast<const char*>(ev.user.data1));
break;
}
}

View file

@ -160,17 +160,17 @@ public:
g_GUI->OpenChildPage(L"hotkey/page_hotkey.xml", data);
// Press 'a'.
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_KEYDOWN;
hotkeyNotification.ev.key.keysym.scancode = SDL_SCANCODE_A;
hotkeyNotification.ev.key.repeat = 0;
SDL_Event hotkeyNotification;
hotkeyNotification.type = SDL_KEYDOWN;
hotkeyNotification.key.keysym.scancode = SDL_SCANCODE_A;
hotkeyNotification.key.repeat = 0;
// Init input and poll the event.
InitInput();
in_push_priority_event(&hotkeyNotification);
SDL_Event_ ev;
while (in_poll_event(&ev))
in_dispatch_event(&ev);
in_push_priority_event(hotkeyNotification);
SDL_Event ev;
while (in_poll_event(ev))
in_dispatch_event(ev);
const ScriptInterface& pageScriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface());
ScriptRequest prq(pageScriptInterface);
@ -190,10 +190,10 @@ public:
TS_ASSERT_EQUALS(hotkey_pressed_value, true);
// We are listening to KeyDown events, so repeat shouldn't matter.
hotkeyNotification.ev.key.repeat = 1;
in_push_priority_event(&hotkeyNotification);
while (in_poll_event(&ev))
in_dispatch_event(&ev);
hotkeyNotification.key.repeat = 1;
in_push_priority_event(hotkeyNotification);
while (in_poll_event(ev))
in_dispatch_event(ev);
hotkey_pressed_value = false;
Script::GetProperty(prq, global, "state_before", &js_hotkey_pressed_value);
@ -205,10 +205,10 @@ public:
Script::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value);
TS_ASSERT_EQUALS(hotkey_pressed_value, true);
hotkeyNotification.ev.type = SDL_KEYUP;
in_push_priority_event(&hotkeyNotification);
while (in_poll_event(&ev))
in_dispatch_event(&ev);
hotkeyNotification.type = SDL_KEYUP;
in_push_priority_event(hotkeyNotification);
while (in_poll_event(ev))
in_dispatch_event(ev);
hotkey_pressed_value = true;
Script::GetProperty(prq, global, "state_before", &js_hotkey_pressed_value);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -28,7 +28,6 @@
#define INCLUDED_SDL
#include "lib/config2.h"
#include "lib/external_libraries/libsdl_fwd.h"
# include <SDL.h>
# include <SDL_thread.h>
@ -43,12 +42,6 @@
// another header that toggles between wsdl and SDL_endian.h.
# include <SDL_endian.h>
// complete definition of our forward-declared SDL_Event (see sdl_fwd.h)
struct SDL_Event_
{
SDL_Event ev;
};
// Returns a windowing subsystem used for the window.
const char* GetSDLSubsystem(SDL_Window* window);

View file

@ -1,41 +0,0 @@
/* Copyright (C) 2010 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.
*/
/*
* forward declaration of SDL_Event
*/
#ifndef INCLUDED_SDL_FWD
#define INCLUDED_SDL_FWD
// 2006-08-26 SDL is dragged into 6 of our 7 static library components.
// it must be specified in each of their "extern_libs" so that the
// include path is set and <SDL.h> can be found.
//
// obviously this is bad, so we work around the root cause. mostly only
// SDL_Event is needed. unfortunately it cannot be forward-declared,
// because it is a union (regrettable design mistake).
// we fix this by wrapping it in a struct, which can safely be
// forward-declared and used for SDL_Event_* parameters.
struct SDL_Event_;
#endif // #ifndef INCLUDED_SDL_FWD

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -40,7 +40,7 @@ const size_t MAX_HANDLERS = 10;
static InHandler handler_stack[MAX_HANDLERS];
static size_t handler_stack_top = 0;
static std::list<SDL_Event_> priority_events;
static std::list<SDL_Event> priority_events;
void in_add_handler(InHandler handler)
{
@ -58,11 +58,11 @@ void in_reset_handlers()
}
// send ev to each handler until one returns IN_HANDLED
void in_dispatch_event(const SDL_Event_* ev)
void in_dispatch_event(const SDL_Event& ev)
{
for(int i = (int)handler_stack_top-1; i >= 0; i--)
{
ENSURE(handler_stack[i] && ev);
ENSURE(handler_stack[i]);
InReaction ret = handler_stack[i](ev);
// .. done, return
if(ret == IN_HANDLED)
@ -76,22 +76,22 @@ void in_dispatch_event(const SDL_Event_* ev)
}
}
void in_push_priority_event(const SDL_Event_* event)
void in_push_priority_event(const SDL_Event& event)
{
priority_events.push_back(*event);
priority_events.push_back(event);
}
int in_poll_priority_event(SDL_Event_* event)
int in_poll_priority_event(SDL_Event& event)
{
if (priority_events.empty())
return 0;
*event = priority_events.front();
event = priority_events.front();
priority_events.pop_front();
return 1;
}
int in_poll_event(SDL_Event_* event)
int in_poll_event(SDL_Event& event)
{
return in_poll_priority_event(event) ? 1 : SDL_PollEvent(&event->ev);
return in_poll_priority_event(event) ? 1 : SDL_PollEvent(&event);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@
#ifndef INCLUDED_INPUT
#define INCLUDED_INPUT
struct SDL_Event_;
union SDL_Event;
// input handler return values.
enum InReaction
@ -42,7 +42,7 @@ enum InReaction
IN_HANDLED = 2
};
typedef InReaction (*InHandler)(const SDL_Event_*);
typedef InReaction (*InHandler)(const SDL_Event&);
// register an input handler, which will receive all subsequent events first.
// events are passed to other handlers if handler returns IN_PASS.
@ -52,19 +52,19 @@ extern void in_add_handler(InHandler handler);
extern void in_reset_handlers();
// send event to each handler (newest first) until one returns true
extern void in_dispatch_event(const SDL_Event_* event);
extern void in_dispatch_event(const SDL_Event& event);
// push an event onto the back of a high-priority queue - the new event will
// be returned by in_poll_event before any standard SDL events
extern void in_push_priority_event(const SDL_Event_* event);
extern void in_push_priority_event(const SDL_Event& event);
// reads events that were pushed by in_push_priority_event
// returns 1 if an event was read, 0 otherwise.
extern int in_poll_priority_event(SDL_Event_* event);
extern int in_poll_priority_event(SDL_Event& event);
// reads events that were pushed by in_push_priority_event, or, if there are
// no high-priority events) reads from the SDL event queue with SDL_PollEvent.
// returns 1 if an event was read, 0 otherwise.
extern int in_poll_event(SDL_Event_* event);
extern int in_poll_event(SDL_Event& event);
#endif // #ifndef INCLUDED_INPUT

View file

@ -195,19 +195,19 @@ void RestartEngine()
}
// main app message handler
static InReaction MainInputHandler(const SDL_Event_* ev)
static InReaction MainInputHandler(const SDL_Event& ev)
{
switch(ev->ev.type)
switch(ev.type)
{
case SDL_WINDOWEVENT:
switch(ev->ev.window.event)
switch(ev.window.event)
{
case SDL_WINDOWEVENT_RESIZED:
g_ResizedW = ev->ev.window.data1;
g_ResizedH = ev->ev.window.data2;
g_ResizedW = ev.window.data1;
g_ResizedH = ev.window.data2;
break;
case SDL_WINDOWEVENT_MOVED:
g_VideoMode.UpdatePosition(ev->ev.window.data1, ev->ev.window.data2);
g_VideoMode.UpdatePosition(ev.window.data1, ev.window.data2);
}
break;
@ -217,7 +217,7 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
case SDL_DROPFILE:
{
char* dropped_filedir = ev->ev.drop.file;
char* dropped_filedir = ev.drop.file;
const Paths paths(g_CmdLineArgs);
CModInstaller installer(paths.UserData() / "mods", paths.Cache());
installer.Install(std::string(dropped_filedir), g_ScriptContext, true);
@ -235,7 +235,7 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
}
case SDL_HOTKEYPRESS:
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
if (hotkey == "exit")
{
QuitEngine(EXIT_SUCCESS);
@ -281,8 +281,8 @@ static void PumpEvents()
PROFILE3("dispatch events");
SDL_Event_ ev;
while (in_poll_event(&ev))
SDL_Event ev{};
while (in_poll_event(ev))
{
PROFILE2("event");
if (g_GUI)
@ -292,7 +292,7 @@ static void PumpEvents()
std::string data = Script::StringifyJSON(rq, &tmpVal);
PROFILE2_ATTR("%s", data.c_str());
}
in_dispatch_event(&ev);
in_dispatch_event(ev);
}
g_TouchInput.Frame();

View file

@ -159,6 +159,8 @@ CNetClient::CNetClient(CGame* game, const CStrW& username, const CStr& hostJID,
CNetClient::~CNetClient()
{
Unregister(nullptr);
// Try to flush messages before dying (probably fails).
if (m_ClientTurnManager)
m_ClientTurnManager->OnDestroyConnection();
@ -364,6 +366,7 @@ void CNetClient::CheckServerConnection()
JSObject* CNetClient::GetNextGUIMessage(const ScriptInterface& guiInterface)
{
Unregister(nullptr);
const ScriptRequest rq{guiInterface};
m_GuiMessagePoll.emplace(GuiPollData{guiInterface, {rq.cx, JS::NewPromiseObject(rq.cx, nullptr)}});
@ -371,10 +374,17 @@ JSObject* CNetClient::GetNextGUIMessage(const ScriptInterface& guiInterface)
return m_GuiMessagePoll.value().promise;
}
void CNetClient::Unregister(const ScriptInterface& guiInterface)
void CNetClient::Unregister(const ScriptInterface* guiInterface)
{
if (m_GuiMessagePoll.has_value() && &m_GuiMessagePoll.value().interface == &guiInterface)
m_GuiMessagePoll.reset();
if (!m_GuiMessagePoll.has_value() ||
(guiInterface && &m_GuiMessagePoll.value().interface != guiInterface))
{
return;
}
auto& [interface, promise] = m_GuiMessagePoll.value();
const ScriptRequest oldRq{interface};
JS::ResolvePromise(oldRq.cx, promise, JS::UndefinedHandleValue);
m_GuiMessagePoll.reset();
}
void CNetClient::FetchMessage()

View file

@ -147,7 +147,7 @@ public:
* Has to be called bevore the @c ScriptInterface gets destroied so that
* no future messages are sent to it.
*/
void Unregister(const ScriptInterface& guiInterface);
void Unregister(const ScriptInterface* guiInterface);
/**
* Add a message to the queue, to be read by GuiPoll.

View file

@ -496,14 +496,13 @@ bool CNetServerWorker::RunStep()
// Remove the session first, so we won't send player-update messages to it
// when updating the FSM
std::erase_if(m_Sessions, [&](const auto& s)
{
return s.get() == session;
});
const auto iter = std::ranges::find(m_Sessions, session,
&std::unique_ptr<CNetServerSession>::get);
const std::unique_ptr<CNetServerSession> _ = std::move(*iter);
m_Sessions.erase(iter);
session->Update((uint)NMT_CONNECTION_LOST, NULL);
delete session;
event.peer->data = NULL;
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -692,14 +692,14 @@ static bool isUnprintableChar(SDL_Keysym key)
}
}
InReaction conInputHandler(const SDL_Event_* ev)
InReaction conInputHandler(const SDL_Event& ev)
{
if (!g_Console)
return IN_PASS;
if (static_cast<int>(ev->ev.type) == SDL_HOTKEYPRESS)
if (static_cast<int>(ev.type) == SDL_HOTKEYPRESS)
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
if (hotkey == "console.toggle")
{
@ -734,23 +734,23 @@ InReaction conInputHandler(const SDL_Event_* ev)
// In SDL2, we no longer get Unicode wchars via SDL_Keysym
// we use text input events instead and they provide UTF-8 chars
if (ev->ev.type == SDL_TEXTINPUT)
if (ev.type == SDL_TEXTINPUT)
{
// TODO: this could be more efficient with an interface to insert UTF-8 strings directly
std::wstring wstr = wstring_from_utf8(ev->ev.text.text);
std::wstring wstr = wstring_from_utf8(ev.text.text);
for (size_t i = 0; i < wstr.length(); ++i)
g_Console->InsertChar(0, wstr[i]);
return IN_HANDLED;
}
// TODO: text editing events for IME support
if (ev->ev.type != SDL_KEYDOWN && ev->ev.type != SDL_KEYUP)
if (ev.type != SDL_KEYDOWN && ev.type != SDL_KEYUP)
return IN_PASS;
int sym = ev->ev.key.keysym.sym;
int sym = ev.key.keysym.sym;
// Stop unprintable characters (ctrl+, alt+ and escape).
if (ev->ev.type == SDL_KEYDOWN && isUnprintableChar(ev->ev.key.keysym) &&
if (ev.type == SDL_KEYDOWN && isUnprintableChar(ev.key.keysym) &&
!HotkeyIsPressed("console.toggle"))
{
g_Console->InsertChar(sym, 0);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -34,7 +34,7 @@
class CCanvas2D;
class CTextRenderer;
struct SDL_Event_;
union SDL_Event;
/**
* In-game console.
@ -137,6 +137,6 @@ private:
extern CConsole* g_Console;
extern InReaction conInputHandler(const SDL_Event_* ev);
extern InReaction conInputHandler(const SDL_Event& ev);
#endif // INCLUDED_CCONSOLE

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -40,14 +40,14 @@ bool g_mouse_buttons[MOUSE_LAST - MOUSE_BASE] = {0};
PIFrequencyFilter g_frequencyFilter;
// updates the state of the above; never swallows messages.
InReaction GlobalsInputHandler(const SDL_Event_* ev)
InReaction GlobalsInputHandler(const SDL_Event& ev)
{
size_t c;
switch(ev->ev.type)
switch(ev.type)
{
case SDL_WINDOWEVENT:
switch(ev->ev.window.event)
switch(ev.window.event)
{
case SDL_WINDOWEVENT_MINIMIZED:
g_app_minimized = true;
@ -72,15 +72,15 @@ InReaction GlobalsInputHandler(const SDL_Event_* ev)
return IN_PASS;
case SDL_MOUSEMOTION:
g_mouse_x = ev->ev.motion.x;
g_mouse_y = ev->ev.motion.y;
g_mouse_x = ev.motion.x;
g_mouse_y = ev.motion.y;
return IN_PASS;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
c = ev->ev.button.button;
c = ev.button.button;
if(c < ARRAY_SIZE(g_mouse_buttons))
g_mouse_buttons[c] = (ev->ev.type == SDL_MOUSEBUTTONDOWN);
g_mouse_buttons[c] = (ev.type == SDL_MOUSEBUTTONDOWN);
else
{
// don't complain: just ignore people with too many mouse buttons
@ -90,7 +90,7 @@ InReaction GlobalsInputHandler(const SDL_Event_* ev)
case SDL_KEYDOWN:
case SDL_KEYUP:
g_scancodes[ev->ev.key.keysym.scancode] = (ev->ev.type == SDL_KEYDOWN);
g_scancodes[ev.key.keysym.scancode] = (ev.type == SDL_KEYDOWN);
return IN_PASS;
default:

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -25,7 +25,7 @@
#include <cstdint>
#include <unordered_map>
struct SDL_Event_;
union SDL_Event;
// thin abstraction layer on top of SDL.
// game code should use it instead of SDL_GetMouseState etc. because
@ -59,7 +59,7 @@ extern std::unordered_map<int32_t, bool> g_scancodes;
*/
extern bool g_mouse_buttons[MOUSE_LAST - MOUSE_BASE];
extern InReaction GlobalsInputHandler(const SDL_Event_* ev);
extern InReaction GlobalsInputHandler(const SDL_Event& ev);
extern PIFrequencyFilter g_frequencyFilter;

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -71,7 +71,7 @@ namespace {
// Stores the 'specificity' of the newly pressed hotkeys.
size_t closestMapMatch = 0;
// This is merely used to ensure consistency in EventWillFireHotkey.
const SDL_Event_* currentEvent;
const SDL_Event* currentEvent;
// List of currently pressed hotkeys. This is used to quickly reset hotkeys.
// This is an unsorted vector because there will generally be very few elements,
@ -174,16 +174,16 @@ bool isPressed(const SKey& key)
return false;
}
InReaction HotkeyStateChange(const SDL_Event_* ev)
InReaction HotkeyStateChange(const SDL_Event& ev)
{
if (ev->ev.type == SDL_HOTKEYPRESS || ev->ev.type == SDL_HOTKEYPRESS_SILENT)
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = true;
else if (ev->ev.type == SDL_HOTKEYUP || ev->ev.type == SDL_HOTKEYUP_SILENT)
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = false;
if (ev.type == SDL_HOTKEYPRESS || ev.type == SDL_HOTKEYPRESS_SILENT)
g_HotkeyStatus[static_cast<const char*>(ev.user.data1)] = true;
else if (ev.type == SDL_HOTKEYUP || ev.type == SDL_HOTKEYUP_SILENT)
g_HotkeyStatus[static_cast<const char*>(ev.user.data1)] = false;
return IN_PASS;
}
InReaction HotkeyInputPrepHandler(const SDL_Event_* ev)
InReaction HotkeyInputPrepHandler(const SDL_Event& ev)
{
int scancode = SDL_SCANCODE_UNKNOWN;
@ -191,40 +191,40 @@ InReaction HotkeyInputPrepHandler(const SDL_Event_* ev)
newPressedHotkeys.clear();
currentEvent = nullptr;
switch(ev->ev.type)
switch(ev.type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
scancode = ev->ev.key.keysym.scancode;
scancode = ev.key.keysym.scancode;
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// Mousewheel events are no longer buttons, but we want to maintain the order
// expected by g_mouse_buttons for compatibility
if (ev->ev.button.button >= SDL_BUTTON_X1)
scancode = MOUSE_BASE + (int)ev->ev.button.button + 2;
if (ev.button.button >= SDL_BUTTON_X1)
scancode = MOUSE_BASE + static_cast<int>(ev.button.button) + 2;
else
scancode = MOUSE_BASE + (int)ev->ev.button.button;
scancode = MOUSE_BASE + static_cast<int>(ev.button.button);
break;
case SDL_MOUSEWHEEL:
if (ev->ev.wheel.y > 0)
if (ev.wheel.y > 0)
{
scancode = MOUSE_WHEELUP;
break;
}
else if (ev->ev.wheel.y < 0)
else if (ev.wheel.y < 0)
{
scancode = MOUSE_WHEELDOWN;
break;
}
else if (ev->ev.wheel.x > 0)
else if (ev.wheel.x > 0)
{
scancode = MOUSE_X2;
break;
}
else if (ev->ev.wheel.x < 0)
else if (ev.wheel.x < 0)
{
scancode = MOUSE_X1;
break;
@ -240,41 +240,41 @@ InReaction HotkeyInputPrepHandler(const SDL_Event_* ev)
// Create phantom 'unified-modifier' events when left- or right- modifier keys are pressed
// Just send them to this handler; don't let the imaginary event codes leak back to real SDL.
SDL_Event_ phantom;
phantom.ev.type = ((ev->ev.type == SDL_KEYDOWN) || (ev->ev.type == SDL_MOUSEBUTTONDOWN)) ? SDL_KEYDOWN : SDL_KEYUP;
if (phantom.ev.type == SDL_KEYDOWN)
phantom.ev.key.repeat = ev->ev.type == SDL_KEYDOWN ? ev->ev.key.repeat : 0;
SDL_Event phantom{};
phantom.type = ((ev.type == SDL_KEYDOWN) || (ev.type == SDL_MOUSEBUTTONDOWN)) ? SDL_KEYDOWN : SDL_KEYUP;
if (phantom.type == SDL_KEYDOWN)
phantom.key.repeat = ev.type == SDL_KEYDOWN ? ev.key.repeat : 0;
if (scancode == SDL_SCANCODE_LSHIFT || scancode == SDL_SCANCODE_RSHIFT)
{
phantom.ev.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_SHIFT);
unified[0] = (phantom.ev.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(&phantom);
phantom.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_SHIFT);
unified[0] = (phantom.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(phantom);
}
else if (scancode == SDL_SCANCODE_LCTRL || scancode == SDL_SCANCODE_RCTRL)
{
phantom.ev.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_CTRL);
unified[1] = (phantom.ev.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(&phantom);
phantom.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_CTRL);
unified[1] = (phantom.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(phantom);
}
else if (scancode == SDL_SCANCODE_LALT || scancode == SDL_SCANCODE_RALT)
{
phantom.ev.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_ALT);
unified[2] = (phantom.ev.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(&phantom);
phantom.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_ALT);
unified[2] = (phantom.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(phantom);
}
else if (scancode == SDL_SCANCODE_LGUI || scancode == SDL_SCANCODE_RGUI)
{
phantom.ev.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_SUPER);
unified[3] = (phantom.ev.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(&phantom);
phantom.key.keysym.scancode = static_cast<SDL_Scancode>(UNIFIED_SUPER);
unified[3] = (phantom.type == SDL_KEYDOWN);
return HotkeyInputPrepHandler(phantom);
}
// Check whether we have any hotkeys registered that include this scancode.
if (g_HotkeyMap.find(scancode) == g_HotkeyMap.end())
return IN_PASS;
currentEvent = ev;
currentEvent = &ev;
/**
* Hotkey behaviour spec (see also tests):
@ -299,9 +299,9 @@ InReaction HotkeyInputPrepHandler(const SDL_Event_* ev)
* ...Yes, this is all surprisingly complex.
*/
bool isReleasedKey = ev->ev.type == SDL_KEYUP || ev->ev.type == SDL_MOUSEBUTTONUP;
bool isReleasedKey = ev.type == SDL_KEYUP || ev.type == SDL_MOUSEBUTTONUP;
// Wheel events are pressed & released in the same go.
bool isInstantaneous = ev->ev.type == SDL_MOUSEWHEEL;
bool isInstantaneous = ev.type == SDL_MOUSEWHEEL;
if (!isInstantaneous)
{
@ -359,12 +359,12 @@ InReaction HotkeyInputPrepHandler(const SDL_Event_* ev)
return IN_PASS;
}
InReaction HotkeyInputActualHandler(const SDL_Event_* ev)
InReaction HotkeyInputActualHandler(const SDL_Event& ev)
{
if (!currentEvent)
return IN_PASS;
bool isInstantaneous = ev->ev.type == SDL_MOUSEWHEEL;
bool isInstantaneous = ev.type == SDL_MOUSEWHEEL;
// TODO: it's probably possible to break hotkeys somewhat if the "Up" event that would release a hotkey is handled
// by a priori handler - it might be safer to do that in the 'Prep' phase.
@ -424,12 +424,12 @@ InReaction HotkeyInputActualHandler(const SDL_Event_* ev)
for (const PressedHotkey& hotkey : isInstantaneous ? newPressedHotkeys : pressedHotkeys)
{
// Send a KeyPress event when a hotkey is pressed initially and on mouseButton and mouseWheel events.
if (ev->ev.type != SDL_KEYDOWN || ev->ev.key.repeat == 0)
if (ev.type != SDL_KEYDOWN || ev.key.repeat == 0)
{
SDL_Event_ hotkeyPressNotification;
hotkeyPressNotification.ev.type = hotkey.retriggered ? SDL_HOTKEYPRESS_SILENT : SDL_HOTKEYPRESS;
hotkeyPressNotification.ev.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(&hotkeyPressNotification);
SDL_Event hotkeyPressNotification{};
hotkeyPressNotification.type = hotkey.retriggered ? SDL_HOTKEYPRESS_SILENT : SDL_HOTKEYPRESS;
hotkeyPressNotification.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(hotkeyPressNotification);
}
// Send a HotkeyDown event on every key, mouseButton and mouseWheel event.
@ -439,12 +439,12 @@ InReaction HotkeyInputActualHandler(const SDL_Event_* ev)
// (It might be better to check for HotkeyIsPressed, however).
// For keys the event is repeated depending on hardware and OS configured interval.
// On linux, modifier keys (shift, alt, ctrl) are not repeated, see https://github.com/SFML/SFML/issues/122.
if (ev->ev.key.repeat == 0 && hotkey.retriggered)
if (ev.key.repeat == 0 && hotkey.retriggered)
continue;
SDL_Event_ hotkeyDownNotification;
hotkeyDownNotification.ev.type = SDL_HOTKEYDOWN;
hotkeyDownNotification.ev.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(&hotkeyDownNotification);
SDL_Event hotkeyDownNotification{};
hotkeyDownNotification.type = SDL_HOTKEYDOWN;
hotkeyDownNotification.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(hotkeyDownNotification);
}
// Release instantaneous events (e.g. mouse wheel) right away.
@ -454,19 +454,19 @@ InReaction HotkeyInputActualHandler(const SDL_Event_* ev)
for (const ReleasedHotkey& hotkey : releasedHotkeys)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = hotkey.wasRetriggered ? SDL_HOTKEYUP_SILENT : SDL_HOTKEYUP;
hotkeyNotification.ev.user.data1 = const_cast<char*>(hotkey.name);
in_push_priority_event(&hotkeyNotification);
SDL_Event hotkeyNotification{};
hotkeyNotification.type = hotkey.wasRetriggered ? SDL_HOTKEYUP_SILENT : SDL_HOTKEYUP;
hotkeyNotification.user.data1 = const_cast<char*>(hotkey.name);
in_push_priority_event(hotkeyNotification);
}
return IN_PASS;
}
bool EventWillFireHotkey(const SDL_Event_* ev, const CStr& keyname)
bool EventWillFireHotkey(const SDL_Event& ev, const CStr& keyname)
{
// Sanity check of sort. This parameter mostly exists because it looks right from the caller's perspective.
if (ev != currentEvent || !currentEvent)
if (&ev != currentEvent || !currentEvent)
return false;
return std::find_if(newPressedHotkeys.begin(), newPressedHotkeys.end(),
@ -478,10 +478,10 @@ void ResetActiveHotkeys()
newPressedHotkeys.clear();
for (const PressedHotkey& hotkey : pressedHotkeys)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = hotkey.retriggered ? SDL_HOTKEYUP_SILENT : SDL_HOTKEYUP;
hotkeyNotification.ev.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(&hotkeyNotification);
SDL_Event hotkeyNotification;
hotkeyNotification.type = hotkey.retriggered ? SDL_HOTKEYUP_SILENT : SDL_HOTKEYUP;
hotkeyNotification.user.data1 = const_cast<char*>(hotkey.mapping->name.c_str());
in_push_priority_event(hotkeyNotification);
}
pressedHotkeys.clear();
activeScancodes.clear();

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -39,7 +39,7 @@
#include <unordered_map>
#include <vector>
struct SDL_Event_;
union SDL_Event;
// SDL_Scancode is an enum, we'll use an explicit int to avoid including SDL in this header.
using SDL_Scancode_ = int;
@ -85,22 +85,22 @@ extern void UnloadHotkeys();
/**
* Updates g_HotkeyMap.
*/
extern InReaction HotkeyStateChange(const SDL_Event_* ev);
extern InReaction HotkeyStateChange(const SDL_Event& ev);
/**
* Detects hotkeys that should fire. This allows using EventWillFireHotkey,
* (and then possibly preventing those hotkeys from firing by handling the event).
*/
extern InReaction HotkeyInputPrepHandler(const SDL_Event_* ev);
extern InReaction HotkeyInputPrepHandler(const SDL_Event& ev);
/**
* Actually fires hotkeys.
*/
extern InReaction HotkeyInputActualHandler(const SDL_Event_* ev);
extern InReaction HotkeyInputActualHandler(const SDL_Event& ev);
/**
* @return whether the event @param ev will fire the hotkey @param keyname.
*/
extern bool EventWillFireHotkey(const SDL_Event_* ev, const CStr& keyname);
extern bool EventWillFireHotkey(const SDL_Event& ev, const CStr& keyname);
/**
* Resets all currently active hotkeys (and clears in-flight hotkeys).

View file

@ -271,16 +271,16 @@ void CProfileViewer::RenderProfile(CCanvas2D& canvas)
// Handle input
InReaction CProfileViewer::Input(const SDL_Event_* ev)
InReaction CProfileViewer::Input(const SDL_Event& ev)
{
switch(ev->ev.type)
switch(ev.type)
{
case SDL_KEYDOWN:
{
if (!m->profileVisible)
break;
int k = ev->ev.key.keysym.sym;
int k = ev.key.keysym.sym;
if (k >= SDLK_0 && k <= SDLK_9)
{
m->NavigateTree(k - SDLK_0);
@ -289,7 +289,7 @@ InReaction CProfileViewer::Input(const SDL_Event_* ev)
break;
}
case SDL_HOTKEYPRESS:
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
std::string hotkey = static_cast<const char*>(ev.user.data1);
if( hotkey == "profile.toggle" )
{
@ -334,7 +334,7 @@ InReaction CProfileViewer::Input(const SDL_Event_* ev)
return( IN_PASS );
}
InReaction CProfileViewer::InputThunk(const SDL_Event_* ev)
InReaction CProfileViewer::InputThunk(const SDL_Event& ev)
{
if (CProfileViewer::IsInitialised())
return g_ProfileViewer.Input(ev);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -30,7 +30,7 @@
#include <vector>
class CCanvas2D;
struct SDL_Event_;
union SDL_Event;
/**
* Struct ProfileColumn: Describes one column of an AbstractProfileTable.
@ -156,7 +156,7 @@ public:
* @return IN_PASS or IN_HANDLED depending on whether the event relates
* to the profiling display.
*/
InReaction Input(const SDL_Event_* ev);
InReaction Input(const SDL_Event& ev);
/**
* AddRootTable: Add a new profile table as a root table (i.e. the
@ -178,7 +178,7 @@ public:
* This allows our input handler to be installed via in_add_handler
* like a normal, global function input handler.
*/
static InReaction InputThunk(const SDL_Event_* ev);
static InReaction InputThunk(const SDL_Event& ev);
/**
* SaveToFile: Save the current profiler data (for all profile tables)

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -95,18 +95,18 @@ void CTouchInput::OnFingerUp(int id, int x, int y)
{
m_State = STATE_INACTIVE;
SDL_Event_ ev;
ev.ev.button.button = SDL_BUTTON_LEFT;
ev.ev.button.x = m_Pos[0].X;
ev.ev.button.y = m_Pos[0].Y;
SDL_Event ev;
ev.button.button = SDL_BUTTON_LEFT;
ev.button.x = m_Pos[0].X;
ev.button.y = m_Pos[0].Y;
ev.ev.type = SDL_MOUSEBUTTONDOWN;
ev.ev.button.state = SDL_PRESSED;
SDL_PushEvent(&ev.ev);
ev.type = SDL_MOUSEBUTTONDOWN;
ev.button.state = SDL_PRESSED;
SDL_PushEvent(&ev);
ev.ev.type = SDL_MOUSEBUTTONUP;
ev.ev.button.state = SDL_RELEASED;
SDL_PushEvent(&ev.ev);
ev.type = SDL_MOUSEBUTTONUP;
ev.button.state = SDL_RELEASED;
SDL_PushEvent(&ev);
}
else if (m_State == STATE_ZOOMING && id == 1)
{
@ -172,44 +172,44 @@ void CTouchInput::Frame()
{
m_State = STATE_INACTIVE;
SDL_Event_ ev;
ev.ev.button.button = SDL_BUTTON_RIGHT;
ev.ev.button.x = m_Pos[0].X;
ev.ev.button.y = m_Pos[0].Y;
SDL_Event ev;
ev.button.button = SDL_BUTTON_RIGHT;
ev.button.x = m_Pos[0].X;
ev.button.y = m_Pos[0].Y;
ev.ev.type = SDL_MOUSEBUTTONDOWN;
ev.ev.button.state = SDL_PRESSED;
SDL_PushEvent(&ev.ev);
ev.type = SDL_MOUSEBUTTONDOWN;
ev.button.state = SDL_PRESSED;
SDL_PushEvent(&ev);
ev.ev.type = SDL_MOUSEBUTTONUP;
ev.ev.button.state = SDL_RELEASED;
SDL_PushEvent(&ev.ev);
ev.type = SDL_MOUSEBUTTONUP;
ev.button.state = SDL_RELEASED;
SDL_PushEvent(&ev);
}
}
InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event& ev)
{
if (!IsEnabled())
return IN_PASS;
#if EMULATE_FINGERS_WITH_MOUSE
switch(ev->ev.type)
switch(ev.type)
{
case SDL_MOUSEBUTTONDOWN:
{
int button;
if (ev->ev.button.button == SDL_BUTTON_LEFT)
if (ev.button.button == SDL_BUTTON_LEFT)
button = 0;
else if (ev->ev.button.button == SDL_BUTTON_RIGHT)
else if (ev.button.button == SDL_BUTTON_RIGHT)
button = 1;
else
return IN_PASS;
m_MouseEmulateDownPos[button] = CVector2D(ev->ev.button.x, ev->ev.button.y);
m_MouseEmulateDownPos[button] = CVector2D(ev.button.x, ev.button.y);
if (m_MouseEmulateState[button] == MOUSE_INACTIVE)
{
m_MouseEmulateState[button] = MOUSE_ACTIVATING;
OnFingerDown(button, ev->ev.button.x, ev->ev.button.y);
OnFingerDown(button, ev.button.x, ev.button.y);
}
else if (m_MouseEmulateState[button] == MOUSE_ACTIVE_UP)
{
@ -221,9 +221,9 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
case SDL_MOUSEBUTTONUP:
{
int button;
if (ev->ev.button.button == SDL_BUTTON_LEFT)
if (ev.button.button == SDL_BUTTON_LEFT)
button = 0;
else if (ev->ev.button.button == SDL_BUTTON_RIGHT)
else if (ev.button.button == SDL_BUTTON_RIGHT)
button = 1;
else
return IN_PASS;
@ -234,11 +234,11 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
}
else if (m_MouseEmulateState[button] == MOUSE_ACTIVE_DOWN)
{
float dist = (m_MouseEmulateDownPos[button] - CVector2D(ev->ev.button.x, ev->ev.button.y)).Length();
float dist = (m_MouseEmulateDownPos[button] - CVector2D(ev.button.x, ev.button.y)).Length();
if (dist <= 2)
{
m_MouseEmulateState[button] = MOUSE_INACTIVE;
OnFingerUp(button, ev->ev.button.x, ev->ev.button.y);
OnFingerUp(button, ev.button.x, ev.button.y);
}
else
{
@ -254,7 +254,7 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
{
if (m_MouseEmulateState[i] == MOUSE_ACTIVE_DOWN)
{
OnFingerMotion(i, ev->ev.motion.x, ev->ev.motion.y);
OnFingerMotion(i, ev.motion.x, ev.motion.y);
}
}
return IN_HANDLED;
@ -262,7 +262,7 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
}
#endif
switch(ev->ev.type)
switch(ev.type)
{
case SDL_FINGERDOWN:
case SDL_FINGERUP:
@ -270,18 +270,18 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
{
// Map finger events onto the mouse, for basic testing
debug_printf("finger %s tid=%" PRId64 " fid=%" PRId64 " x=%f y=%f dx=%f dy=%f p=%f\n",
ev->ev.type == SDL_FINGERDOWN ? "down" :
ev->ev.type == SDL_FINGERUP ? "up" :
ev->ev.type == SDL_FINGERMOTION ? "motion" : "?",
ev->ev.tfinger.touchId, ev->ev.tfinger.fingerId,
ev->ev.tfinger.x, ev->ev.tfinger.y, ev->ev.tfinger.dx, ev->ev.tfinger.dy, ev->ev.tfinger.pressure);
ev.type == SDL_FINGERDOWN ? "down" :
ev.type == SDL_FINGERUP ? "up" :
ev.type == SDL_FINGERMOTION ? "motion" : "?",
ev.tfinger.touchId, ev.tfinger.fingerId,
ev.tfinger.x, ev.tfinger.y, ev.tfinger.dx, ev.tfinger.dy, ev.tfinger.pressure);
if (ev->ev.type == SDL_FINGERDOWN)
OnFingerDown(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
else if (ev->ev.type == SDL_FINGERUP)
OnFingerUp(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
else if (ev->ev.type == SDL_FINGERMOTION)
OnFingerMotion(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
if (ev.type == SDL_FINGERDOWN)
OnFingerDown(ev.tfinger.fingerId, g_xres * ev.tfinger.x, g_yres * ev.tfinger.y);
else if (ev.type == SDL_FINGERUP)
OnFingerUp(ev.tfinger.fingerId, g_xres * ev.tfinger.x, g_yres * ev.tfinger.y);
else if (ev.type == SDL_FINGERMOTION)
OnFingerMotion(ev.tfinger.fingerId, g_xres * ev.tfinger.x, g_yres * ev.tfinger.y);
return IN_HANDLED;
}
}
@ -291,7 +291,7 @@ InReaction CTouchInput::HandleEvent([[maybe_unused]] const SDL_Event_* ev)
CTouchInput g_TouchInput;
InReaction touch_input_handler(const SDL_Event_* ev)
InReaction touch_input_handler(const SDL_Event& ev)
{
return g_TouchInput.HandleEvent(ev);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,7 +24,7 @@
#include <cstddef>
struct SDL_Event_;
union SDL_Event;
/**
* Maps touch events (e.g. on Android touchscreen devices) onto mouse events
@ -41,7 +41,7 @@ public:
*/
bool IsEnabled();
InReaction HandleEvent(const SDL_Event_* ev);
InReaction HandleEvent(const SDL_Event& ev);
/**
* Should be called once per frame to perform updates.
@ -90,6 +90,6 @@ private:
extern CTouchInput g_TouchInput;
extern InReaction touch_input_handler(const SDL_Event_* ev);
extern InReaction touch_input_handler(const SDL_Event& ev);
#endif // INCLUDED_TOUCHINPUT

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -52,20 +52,20 @@ private:
void fakeInput(const char* key, bool keyDown)
{
SDL_Event_ ev;
ev.ev.type = keyDown ? SDL_KEYDOWN : SDL_KEYUP;
ev.ev.key.repeat = 0;
ev.ev.key.keysym.scancode = SDL_GetScancodeFromName(key);
GlobalsInputHandler(&ev);
HotkeyInputPrepHandler(&ev);
HotkeyInputActualHandler(&ev);
SDL_Event ev;
ev.type = keyDown ? SDL_KEYDOWN : SDL_KEYUP;
ev.key.repeat = 0;
ev.key.keysym.scancode = SDL_GetScancodeFromName(key);
GlobalsInputHandler(ev);
HotkeyInputPrepHandler(ev);
HotkeyInputActualHandler(ev);
hotkeyPress = false;
hotkeyUp = false;
while(in_poll_priority_event(&ev))
while(in_poll_priority_event(ev))
{
hotkeyUp |= ev.ev.type == SDL_HOTKEYUP;
hotkeyPress |= ev.ev.type == SDL_HOTKEYPRESS;
HotkeyStateChange(&ev);
hotkeyUp |= ev.type == SDL_HOTKEYUP;
hotkeyPress |= ev.type == SDL_HOTKEYPRESS;
HotkeyStateChange(ev);
}
}

View file

@ -259,6 +259,10 @@ CDeviceCommandContext::CDeviceCommandContext(CDevice* device)
// Currently we don't support upload buffers for GL.
if (type == CBuffer::Type::UPLOAD)
continue;
#if CONFIG2_GLES
if (type == CBuffer::Type::UNIFORM)
continue;
#endif
const GLenum target = BufferTypeToGLTarget(type);
const GLuint handle = 0;
m_BoundBuffers[index].first = target;

View file

@ -213,8 +213,8 @@ std::unique_ptr<CTexture> CTexture::Create(
break;
#if CONFIG2_GLES
// GLES requires pixel type == UNSIGNED_SHORT or UNSIGNED_INT for depth.
case Format::D16_UNORM: FALLTHROUGH;
case Format::D24_UNORM: FALLTHROUGH;
case Format::D16_UNORM: [[fallthrough]];
case Format::D24_UNORM: [[fallthrough]];
case Format::D32_SFLOAT:
internalFormat = GL_DEPTH_COMPONENT;
pixelFormat = GL_DEPTH_COMPONENT;

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(ActorEditor
PRIVATE
$<$<PLATFORM_ID:Windows>: ActorEditor.rc> # Caution, the blank before the File is needed
ActorEditor.cpp
)

View file

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasObject
PRIVATE
AtlasObject.h
AtlasObjectImpl.cpp
AtlasObjectImpl.h
AtlasObjectJS.cpp
AtlasObjectText.cpp
AtlasObjectText.h
AtlasObjectXML.cpp
JSONSpiritInclude.h
)
add_subdirectory(tests/)

View file

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasObject
PRIVATE
test_AtlasObjectXML.h
)

View file

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
ActorEditor.cpp
ActorEditor.h
ActorEditorListCtrl.cpp
ActorEditorListCtrl.h
AnimListEditor.cpp
AnimListEditor.h
PropListEditor.cpp
PropListEditor.h
TexListEditor.cpp
TexListEditor.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
add_subdirectory(ActorEditor/)
add_subdirectory(CustomControls/)
add_subdirectory(General/)
add_subdirectory(Misc/)
add_subdirectory(ScenarioEditor/)

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
ActionButton.cpp
ActionButton.h
ToolButton.cpp
ToolButton.h
)

View file

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
add_subdirectory(Buttons/)
add_subdirectory(Canvas/)
add_subdirectory(ColorDialog/)
add_subdirectory(DraggableListCtrl/)
add_subdirectory(EditableListCtrl/)
add_subdirectory(FileHistory/)
add_subdirectory(HighResTimer/)
add_subdirectory(MapDialog/)
add_subdirectory(MapResizeDialog/)
add_subdirectory(SnapSplitterWindow/)
add_subdirectory(VirtualDirTreeCtrl/)
add_subdirectory(Windows/)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
Canvas.cpp
Canvas.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
ColorDialog.cpp
ColorDialog.h
)

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
DraggableListCtrl.cpp
DraggableListCtrl.h
DraggableListCtrlCommands.cpp
DraggableListCtrlCommands.h
)

View file

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
EditableListCtrl.cpp
EditableListCtrl.h
EditableListCtrlCommands.cpp
EditableListCtrlCommands.h
FieldEditCtrl.cpp
FieldEditCtrl.h
ListCtrlValidator.cpp
ListCtrlValidator.h
QuickComboBox.cpp
QuickComboBox.h
QuickFileCtrl.cpp
QuickFileCtrl.h
QuickTextCtrl.cpp
QuickTextCtrl.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
FileHistory.cpp
FileHistory.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
HighResTimer.cpp
HighResTimer.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
MapDialog.cpp
MapDialog.h
)

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
MapResizeDialog.cpp
MapResizeDialog.h
PseudoMiniMapPanel.cpp
PseudoMiniMapPanel.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
SnapSplitterWindow.cpp
SnapSplitterWindow.h
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
virtualdirtreectrl.cpp
virtualdirtreectrl.h
)

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
AtlasDialog.cpp
AtlasDialog.h
AtlasWindow.cpp
AtlasWindow.h
)

View file

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
AtlasClipboard.cpp
AtlasClipboard.h
AtlasEventLoop.cpp
AtlasEventLoop.h
AtlasWindowCommand.cpp
AtlasWindowCommand.h
AtlasWindowCommandProc.cpp
AtlasWindowCommandProc.h
Datafile.cpp
Datafile.h
IAtlasSerialiser.h
Observable.cpp
Observable.h
)

View file

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
DLLInterface.cpp
DLLInterface.h
KeyMap.cpp
KeyMap.h
actored.h
precompiled.cpp
precompiled.h
$<$<PLATFORM_ID:Windows>: atlas.rc> # Caution, the blank before the File is needed
)
add_subdirectory(Graphics/)

View file

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
include(GNUInstallDirs)
install(
FILES
${CMAKE_CURRENT_SOURCE_DIR}/ActorEditor.ico
${CMAKE_CURRENT_SOURCE_DIR}/ArchiveViewer.ico
${CMAKE_CURRENT_SOURCE_DIR}/FileConverter.ico
${CMAKE_CURRENT_SOURCE_DIR}/ScenarioEditor.ico
DESTINATION
${CMAKE_INSTALL_DATADIR}/0ad/tools/ActorEditor/icons
)
endif()

View file

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
ScenarioEditor.cpp
ScenarioEditor.h
SectionLayout.cpp
SectionLayout.h
)
add_subdirectory(Sections/)
add_subdirectory(Tools/)

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
add_subdirectory(Cinema/)
add_subdirectory(Common/)
add_subdirectory(Environment/)
add_subdirectory(Map/)
add_subdirectory(Object/)
add_subdirectory(Player/)
add_subdirectory(Terrain/)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.25.1...4.0.0)
target_sources(AtlasUI
PRIVATE
Cinema.cpp
Cinema.h
)

Some files were not shown because too many files have changed in this diff Show more