From 7f890122e6ad1d7e21fe1e20a4d5d197691024e3 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Sat, 25 Apr 2020 09:45:46 +0400
Subject: [PATCH] Add methods to detect appimage, static binary and forced gtk
 dialog

---
 .../platform/linux/specific_linux.cpp         | 142 +++++++++++++-----
 .../platform/linux/specific_linux.h           |   4 +
 2 files changed, 108 insertions(+), 38 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index c12e5a078..20ba05334 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -194,30 +194,31 @@ bool GenerateDesktopFile(
 
 	QFile target(targetFile);
 	if (target.open(QIODevice::WriteOnly)) {
-#ifdef DESKTOP_APP_USE_PACKAGED
-		fileText = fileText.replace(
-			QRegularExpression(
-				qsl("^Exec=(.*) -- %u$"),
-				QRegularExpression::MultilineOption),
-			qsl("Exec=\\1")
-				+ (args.isEmpty() ? QString() : ' ' + args));
-#else // DESKTOP_APP_USE_PACKAGED
-		fileText = fileText.replace(
-			QRegularExpression(
-				qsl("^TryExec=.*$"),
-				QRegularExpression::MultilineOption),
-			qsl("TryExec=")
-				+ QFile::encodeName(cExeDir() + cExeName())
-					.replace('\\', qsl("\\\\")));
-		fileText = fileText.replace(
-			QRegularExpression(
-				qsl("^Exec=.*$"),
-				QRegularExpression::MultilineOption),
-			qsl("Exec=")
-				+ EscapeShell(QFile::encodeName(cExeDir() + cExeName()))
-					.replace('\\', qsl("\\\\"))
-				+ (args.isEmpty() ? QString() : ' ' + args));
-#endif // !DESKTOP_APP_USE_PACKAGED
+		if (IsStaticBinary() || InAppImage()) {
+			fileText = fileText.replace(
+				QRegularExpression(
+					qsl("^TryExec=.*$"),
+					QRegularExpression::MultilineOption),
+				qsl("TryExec=")
+					+ QFile::encodeName(cExeDir() + cExeName())
+						.replace('\\', qsl("\\\\")));
+			fileText = fileText.replace(
+				QRegularExpression(
+					qsl("^Exec=.*$"),
+					QRegularExpression::MultilineOption),
+				qsl("Exec=")
+					+ EscapeShell(QFile::encodeName(cExeDir() + cExeName()))
+						.replace('\\', qsl("\\\\"))
+					+ (args.isEmpty() ? QString() : ' ' + args));
+		} else {
+			fileText = fileText.replace(
+				QRegularExpression(
+					qsl("^Exec=(.*) -- %u$"),
+					QRegularExpression::MultilineOption),
+				qsl("Exec=\\1")
+					+ (args.isEmpty() ? QString() : ' ' + args));
+		}
+
 		target.write(fileText.toUtf8());
 		target.close();
 
@@ -249,6 +250,27 @@ bool InSnap() {
 	return Snap;
 }
 
+bool InAppImage() {
+	static const auto AppImage = qEnvironmentVariableIsSet("APPIMAGE");
+	return AppImage;
+}
+
+bool IsStaticBinary() {
+#ifdef DESKTOP_APP_USE_PACKAGED
+		return false;
+#else // DESKTOP_APP_USE_PACKAGED
+		return true;
+#endif // !DESKTOP_APP_USE_PACKAGED
+}
+
+bool IsGtkFileDialogForced() {
+#ifdef TDESKTOP_FORCE_GTK_FILE_DIALOG
+	return true;
+#else // TDESKTOP_FORCE_GTK_FILE_DIALOG
+	return false;
+#endif // !TDESKTOP_FORCE_GTK_FILE_DIALOG
+}
+
 bool IsXDGDesktopPortalPresent() {
 #ifdef TDESKTOP_DISABLE_DBUS_INTEGRATION
 	static const auto XDGDesktopPortalPresent = false;
@@ -266,7 +288,11 @@ bool UseXDGDesktopPortal() {
 		const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL");
 		const auto portalPresent = IsXDGDesktopPortalPresent();
 
-		return (DesktopEnvironment::IsKDE() || envVar) && portalPresent;
+		return (
+			DesktopEnvironment::IsKDE()
+				|| InSnap()
+				|| envVar
+			) && portalPresent;
 	}();
 
 	return UsePortal;
@@ -288,7 +314,7 @@ QString ProcessNameByPID(const QString &pid) {
 	return QString();
 }
 
-QString CurrentExecutablePath(int argc, char *argv[]) {
+QString RealExecutablePath(int argc, char *argv[]) {
 	const auto processName = ProcessNameByPID(qsl("self"));
 
 	// Fallback to the first command line argument.
@@ -299,6 +325,25 @@ QString CurrentExecutablePath(int argc, char *argv[]) {
 			: QString();
 }
 
+QString CurrentExecutablePath(int argc, char *argv[]) {
+	if (InAppImage()) {
+		const auto appimagePath = QString::fromUtf8(qgetenv("APPIMAGE"));
+		const auto appimagePathList = appimagePath.split('/');
+
+		if (qEnvironmentVariableIsSet("ARGV0")
+			&& appimagePathList.size() >= 5
+			&& appimagePathList[1] == qstr("run")
+			&& appimagePathList[2] == qstr("user")
+			&& appimagePathList[4] == qstr("appimagelauncherfs")) {
+			return QString::fromUtf8(qgetenv("ARGV0"));
+		}
+
+		return appimagePath;
+	}
+
+	return RealExecutablePath(argc, argv);
+}
+
 QString AppRuntimeDirectory() {
 	static const auto RuntimeDirectory = [&] {
 		auto runtimeDir = QStandardPaths::writableLocation(
@@ -356,6 +401,20 @@ QString GetLauncherBasename() {
 				.arg(cExeName());
 		}
 
+		if (InAppImage()) {
+			const auto appimagePath = qsl("file://%1%2")
+				.arg(cExeDir())
+				.arg(cExeName())
+				.toUtf8();
+
+			char md5Hash[33] = { 0 };
+			hashMd5Hex(appimagePath.constData(), appimagePath.size(), md5Hash);
+
+			return qsl("appimagekit_%1-%2")
+				.arg(md5Hash)
+				.arg(AppName.utf16().replace(' ', '_'));
+		}
+
 		const auto possibleBasenames = std::vector<QString>{
 			qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME)),
 			qsl("Telegram")
@@ -564,6 +623,10 @@ namespace Platform {
 void start() {
 	LOG(("Launcher filename: %1").arg(GetLauncherFilename()));
 
+	if (InAppImage()) {
+		qputenv("LIBGL_ALWAYS_INDIRECT", "1");
+	}
+
 #ifdef TDESKTOP_USE_FONTCONFIG_FALLBACK
 	FallbackFontConfig();
 #endif // TDESKTOP_USE_FONTCONFIG_FALLBACK
@@ -571,21 +634,24 @@ void start() {
 	qputenv("PULSE_PROP_application.name", AppName.utf8());
 	qputenv("PULSE_PROP_application.icon_name", GetIconName().toLatin1());
 
-#ifdef TDESKTOP_FORCE_GTK_FILE_DIALOG
-	LOG(("Checking for XDG Desktop Portal..."));
-	// this can give us a chance to use a proper file dialog for current session
-	if (IsXDGDesktopPortalPresent()) {
-		LOG(("XDG Desktop Portal is present!"));
-		if (UseXDGDesktopPortal()) {
-			LOG(("Usage of XDG Desktop Portal is enabled."));
-			qputenv("QT_QPA_PLATFORMTHEME", "xdgdesktopportal");
+	if(IsStaticBinary() 
+		|| InAppImage()
+		|| InSnap()
+		|| IsGtkFileDialogForced()) {
+		LOG(("Checking for XDG Desktop Portal..."));
+		// this can give us a chance to use a proper file dialog for current session
+		if (IsXDGDesktopPortalPresent()) {
+			LOG(("XDG Desktop Portal is present!"));
+			if (UseXDGDesktopPortal()) {
+				LOG(("Usage of XDG Desktop Portal is enabled."));
+				qputenv("QT_QPA_PLATFORMTHEME", "xdgdesktopportal");
+			} else {
+				LOG(("Usage of XDG Desktop Portal is disabled."));
+			}
 		} else {
-			LOG(("Usage of XDG Desktop Portal is disabled."));
+			LOG(("XDG Desktop Portal is not present :("));
 		}
-	} else {
-		LOG(("XDG Desktop Portal is not present :("));
 	}
-#endif // TDESKTOP_FORCE_GTK_FILE_DIALOG
 }
 
 void finish() {
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h
index 28e3ef386..f3a79555e 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.h
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.h
@@ -22,11 +22,15 @@ inline void SetWatchingMediaKeys(bool watching) {
 
 bool InSandbox();
 bool InSnap();
+bool InAppImage();
+bool IsStaticBinary();
+bool IsGtkFileDialogForced();
 
 bool IsXDGDesktopPortalPresent();
 bool UseXDGDesktopPortal();
 
 QString ProcessNameByPID(const QString &pid);
+QString RealExecutablePath(int argc, char *argv[]);
 QString CurrentExecutablePath(int argc, char *argv[]);
 
 QString AppRuntimeDirectory();