Merge pull request #8562 from Robyt3/Client-Open-Link-SDL

Use `SDL_OpenURL` to open links and files on Android
This commit is contained in:
Dennis Felsing 2024-07-05 17:56:03 +00:00 committed by GitHub
commit 398dc7ece9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 94 additions and 61 deletions

View file

@ -4175,6 +4175,7 @@ bool is_process_alive(PROCESS process)
#endif
}
#if !defined(CONF_PLATFORM_ANDROID)
int open_link(const char *link)
{
#if defined(CONF_FAMILY_WINDOWS)
@ -4236,6 +4237,7 @@ int open_file(const char *path)
return open_link(buf);
#endif
}
#endif // !defined(CONF_PLATFORM_ANDROID)
struct SECURE_RANDOM_DATA
{

View file

@ -2583,6 +2583,7 @@ int kill_process(PROCESS process);
*/
bool is_process_alive(PROCESS process);
#if !defined(CONF_PLATFORM_ANDROID)
/**
* Opens a link in the browser.
*
@ -2598,11 +2599,11 @@ bool is_process_alive(PROCESS process);
int open_link(const char *link);
/**
* Opens a file or directory with default program.
* Opens a file or directory with the default program.
*
* @ingroup Shell
*
* @param path The path to open.
* @param path The file or folder to open with the default program.
*
* @return `1` on success, `0` on failure.
*
@ -2610,6 +2611,7 @@ int open_link(const char *link);
* @remark This may not be called with untrusted input or it'll result in arbitrary code execution, especially on Windows.
*/
int open_file(const char *path);
#endif // !defined(CONF_PLATFORM_ANDROID)
/**
* @defgroup Secure-Random

View file

@ -289,6 +289,27 @@ public:
virtual CChecksumData *ChecksumData() = 0;
virtual int UdpConnectivity(int NetType) = 0;
/**
* Opens a link in the browser.
*
* @param pLink The link to open in a browser.
*
* @return `true` on success, `false` on failure.
*
* @remark This may not be called with untrusted input or it'll result in arbitrary code execution, especially on Windows.
*/
virtual bool ViewLink(const char *pLink) = 0;
/**
* Opens a file or directory with the default program.
*
* @param pFilename The file or folder to open with the default program.
*
* @return `true` on success, `false` on failure.
*
* @remark This may not be called with untrusted input or it'll result in arbitrary code execution, especially on Windows.
*/
virtual bool ViewFile(const char *pFilename) = 0;
#if defined(CONF_FAMILY_WINDOWS)
virtual void ShellRegister() = 0;
virtual void ShellUnregister() = 0;

View file

@ -4781,6 +4781,51 @@ int CClient::UdpConnectivity(int NetType)
return Connectivity;
}
bool CClient::ViewLink(const char *pLink)
{
#if defined(CONF_PLATFORM_ANDROID)
if(SDL_OpenURL(pLink) == 0)
{
return true;
}
log_error("client", "Failed to open link '%s' (%s)", pLink, SDL_GetError());
return false;
#else
if(open_link(pLink))
{
return true;
}
log_error("client", "Failed to open link '%s'", pLink);
return false;
#endif
}
bool CClient::ViewFile(const char *pFilename)
{
#if defined(CONF_PLATFORM_MACOS)
return ViewLink(pFilename);
#else
// Create a file link so the path can contain forward and
// backward slashes. But the file link must be absolute.
char aWorkingDir[IO_MAX_PATH_LENGTH];
if(fs_is_relative_path(pFilename))
{
if(!fs_getcwd(aWorkingDir, sizeof(aWorkingDir)))
{
log_error("client", "Failed to open file '%s' (failed to get working directory)", pFilename);
return false;
}
str_append(aWorkingDir, "/");
}
else
aWorkingDir[0] = '\0';
char aFileLink[IO_MAX_PATH_LENGTH];
str_format(aFileLink, sizeof(aFileLink), "file://%s%s", aWorkingDir, pFilename);
return ViewLink(aFileLink);
#endif
}
#if defined(CONF_FAMILY_WINDOWS)
void CClient::ShellRegister()
{

View file

@ -503,6 +503,9 @@ public:
CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
int UdpConnectivity(int NetType) override;
bool ViewLink(const char *pLink) override;
bool ViewFile(const char *pFilename) override;
#if defined(CONF_FAMILY_WINDOWS)
void ShellRegister() override;
void ShellUnregister() override;

View file

@ -1625,10 +1625,7 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
static CButtonContainer s_ButtonOpenFolder;
if(DoButton_Menu(&s_ButtonOpenFolder, Localize("Videos directory"), 0, &OpenFolder))
{
if(!open_file(aSaveFolder))
{
dbg_msg("menus", "couldn't open file '%s'", aSaveFolder);
}
Client()->ViewFile(aSaveFolder);
}
static CButtonContainer s_ButtonOk;

View file

@ -1458,10 +1458,7 @@ void CMenus::RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemAc
{
char aBuf[IO_MAX_PATH_LENGTH];
Storage()->GetCompletePath(m_DemolistSelectedIndex >= 0 ? m_vpFilteredDemos[m_DemolistSelectedIndex]->m_StorageType : IStorage::TYPE_SAVE, m_aCurrentDemoFolder[0] == '\0' ? "demos" : m_aCurrentDemoFolder, aBuf, sizeof(aBuf));
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_DemosDirectoryButton, &DemosDirectoryButton, Localize("Open the directory that contains the demo files"));
}

View file

@ -1166,10 +1166,7 @@ void CMenus::RenderGhost(CUIRect MainView)
char aBuf[IO_MAX_PATH_LENGTH];
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "ghosts", aBuf, sizeof(aBuf));
Storage()->CreateFolder("ghosts", IStorage::TYPE_SAVE);
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
Status.VSplitLeft(5.0f, &Button, &Status);

View file

@ -77,8 +77,8 @@ bool CMenusKeyBinder::OnInput(const IInput::CEvent &Event)
void CMenus::RenderSettingsGeneral(CUIRect MainView)
{
char aBuf[128 + IO_MAX_PATH_LENGTH];
CUIRect Label, Button, Left, Right, Game, Client;
MainView.HSplitTop(150.0f, &Game, &Client);
CUIRect Label, Button, Left, Right, Game, ClientSettings;
MainView.HSplitTop(150.0f, &Game, &ClientSettings);
// game
{
@ -139,10 +139,10 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView)
// client
{
// headline
Client.HSplitTop(30.0f, &Label, &Client);
ClientSettings.HSplitTop(30.0f, &Label, &ClientSettings);
Ui()->DoLabel(&Label, Localize("Client"), 20.0f, TEXTALIGN_ML);
Client.HSplitTop(5.0f, nullptr, &Client);
Client.VSplitMid(&Left, &Right, 20.0f);
ClientSettings.HSplitTop(5.0f, nullptr, &ClientSettings);
ClientSettings.VSplitMid(&Left, &Right, 20.0f);
// skip main menu
Left.HSplitTop(20.0f, &Button, &Left);
@ -165,10 +165,7 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView)
if(DoButton_Menu(&s_SettingsButtonId, Localize("Settings file"), 0, &SettingsButton))
{
Storage()->GetCompletePath(IStorage::TYPE_SAVE, CONFIG_FILE, aBuf, sizeof(aBuf));
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_SettingsButtonId, &SettingsButton, Localize("Open the settings file"));
@ -179,10 +176,7 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView)
if(DoButton_Menu(&s_ConfigButtonId, Localize("Config directory"), 0, &ConfigButton))
{
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "", aBuf, sizeof(aBuf));
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_ConfigButtonId, &ConfigButton, Localize("Open the directory that contains the configuration and user files"));
@ -194,10 +188,7 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView)
{
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "themes", aBuf, sizeof(aBuf));
Storage()->CreateFolder("themes", IStorage::TYPE_SAVE);
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_ThemesButtonId, &DirectoryButton, Localize("Open the directory to add custom themes"));
@ -938,11 +929,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
static CButtonContainer s_SkinDatabaseButton;
if(DoButton_Menu(&s_SkinDatabaseButton, Localize("Skin Database"), 0, &DatabaseButton))
{
const char *pLink = "https://ddnet.org/skins/";
if(!open_link(pLink))
{
dbg_msg("menus", "couldn't open link '%s'", pLink);
}
Client()->ViewLink("https://ddnet.org/skins/");
}
static CButtonContainer s_DirectoryButton;
@ -950,10 +937,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
{
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "skins", aBuf, sizeof(aBuf));
Storage()->CreateFolder("skins", IStorage::TYPE_SAVE);
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButton, &DirectoryButton, Localize("Open the directory to add custom skins"));

View file

@ -648,10 +648,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
Storage()->GetCompletePath(IStorage::TYPE_SAVE, aBufFull, aBuf, sizeof(aBuf));
Storage()->CreateFolder("assets", IStorage::TYPE_SAVE);
Storage()->CreateFolder(aBufFull, IStorage::TYPE_SAVE);
if(!open_file(aBuf))
{
dbg_msg("menus", "couldn't open file '%s'", aBuf);
}
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_AssetsDirId, &DirectoryButton, Localize("Open the directory to add custom assets"));

View file

@ -43,11 +43,7 @@ void CMenus::RenderStartMenu(CUIRect MainView)
static CButtonContainer s_DiscordButton;
if(DoButton_Menu(&s_DiscordButton, Localize("Discord"), 0, &Button, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)))
{
const char *pLink = Localize("https://ddnet.org/discord");
if(!open_link(pLink))
{
dbg_msg("menus", "couldn't open link '%s'", pLink);
}
Client()->ViewLink(Localize("https://ddnet.org/discord"));
}
ExtMenu.HSplitBottom(5.0f, &ExtMenu, 0); // little space
@ -55,11 +51,7 @@ void CMenus::RenderStartMenu(CUIRect MainView)
static CButtonContainer s_LearnButton;
if(DoButton_Menu(&s_LearnButton, Localize("Learn"), 0, &Button, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)))
{
const char *pLink = Localize("https://wiki.ddnet.org/");
if(!open_link(pLink))
{
dbg_msg("menus", "couldn't open link '%s'", pLink);
}
Client()->ViewLink(Localize("https://wiki.ddnet.org/"));
}
ExtMenu.HSplitBottom(5.0f, &ExtMenu, 0); // little space
@ -96,11 +88,7 @@ void CMenus::RenderStartMenu(CUIRect MainView)
static CButtonContainer s_WebsiteButton;
if(DoButton_Menu(&s_WebsiteButton, Localize("Website"), 0, &Button, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)))
{
const char *pLink = "https://ddnet.org/";
if(!open_link(pLink))
{
dbg_msg("menus", "couldn't open link '%s'", pLink);
}
Client()->ViewLink("https://ddnet.org/");
}
ExtMenu.HSplitBottom(5.0f, &ExtMenu, 0); // little space

View file

@ -5396,7 +5396,7 @@ void CEditor::RenderFileDialog()
{
char aOpenPath[IO_MAX_PATH_LENGTH];
Storage()->GetCompletePath(m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : IStorage::TYPE_SAVE, m_pFileDialogPath, aOpenPath, sizeof(aOpenPath));
if(!open_file(aOpenPath))
if(!Client()->ViewFile(aOpenPath))
{
ShowFileDialogError("Failed to open the directory '%s'.", aOpenPath);
}
@ -7712,7 +7712,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
if(DoButton_Editor(&s_HelpButton, "?", 0, &Help, 0, "[F1] Open the DDNet Wiki page for the Map Editor in a web browser") || (Input()->KeyPress(KEY_F1) && m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr))
{
const char *pLink = Localize("https://wiki.ddnet.org/wiki/Mapping");
if(!open_link(pLink))
if(!Client()->ViewLink(pLink))
{
ShowFileDialogError("Failed to open the link '%s' in the default web browser.", pLink);
}