Compare commits

...

38 Commits

Author SHA1 Message Date
John Preston
f09b91ebb5 Beta version 2.8.5: Fix build on non-Windows. 2021-07-02 04:45:58 +03:00
John Preston
57b147e0c8 Beta version 2.8.5.
- Use ANGLE for OpenGL over DirectX 9 / DirectX 11 (Windows).
- Use GTK from a child process (Linux).
2021-07-02 00:59:51 +03:00
Ilya Fedin
551ea7d879 Move GTK integration out of process with D-Bus 2021-07-02 00:59:36 +03:00
John Preston
d3c9bb0bc6 Try disabling native child OpenGL workaround. 2021-07-02 00:37:27 +03:00
John Preston
c711b1f1df Fix build on non-Linux systems. 2021-07-02 00:27:25 +03:00
23rd
af7ea90246 Added floating panel of playing playlist for scheduled audio files. 2021-07-01 23:53:45 +03:00
23rd
348cf4829c Added ability to scroll media in section of scheduled messages.
Fixed #8388.
2021-07-01 23:53:45 +03:00
23rd
4753a57091 Added ability to validate playlists in section of scheduled messages. 2021-07-01 23:53:45 +03:00
23rd
1a93f4fa4c Added ability to sparse id slices of scheduled media. 2021-07-01 23:53:45 +03:00
23rd
118fd187e3 Added abstract class for sparse ids slices. 2021-07-01 23:53:45 +03:00
23rd
baa47bde7f Removed unused MsgRange from SparseIdsSlice. 2021-07-01 23:53:45 +03:00
John Preston
18b48df9ce Allow to choose ANGLE backend. 2021-07-01 23:48:18 +03:00
John Preston
e71fc60d22 Use exact revision of ANGLE. 2021-07-01 23:48:16 +03:00
John Preston
148af59615 Don't check dll-s if "SetDefaultDllDirectories" is available. 2021-07-01 23:47:12 +03:00
John Preston
5b2db4112f Don't allow any .dll-s near Telegram.exe 2021-07-01 23:47:12 +03:00
John Preston
7cedc1f7a5 Add dynamic DirectX loading helper. 2021-07-01 23:47:08 +03:00
John Preston
6cea7d4a52 Fix YUV->RGB on D3D9 ANGLE backend. 2021-07-01 23:46:52 +03:00
John Preston
bd93aed393 Test build with statically linked ANGLE. 2021-07-01 23:46:52 +03:00
John Preston
348666de6d Use media viewer size hack only when required. 2021-07-01 23:46:52 +03:00
Ilya Fedin
47e32bebe4 Remove not really needed gtk scale factor query 2021-07-01 22:13:50 +03:00
Ilya Fedin
0b21c04489 Remove the copy of gtk file dialog 2021-07-01 22:13:50 +03:00
Ilya Fedin
85f013ebdb Revert "Avoid removing portal platformtheme plugin in snap"
This reverts commit 12db51fe75.
2021-07-01 22:13:50 +03:00
Ilya Fedin
832cc6ac69 Build Qt with gtk integration 2021-07-01 22:13:50 +03:00
Ilya Fedin
30ce049f51 Update submodules 2021-07-01 22:13:20 +03:00
Ilya Fedin
d42fb6d1b9 Switch from mallocng to jemalloc
Now it's known how to make it free the memory in an expected manner and it's better maintained
2021-07-01 22:13:20 +03:00
John Preston
cade53aa0a Version 2.8.4.
- Crash fixes in WebView on Windows.
2021-07-01 11:05:21 +03:00
GitHub Action
2fdcda7536 Update User-Agent for DNS to Chrome 91.0.4472.114. 2021-07-01 10:48:17 +03:00
Ilya Fedin
7e6439e4f8 Fix counting screen bottom point when restoring geometry 2021-06-30 00:27:39 +03:00
Ilya Fedin
f07ee7f590 Update lib_base and cmake_helpers 2021-06-29 17:35:39 +03:00
Ilya Fedin
02db4e01fa Get rid of qt5ct 2021-06-29 17:35:39 +03:00
Ilya Fedin
8d75078a42 Use Glib::MainLoop instead of QEventLoop in glib code 2021-06-29 15:10:08 +03:00
John Preston
0e25ef7524 Handle WinRT exceptions in WebView. 2021-06-29 14:08:50 +03:00
John Preston
8608d8aa4d Crash Fix: Destroy WebView before the container. 2021-06-29 11:07:47 +03:00
sammiee5311
c1a7332a5e Shorten if statement
Shorten if statement
2021-06-29 10:38:35 +03:00
Ilya Fedin
c3fb392906 Clean dbus-specific code in main_window_linux.h 2021-06-29 10:30:48 +03:00
Ilya Fedin
a59bfdb2f8 Fix handleNativeSurfaceChanged when dbus integration is disabled 2021-06-29 10:30:48 +03:00
Ilya Fedin
79f96480c2 Use QMenuBar instead of own global menu implementation on Linux 2021-06-29 10:30:48 +03:00
John Preston
60cbd96d91 Version 2.8.3.
- Fix crashes in OpenSSL on macOS.
2021-06-28 13:51:06 +03:00
73 changed files with 1682 additions and 1980 deletions

View File

@@ -93,6 +93,9 @@ jobs:
DEFINE=""
if [ -n "${{ matrix.defines }}" ]; then
DEFINE="-D ${{ matrix.defines }}=ON"
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" ]; then
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_GTK_INTEGRATION=ON"
fi
echo Define from matrix: $DEFINE
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
else

6
.gitmodules vendored
View File

@@ -76,9 +76,6 @@
[submodule "Telegram/ThirdParty/hime"]
path = Telegram/ThirdParty/hime
url = https://github.com/hime-ime/hime.git
[submodule "Telegram/ThirdParty/qt5ct"]
path = Telegram/ThirdParty/qt5ct
url = https://github.com/desktop-app/qt5ct.git
[submodule "Telegram/ThirdParty/fcitx5-qt"]
path = Telegram/ThirdParty/fcitx5-qt
url = https://github.com/fcitx/fcitx5-qt.git
@@ -91,9 +88,6 @@
[submodule "Telegram/lib_webview"]
path = Telegram/lib_webview
url = https://github.com/desktop-app/lib_webview.git
[submodule "Telegram/ThirdParty/mallocng"]
path = Telegram/ThirdParty/mallocng
url = https://github.com/desktop-app/mallocng.git
[submodule "Telegram/lib_waylandshells"]
path = Telegram/lib_waylandshells
url = https://github.com/desktop-app/lib_waylandshells.git

View File

@@ -87,14 +87,12 @@ if (LINUX)
PRIVATE
desktop-app::external_glibmm
desktop-app::external_glib
desktop-app::external_mallocng
)
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_statusnotifieritem
desktop-app::external_dbusmenu_qt
)
endif()
@@ -114,6 +112,7 @@ if (LINUX)
endif()
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
target_link_libraries(Telegram PRIVATE rt)
find_package(PkgConfig REQUIRED)
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
@@ -395,6 +394,7 @@ PRIVATE
data/stickers/data_stickers_set.h
data/stickers/data_stickers.cpp
data/stickers/data_stickers.h
data/data_abstract_sparse_ids.h
data/data_abstract_structure.cpp
data/data_abstract_structure.h
data/data_auto_download.cpp
@@ -863,8 +863,6 @@ PRIVATE
platform/linux/linux_gdk_helper.h
platform/linux/linux_gsd_media_keys.cpp
platform/linux/linux_gsd_media_keys.h
platform/linux/linux_gtk_file_dialog.cpp
platform/linux/linux_gtk_file_dialog.h
platform/linux/linux_gtk_integration_dummy.cpp
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
@@ -1196,8 +1194,6 @@ if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gdk_helper.cpp
platform/linux/linux_gdk_helper.h
platform/linux/linux_gtk_file_dialog.cpp
platform/linux/linux_gtk_file_dialog.h
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
platform/linux/linux_gtk_open_with_dialog.cpp
@@ -1362,6 +1358,32 @@ endif()
set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (WIN32)
target_link_options(Telegram
PRIVATE
/DELAYLOAD:secur32.dll
/DELAYLOAD:winmm.dll
/DELAYLOAD:ws2_32.dll
/DELAYLOAD:user32.dll
/DELAYLOAD:gdi32.dll
/DELAYLOAD:advapi32.dll
/DELAYLOAD:shell32.dll
/DELAYLOAD:ole32.dll
/DELAYLOAD:oleaut32.dll
/DELAYLOAD:shlwapi.dll
/DELAYLOAD:iphlpapi.dll
/DELAYLOAD:gdiplus.dll
/DELAYLOAD:version.dll
/DELAYLOAD:dwmapi.dll
/DELAYLOAD:crypt32.dll
/DELAYLOAD:bcrypt.dll
/DELAYLOAD:imm32.dll
/DELAYLOAD:netapi32.dll
/DELAYLOAD:userenv.dll
/DELAYLOAD:wtsapi32.dll
)
endif()
if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT build_winstore)
add_executable(Updater WIN32)
init_target(Updater)
@@ -1378,8 +1400,26 @@ if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT
set_target_properties(Updater PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (WIN32 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_link_options(Updater PRIVATE -municode)
if (WIN32)
get_filename_component(lib_base_loc lib_base REALPATH)
nice_target_sources(Updater ${lib_base_loc}
PRIVATE
base/platform/win/base_windows_safe_library.cpp
base/platform/win/base_windows_safe_library.h
)
target_include_directories(Updater PRIVATE ${lib_base_loc})
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_link_options(Updater
PRIVATE
/DELAYLOAD:user32.dll
/DELAYLOAD:advapi32.dll
/DELAYLOAD:shell32.dll
/DELAYLOAD:ole32.dll
/DELAYLOAD:shlwapi.dll
)
else()
target_link_options(Updater PRIVATE -municode)
endif()
endif()
if (LINUX)

View File

@@ -452,6 +452,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_performance" = "Performance";
"lng_settings_enable_animations" = "Enable animations";
"lng_settings_enable_opengl" = "Enable OpenGL rendering for media";
"lng_settings_angle_backend" = "ANGLE graphics backend";
"lng_settings_angle_backend_auto" = "Auto";
"lng_settings_angle_backend_d3d9" = "Direct3D 9";
"lng_settings_angle_backend_d3d11" = "Direct3D 11";
"lng_settings_angle_backend_d3d11on12" = "D3D11on12";
"lng_settings_angle_backend_opengl" = "OpenGL";
"lng_settings_angle_backend_disabled" = "Disabled";
"lng_settings_sensitive_title" = "Sensitive content";
"lng_settings_sensitive_disable_filtering" = "Disable filtering";
"lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="2.8.2.0" />
Version="2.8.5.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,0
FILEVERSION 2,8,5,0
PRODUCTVERSION 2,8,5,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "2.8.2.0"
VALUE "FileVersion", "2.8.5.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.8.2.0"
VALUE "ProductVersion", "2.8.5.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,0
FILEVERSION 2,8,5,0
PRODUCTVERSION 2,8,5,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "2.8.2.0"
VALUE "FileVersion", "2.8.5.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.8.2.0"
VALUE "ProductVersion", "2.8.5.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "updater.h"
#include "base/platform/win/base_windows_safe_library.h"
bool _debug = false;
wstring updaterName, updaterDir, updateTo, exeName, customWorkingDir, customKeyFile;
@@ -329,6 +331,8 @@ void updateRegistry() {
}
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdParamarg, int cmdShow) {
base::Platform::InitDynamicLibraries();
openLog();
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);

View File

@@ -223,6 +223,7 @@ void Panel::Incoming::RendererGL::paint(
Assert(data.format == Webrtc::FrameFormat::YUV420);
Assert(!data.yuv420->size.isEmpty());
const auto yuv = data.yuv420;
const auto format = Ui::GL::CurrentSingleComponentFormat();
f.glActiveTexture(GL_TEXTURE0);
_textures.bind(f, 1);
@@ -230,8 +231,8 @@ void Panel::Incoming::RendererGL::paint(
f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->size,
_lumaSize,
yuv->y.stride,
@@ -243,8 +244,8 @@ void Panel::Incoming::RendererGL::paint(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->u.stride,
@@ -255,8 +256,8 @@ void Panel::Incoming::RendererGL::paint(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->v.stride,

View File

@@ -988,6 +988,7 @@ void Viewport::RendererGL::bindFrame(
program.argb32->setUniformValue("s_texture", GLint(0));
} else {
const auto yuv = data.yuv420;
const auto format = Ui::GL::CurrentSingleComponentFormat();
program.yuv420->bind();
f.glActiveTexture(GL_TEXTURE0);
tileData.textures.bind(f, 0);
@@ -995,8 +996,8 @@ void Viewport::RendererGL::bindFrame(
f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->size,
tileData.textureSize,
yuv->y.stride,
@@ -1009,8 +1010,8 @@ void Viewport::RendererGL::bindFrame(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
tileData.textureChromaSize,
yuv->u.stride,
@@ -1021,8 +1022,8 @@ void Viewport::RendererGL::bindFrame(
if (upload) {
uploadTexture(
f,
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
tileData.textureChromaSize,
yuv->v.stride,
@@ -1368,16 +1369,17 @@ void Viewport::RendererGL::validateNoiseTexture(
if (_noiseTexture.created()) {
return;
}
const auto format = Ui::GL::CurrentSingleComponentFormat();
_noiseTexture.ensureCreated(f, GL_NEAREST, GL_REPEAT);
_noiseTexture.bind(f, 0);
f.glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
format,
kNoiseTextureSize,
kNoiseTextureSize,
0,
GL_RED,
format,
GL_UNSIGNED_BYTE,
nullptr);

View File

@@ -58,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_lock_widgets.h"
#include "history/history_location_manager.h"
#include "ui/widgets/tooltip.h"
#include "ui/gl/gl_detection.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
#include "ui/emoji_config.h"
@@ -281,8 +282,7 @@ void Application::run() {
LOG(("Shortcuts Error: %1").arg(error));
}
if (!Platform::IsMac()
&& Ui::Integration::Instance().openglLastCheckFailed()) {
if (!Platform::IsMac() && Ui::GL::LastCrashCheckFailed()) {
showOpenGLCrashNotification();
}
@@ -297,14 +297,14 @@ void Application::run() {
void Application::showOpenGLCrashNotification() {
const auto enable = [=] {
Ui::GL::ForceDisable(false);
Ui::Integration::Instance().openglCheckFinish();
Ui::GL::CrashCheckFinish();
Core::App().settings().setDisableOpenGL(false);
Local::writeSettings();
App::restart();
};
const auto keepDisabled = [=] {
Ui::GL::ForceDisable(true);
Ui::Integration::Instance().openglCheckFinish();
Ui::GL::CrashCheckFinish();
Core::App().settings().setDisableOpenGL(true);
Local::writeSettings();
};

View File

@@ -549,7 +549,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_disableOpenGL = (disableOpenGL == 1);
if (!Platform::IsMac()) {
Ui::GL::ForceDisable(_disableOpenGL
|| Ui::Integration::Instance().openglLastCheckFailed());
|| Ui::GL::LastCrashCheckFailed());
}
_groupCallNoiseSuppression = (groupCallNoiseSuppression == 1);
const auto uncheckedWorkMode = static_cast<WorkMode>(workMode);

View File

@@ -19,7 +19,7 @@ public:
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);
int exec();
virtual int exec();
QString argumentsString() const;
bool customWorkingDir() const;

View File

@@ -88,6 +88,10 @@ const auto kBadPrefix = u"http://"_q;
return cWorkingDir() + "tdata/opengl_crash_check";
}
[[nodiscard]] QString ANGLEBackendFilePath() {
return cWorkingDir() + "tdata/angle_backend";
}
} // namespace
void UiIntegration::postponeCall(FnMut<void()> &&callable) {
@@ -106,20 +110,12 @@ QString UiIntegration::emojiCacheFolder() {
return cWorkingDir() + "tdata/emoji";
}
void UiIntegration::openglCheckStart() {
auto f = QFile(OpenGLCheckFilePath());
if (f.open(QIODevice::WriteOnly)) {
f.write("1", 1);
f.close();
}
QString UiIntegration::openglCheckFilePath() {
return OpenGLCheckFilePath();
}
void UiIntegration::openglCheckFinish() {
QFile::remove(OpenGLCheckFilePath());
}
bool UiIntegration::openglLastCheckFailed() {
return OpenGLLastCheckFailed();
QString UiIntegration::angleBackendFilePath() {
return ANGLEBackendFilePath();
}
void UiIntegration::textActionsUpdated() {

View File

@@ -37,10 +37,8 @@ public:
void unregisterLeaveSubscription(not_null<QWidget*> widget) override;
QString emojiCacheFolder() override;
void openglCheckStart() override;
void openglCheckFinish() override;
bool openglLastCheckFailed() override;
QString openglCheckFilePath() override;
QString angleBackendFilePath() override;
void textActionsUpdated() override;
void activationFromTopPanel() override;

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 2008002;
constexpr auto AppVersionStr = "2.8.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppVersion = 2008005;
constexpr auto AppVersionStr = "2.8.5";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -0,0 +1,78 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
template <typename IdsContainer>
class AbstractSparseIds {
public:
using Id = typename IdsContainer::value_type;
AbstractSparseIds() = default;
AbstractSparseIds(
const IdsContainer &ids,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _ids(ids)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
std::optional<int> fullCount() const {
return _fullCount;
}
std::optional<int> skippedBefore() const {
return _skippedBefore;
}
std::optional<int> skippedAfter() const {
return _skippedAfter;
}
std::optional<int> indexOf(Id id) const {
const auto it = ranges::find(_ids, id);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
int size() const {
return _ids.size();
}
Id operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> distance(Id a, Id b) const {
if (const auto i = indexOf(a)) {
if (const auto j = indexOf(b)) {
return *j - *i;
}
}
return std::nullopt;
}
std::optional<Id> nearest(Id id) const {
if (const auto it = ranges::lower_bound(_ids, id); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return std::nullopt;
}
return _ids.back();
}
void reverse() {
ranges::reverse(_ids);
std::swap(_skippedBefore, _skippedAfter);
}
private:
IdsContainer _ids;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
};

View File

@@ -16,8 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "history/history.h"
#include "history/history_item.h"
#include "data/data_document.h"
#include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_scheduled_messages.h"
#include "data/data_sparse_ids.h"
#include "data/data_session.h"
#include "info/info_memento.h"
@@ -30,6 +32,51 @@ namespace {
using Type = Storage::SharedMediaType;
bool IsItemGoodForType(const not_null<HistoryItem*> item, Type type) {
const auto media = item->media();
if (!media || media->webpage()) {
return false;
}
const auto photo = media->photo();
const auto photoType = (type == Type::Photo);
const auto photoVideoType = (type == Type::PhotoVideo);
if ((photoType || photoVideoType) && photo) {
return true;
}
const auto document = media->document();
if (!document) {
return false;
}
const auto voiceType = (type == Type::VoiceFile);
const auto voiceDoc = document->isVoiceMessage();
const auto roundType = (type == Type::RoundFile);
const auto roundDoc = document->isVideoMessage();
const auto audioType = (type == Type::MusicFile);
const auto audioDoc = document->isAudioFile();
const auto gifType = (type == Type::GIF);
const auto gifDoc = document->isGifv();
const auto videoType = (type == Type::Video);
const auto videoDoc = document->isVideoFile();
const auto voiceRoundType = (type == Type::RoundVoiceFile);
const auto fileType = (type == Type::File);
return (audioType && audioDoc)
|| (voiceType && voiceDoc)
|| (roundType && roundDoc)
|| (voiceRoundType && (roundDoc || voiceDoc))
|| (gifType && gifDoc)
|| ((videoType || photoVideoType) && videoDoc)
|| (fileType && (document->isTheme()
|| document->isImage()
|| !document->canBeStreamed()));
}
} // namespace
std::optional<Storage::SharedMediaType> SharedMediaOverviewType(
@@ -153,6 +200,55 @@ rpl::producer<SparseIdsSlice> SharedMediaViewer(
};
}
rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
int limitBefore,
int limitAfter) {
Expects(!IsServerMsgId(key.mergedKey.universalId));
Expects((key.mergedKey.universalId != 0)
|| (limitBefore == 0 && limitAfter == 0));
const auto history = session->data().history(key.mergedKey.peerId);
return rpl::single(
rpl::empty_value()
) | rpl::then(
session->data().scheduledMessages().updates(history)
) | rpl::map([=] {
const auto list = session->data().scheduledMessages().list(history);
auto items = ranges::views::all(
list.ids
) | ranges::views::transform([=](const FullMsgId &fullId) {
return session->data().message(fullId);
}) | ranges::views::filter([=](HistoryItem *item) {
return item
? IsItemGoodForType(item, key.type)
: false;
}) | ranges::to_vector;
ranges::sort(items, ranges::less(), &HistoryItem::position);
auto finishMsgIds = ranges::views::all(
items
) | ranges::views::transform([=](not_null<HistoryItem*> item) {
return item->fullId().msg;
}) | ranges::to_vector;
const auto fullCount = finishMsgIds.size();
auto unsorted = SparseUnsortedIdsSlice(
std::move(finishMsgIds),
fullCount,
list.skippedBefore,
list.skippedAfter);
return SparseIdsMergedSlice(
key.mergedKey,
std::move(unsorted));
});
}
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
@@ -374,12 +470,29 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
int limitBefore,
int limitAfter) {
return [=](auto consumer) {
auto viewerKey = SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type);
if (std::get_if<not_null<PhotoData*>>(&key.universalId)) {
return SharedMediaMergedViewer(
session,
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
std::move(viewerKey),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
consumer.put_next(SharedMediaWithLastSlice(
session,
key,
std::move(update),
std::nullopt));
});
}
if (key.scheduled) {
return SharedScheduledMediaViewer(
session,
std::move(viewerKey),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
@@ -393,9 +506,7 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
return rpl::combine(
SharedMediaMergedViewer(
session,
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
std::move(viewerKey),
limitBefore,
limitAfter),
SharedMediaMergedViewer(

View File

@@ -50,6 +50,12 @@ struct SharedMediaMergedKey {
};
rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
int limitBefore,
int limitAfter);
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
not_null<Main::Session*> session,
SharedMediaMergedKey key,
@@ -71,11 +77,13 @@ public:
PeerId peerId,
PeerId migratedPeerId,
Type type,
UniversalMsgId universalId)
UniversalMsgId universalId,
bool scheduled = false)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, type(type)
, universalId(universalId) {
, universalId(universalId)
, scheduled(scheduled) {
Expects(v::is<MessageId>(universalId) || type == Type::ChatPhoto);
}
@@ -93,6 +101,7 @@ public:
PeerId migratedPeerId = 0;
Type type = Type::kCount;
UniversalMsgId universalId;
bool scheduled = false;
};

View File

@@ -10,53 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/combine.h>
#include "storage/storage_sparse_ids_list.h"
SparseIdsSlice::SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _ids(ids)
, _range(range)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
std::optional<int> SparseIdsSlice::indexOf(MsgId msgId) const {
auto it = _ids.find(msgId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
MsgId SparseIdsSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> SparseIdsSlice::distance(
MsgId a,
MsgId b) const {
if (auto i = indexOf(a)) {
if (auto j = indexOf(b)) {
return *j - *i;
}
}
return std::nullopt;
}
std::optional<MsgId> SparseIdsSlice::nearest(MsgId msgId) const {
if (auto it = ranges::lower_bound(_ids, msgId); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return std::nullopt;
}
return _ids.back();
}
SparseIdsMergedSlice::SparseIdsMergedSlice(Key key)
: SparseIdsMergedSlice(
key,
@@ -73,33 +26,48 @@ SparseIdsMergedSlice::SparseIdsMergedSlice(
, _migrated(std::move(migrated)) {
}
SparseIdsMergedSlice::SparseIdsMergedSlice(
Key key,
SparseUnsortedIdsSlice scheduled)
: _key(key)
, _scheduled(std::move(scheduled)) {
}
std::optional<int> SparseIdsMergedSlice::fullCount() const {
return Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
return _scheduled
? _scheduled->fullCount()
: Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
}
std::optional<int> SparseIdsMergedSlice::skippedBefore() const {
return Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
return _scheduled
? _scheduled->skippedBefore()
: Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
}
std::optional<int> SparseIdsMergedSlice::skippedAfter() const {
return Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
return _scheduled
? _scheduled->skippedAfter()
: Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
}
std::optional<int> SparseIdsMergedSlice::indexOf(
FullMsgId fullId) const {
return isFromPart(fullId)
return _scheduled
? _scheduled->indexOf(fullId.msg)
: isFromPart(fullId)
? (_part.indexOf(fullId.msg) | func::add(migratedSize()))
: isolatedInPart()
? std::nullopt
@@ -109,14 +77,20 @@ std::optional<int> SparseIdsMergedSlice::indexOf(
}
int SparseIdsMergedSlice::size() const {
return (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
return _scheduled
? _scheduled->size()
: (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
}
FullMsgId SparseIdsMergedSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
if (auto size = migratedSize()) {
if (_scheduled) {
return ComputeId(_key.peerId, (*_scheduled)[index]);
}
if (const auto size = migratedSize()) {
if (index < size) {
return ComputeId(_key.migratedPeerId, (*_migrated)[index]);
}
@@ -128,8 +102,8 @@ FullMsgId SparseIdsMergedSlice::operator[](int index) const {
std::optional<int> SparseIdsMergedSlice::distance(
const Key &a,
const Key &b) const {
if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) {
if (const auto i = indexOf(ComputeId(a))) {
if (const auto j = indexOf(ComputeId(b))) {
return *j - *i;
}
}
@@ -138,10 +112,15 @@ std::optional<int> SparseIdsMergedSlice::distance(
auto SparseIdsMergedSlice::nearest(
UniversalMsgId id) const -> std::optional<FullMsgId> {
auto convertFromPartNearest = [&](MsgId result) {
if (_scheduled) {
if (const auto nearestId = _scheduled->nearest(id)) {
return ComputeId(_key.peerId, *nearestId);
}
}
const auto convertFromPartNearest = [&](MsgId result) {
return ComputeId(_key.peerId, result);
};
auto convertFromMigratedNearest = [&](MsgId result) {
const auto convertFromMigratedNearest = [&](MsgId result) {
return ComputeId(_key.migratedPeerId, result);
};
if (IsServerMsgId(id)) {
@@ -245,7 +224,6 @@ bool SparseIdsSliceBuilder::removeOne(MsgId messageId) {
bool SparseIdsSliceBuilder::removeAll() {
_ids = {};
_range = { 0, ServerMaxMsgId };
_fullCount = 0;
_skippedBefore = 0;
_skippedAfter = 0;
@@ -254,9 +232,6 @@ bool SparseIdsSliceBuilder::removeAll() {
bool SparseIdsSliceBuilder::invalidateBottom() {
_fullCount = _skippedAfter = std::nullopt;
if (_range.till == ServerMaxMsgId) {
_range.till = _ids.empty() ? _range.from : _ids.back();
}
checkInsufficient();
return true;
}
@@ -389,7 +364,6 @@ void SparseIdsSliceBuilder::requestMessagesCount() {
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
return SparseIdsSlice(
_ids,
_range,
_fullCount,
_skippedBefore,
_skippedAfter);
@@ -440,4 +414,4 @@ rpl::producer<SparseIdsMergedSlice> SparseIdsMergedSlice::CreateViewer(
std::move(migrated)));
});
};
}
}

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_abstract_sparse_ids.h"
#include "data/data_messages.h"
namespace Storage {
@@ -14,36 +15,15 @@ struct SparseIdsListResult;
struct SparseIdsSliceUpdate;
} // namespace Storage
class SparseIdsSlice {
class SparseIdsSlice final : public AbstractSparseIds<base::flat_set<MsgId>> {
public:
using Key = MsgId;
SparseIdsSlice() = default;
SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter);
std::optional<int> fullCount() const { return _fullCount; }
std::optional<int> skippedBefore() const { return _skippedBefore; }
std::optional<int> skippedAfter() const { return _skippedAfter; }
std::optional<int> indexOf(MsgId msgId) const;
int size() const { return _ids.size(); }
MsgId operator[](int index) const;
std::optional<int> distance(MsgId a, MsgId b) const;
std::optional<MsgId> nearest(MsgId msgId) const;
private:
base::flat_set<MsgId> _ids;
MsgRange _range;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
using AbstractSparseIds<base::flat_set<MsgId>>::AbstractSparseIds;
};
using SparseUnsortedIdsSlice = AbstractSparseIds<std::vector<MsgId>>;
class SparseIdsMergedSlice {
public:
using UniversalMsgId = MsgId;
@@ -51,9 +31,11 @@ public:
Key(
PeerId peerId,
PeerId migratedPeerId,
UniversalMsgId universalId)
UniversalMsgId universalId,
bool scheduled = false)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, scheduled(scheduled)
, migratedPeerId(scheduled ? 0 : migratedPeerId)
, universalId(universalId) {
}
@@ -67,6 +49,7 @@ public:
}
PeerId peerId = 0;
bool scheduled = false;
PeerId migratedPeerId = 0;
UniversalMsgId universalId = 0;
@@ -77,6 +60,9 @@ public:
Key key,
SparseIdsSlice part,
std::optional<SparseIdsSlice> migrated);
SparseIdsMergedSlice(
Key key,
SparseUnsortedIdsSlice scheduled);
std::optional<int> fullCount() const;
std::optional<int> skippedBefore() const;
@@ -155,6 +141,7 @@ private:
Key _key;
SparseIdsSlice _part;
std::optional<SparseIdsSlice> _migrated;
std::optional<SparseUnsortedIdsSlice> _scheduled;
};
@@ -205,7 +192,6 @@ private:
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;

View File

@@ -64,39 +64,23 @@ UserPhotosSlice::UserPhotosSlice(
std::optional<int> fullCount,
std::optional<int> skippedBefore,
std::optional<int> skippedAfter)
: _key(key)
, _ids(std::move(ids))
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
: AbstractSparseIds<std::deque<PhotoId>>(
ids,
fullCount,
skippedBefore,
skippedAfter)
, _key(key) {
}
void UserPhotosSlice::reverse() {
ranges::reverse(_ids);
std::swap(_skippedBefore, _skippedAfter);
}
std::optional<int> UserPhotosSlice::indexOf(PhotoId photoId) const {
auto it = ranges::find(_ids, photoId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return std::nullopt;
}
PhotoId UserPhotosSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
std::optional<int> UserPhotosSlice::distance(const Key &a, const Key &b) const {
std::optional<int> UserPhotosSlice::distance(
const Key &a,
const Key &b) const {
if (a.userId != _key.userId
|| b.userId != _key.userId) {
return std::nullopt;
}
if (auto i = indexOf(a.photoId)) {
if (auto j = indexOf(b.photoId)) {
if (const auto i = indexOf(a.photoId)) {
if (const auto j = indexOf(b.photoId)) {
return *j - *i;
}
}
@@ -248,4 +232,4 @@ rpl::producer<UserPhotosSlice> UserPhotosReversedViewer(
slice.reverse();
return std::move(slice);
});
}
}

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_abstract_sparse_ids.h"
#include "storage/storage_user_photos.h"
#include "base/weak_ptr.h"
@@ -14,7 +15,7 @@ namespace Main {
class Session;
} // namespace Main
class UserPhotosSlice {
class UserPhotosSlice final : public AbstractSparseIds<std::deque<PhotoId>> {
public:
using Key = Storage::UserPhotosKey;
@@ -26,24 +27,11 @@ public:
std::optional<int> skippedBefore,
std::optional<int> skippedAfter);
void reverse();
const Key &key() const { return _key; }
std::optional<int> fullCount() const { return _fullCount; }
std::optional<int> skippedBefore() const { return _skippedBefore; }
std::optional<int> skippedAfter() const { return _skippedAfter; }
std::optional<int> indexOf(PhotoId msgId) const;
int size() const { return _ids.size(); }
PhotoId operator[](int index) const;
std::optional<int> distance(const Key &a, const Key &b) const;
const Key &key() const { return _key; }
private:
Key _key;
std::deque<PhotoId> _ids;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
friend class UserPhotosSliceBuilder;

View File

@@ -70,7 +70,22 @@ rpl::producer<SparseIdsMergedSlice> AbstractController::mediaSource(
int limitAfter) const {
Expects(peer() != nullptr);
return SharedMediaMergedViewer(
const auto isScheduled = [&] {
if (IsServerMsgId(aroundId)) {
return false;
}
const auto channelId = peerToChannel(peer()->id);
if (const auto item = session().data().message(channelId, aroundId)) {
return item->isScheduled();
}
return false;
}();
auto mediaViewer = isScheduled
? SharedScheduledMediaViewer
: SharedMediaMergedViewer;
return mediaViewer(
&session(),
SharedMediaMergedKey(
SparseIdsMergedSlice::Key(

View File

@@ -913,7 +913,7 @@ SparseIdsMergedSlice::Key ListWidget::sliceKey(
void ListWidget::refreshViewer() {
_viewerLifetime.destroy();
auto idForViewer = sliceKey(_universalAroundId).universalId;
const auto idForViewer = sliceKey(_universalAroundId).universalId;
_controller->mediaSource(
idForViewer,
_idsLimit,

View File

@@ -269,7 +269,8 @@ bool Instance::validPlaylist(not_null<Data*> data) {
using Key = SliceKey;
const auto inSameDomain = [](const Key &a, const Key &b) {
return (a.peerId == b.peerId)
&& (a.migratedPeerId == b.migratedPeerId);
&& (a.migratedPeerId == b.migratedPeerId)
&& (a.scheduled == b.scheduled);
};
const auto countDistanceInData = [&](const Key &a, const Key &b) {
return [&](const SparseIdsMergedSlice &data) {
@@ -300,7 +301,11 @@ void Instance::validatePlaylist(not_null<Data*> data) {
data->playlistLifetime.destroy();
if (const auto key = playlistKey(data)) {
data->playlistRequestedKey = key;
SharedMediaMergedViewer(
const auto sharedMediaViewer = key->scheduled
? SharedScheduledMediaViewer
: SharedMediaMergedViewer;
sharedMediaViewer(
&data->history->session(),
SharedMediaMergedKey(*key, data->overview),
kIdsLimit,
@@ -321,7 +326,11 @@ auto Instance::playlistKey(not_null<Data*> data) const
-> std::optional<SliceKey> {
const auto contextId = data->current.contextId();
const auto history = data->history;
if (!contextId || !history || !IsServerMsgId(contextId.msg)) {
if (!contextId || !history) {
return {};
}
const auto item = data->history->owner().message(contextId);
if (!item || (!IsServerMsgId(contextId.msg) && !item->isScheduled())) {
return {};
}
@@ -331,7 +340,8 @@ auto Instance::playlistKey(not_null<Data*> data) const
return SliceKey(
data->history->peer->id,
data->migrated ? data->migrated->peer->id : 0,
universalId);
universalId,
item->isScheduled());
}
HistoryItem *Instance::itemByIndex(not_null<Data*> data, int index) {

View File

@@ -244,7 +244,7 @@ void Panel::refreshList() {
const auto document = media ? media->document() : nullptr;
if (!document
|| !document->isSharedMediaMusic()
|| !IsServerMsgId(item->id)) {
|| (!IsServerMsgId(item->id) && !item->isScheduled())) {
return nullptr;
}
const auto result = item->history()->peer;

View File

@@ -193,6 +193,7 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
const auto upload = (_trackFrameIndex != data.index)
|| (_streamedIndex != _owner->streamedIndex());
const auto format = Ui::GL::CurrentSingleComponentFormat();
_trackFrameIndex = data.index;
_streamedIndex = _owner->streamedIndex();
@@ -201,8 +202,8 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
if (upload) {
_f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->size,
_lumaSize,
yuv->y.stride,
@@ -213,8 +214,8 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
_textures.bind(*_f, 2);
if (upload) {
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->u.stride,
@@ -224,8 +225,8 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
_textures.bind(*_f, 3);
if (upload) {
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->v.stride,

View File

@@ -508,8 +508,16 @@ void OverlayWidget::updateGeometry() {
? window()->screen()
: QApplication::primaryScreen();
const auto available = screen->geometry();
const auto useSizeHack = _opengl && Platform::IsWindows();
const auto use = available.marginsAdded({ 0, 0, 0, 1 });
const auto openglWidget = _opengl
? static_cast<QOpenGLWidget*>(_widget.get())
: nullptr;
const auto useSizeHack = Platform::IsWindows()
&& openglWidget
&& (openglWidget->format().renderableType()
!= QSurfaceFormat::OpenGLES);
const auto use = useSizeHack
? available.marginsAdded({ 0, 0, 0, 1 })
: available;
const auto mask = useSizeHack
? QRegion(QRect(QPoint(), available.size()))
: QRegion();
@@ -1814,19 +1822,31 @@ auto OverlayWidget::sharedMediaKey() const -> std::optional<SharedMediaKey> {
_photo
};
}
if (!IsServerMsgId(_msgid.msg)) {
return std::nullopt;
}
auto keyForType = [this](SharedMediaType type) -> SharedMediaKey {
const auto isServerMsgId = IsServerMsgId(_msgid.msg);
const auto isScheduled = [&] {
if (isServerMsgId) {
return false;
}
if (const auto item = _session->data().message(_msgid)) {
return item->isScheduled();
}
return false;
}();
const auto keyForType = [&](SharedMediaType type) -> SharedMediaKey {
return {
_history->peer->id,
_migrated ? _migrated->peer->id : 0,
type,
(_msgid.channel == _history->channelId()) ? _msgid.msg : (_msgid.msg - ServerMaxMsgId) };
(_msgid.channel == _history->channelId())
? _msgid.msg
: (_msgid.msg - ServerMaxMsgId),
isScheduled
};
};
return
sharedMediaType()
| keyForType;
if (!isServerMsgId && !isScheduled) {
return std::nullopt;
}
return sharedMediaType() | keyForType;
}
Data::FileOrigin OverlayWidget::fileOrigin() const {
@@ -1864,7 +1884,8 @@ bool OverlayWidget::validSharedMedia() const {
auto inSameDomain = [](const Key &a, const Key &b) {
return (a.type == b.type)
&& (a.peerId == b.peerId)
&& (a.migratedPeerId == b.migratedPeerId);
&& (a.migratedPeerId == b.migratedPeerId)
&& (a.scheduled == b.scheduled);
};
auto countDistanceInData = [&](const Key &a, const Key &b) {
return [&](const SharedMediaWithLastSlice &data) {

View File

@@ -297,13 +297,14 @@ void Pip::RendererGL::paintTransformedVideoFrame(
const auto upload = (_trackFrameIndex != data.index);
_trackFrameIndex = data.index;
const auto format = Ui::GL::CurrentSingleComponentFormat();
_f->glActiveTexture(GL_TEXTURE0);
_textures.bind(*_f, 1);
if (upload) {
_f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->size,
_lumaSize,
yuv->y.stride,
@@ -314,8 +315,8 @@ void Pip::RendererGL::paintTransformedVideoFrame(
_textures.bind(*_f, 2);
if (upload) {
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->u.stride,
@@ -325,8 +326,8 @@ void Pip::RendererGL::paintTransformedVideoFrame(
_textures.bind(*_f, 3);
if (upload) {
uploadTexture(
GL_RED,
GL_RED,
format,
format,
yuv->chromaSize,
_chromaSize,
yuv->v.stride,

View File

@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
static const auto kResult = QByteArray(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/90.0.4430.85 Safari/537.36");
"Chrome/91.0.4472.114 Safari/537.36");
return kResult;
}

View File

@@ -43,6 +43,21 @@ struct Panel::Progress {
rpl::lifetime geometryLifetime;
};
struct Panel::WebviewWithLifetime {
WebviewWithLifetime(
QWidget *parent = nullptr,
Webview::WindowConfig config = Webview::WindowConfig());
Webview::Window window;
rpl::lifetime lifetime;
};
Panel::WebviewWithLifetime::WebviewWithLifetime(
QWidget *parent,
Webview::WindowConfig config)
: window(parent, std::move(config)) {
}
Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
: widget(parent)
, animation(
@@ -68,7 +83,7 @@ Panel::Panel(not_null<PanelDelegate*> delegate)
}
Panel::~Panel() {
// Destroy _widget before _webview.
_webview = nullptr;
_progress = nullptr;
_widget = nullptr;
}
@@ -451,7 +466,7 @@ bool Panel::showWebview(
}
showWebviewProgress();
_widget->destroyLayer();
_webview->navigate(url);
_webview->window.navigate(url);
_widget->setBackAllowed(allowBack);
if (bottomText) {
const auto &padding = st::paymentsPanelPadding;
@@ -490,14 +505,14 @@ bool Panel::createWebview() {
}, bottom->lifetime());
container->show();
_webview = std::make_unique<Webview::Window>(
_webview = std::make_unique<WebviewWithLifetime>(
container.get(),
Webview::WindowConfig{
.userDataPath = _delegate->panelWebviewDataPath(),
});
const auto raw = _webview.get();
const auto raw = &_webview->window;
QObject::connect(container.get(), &QObject::destroyed, [=] {
if (_webview.get() == raw) {
if (_webview && &_webview->window == raw) {
_webview = nullptr;
if (_webviewProgress) {
hideWebviewProgress();
@@ -517,7 +532,7 @@ bool Panel::createWebview() {
container->geometryValue(
) | rpl::start_with_next([=](QRect geometry) {
raw->widget()->setGeometry(geometry);
}, container->lifetime());
}, _webview->lifetime);
raw->setMessageHandler([=](const QJsonDocument &message) {
const auto save = _saveWebviewInformation

View File

@@ -17,7 +17,6 @@ class Checkbox;
} // namespace Ui
namespace Webview {
class Window;
struct Available;
} // namespace Webview
@@ -88,6 +87,7 @@ public:
private:
struct Progress;
struct WebviewWithLifetime;
bool createWebview();
void showWebviewProgress();
@@ -103,7 +103,7 @@ private:
const not_null<PanelDelegate*> _delegate;
std::unique_ptr<SeparatePanel> _widget;
std::unique_ptr<Webview::Window> _webview;
std::unique_ptr<WebviewWithLifetime> _webview;
std::unique_ptr<RpWidget> _webviewBottom;
std::unique_ptr<Progress> _progress;
QPointer<Checkbox> _saveWebviewInformation;

View File

@@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_gtk_integration.h"
#include "platform/linux/specific_linux.h"
#include "storage/localstorage.h"
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include "platform/linux/linux_xdp_file_dialog.h"
@@ -18,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QProcess>
#include <QtGui/QDesktopServices>
#include <QtWidgets/QFileDialog>
#include <glibmm.h>
#include <giomm.h>
@@ -91,73 +89,6 @@ void UnsafeLaunch(const QString &filepath) {
} // namespace File
namespace FileDialog {
namespace {
using Type = ::FileDialog::internal::Type;
bool GetQt(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
Type type,
QString startFile) {
if (cDialogLastPath().isEmpty()) {
InitLastPath();
}
QFileDialog dialog(parent, caption, QString(), filter);
dialog.setOptions(QFileDialog::DontUseNativeDialog);
dialog.setModal(true);
if (type == Type::ReadFile || type == Type::ReadFiles) {
dialog.setFileMode((type == Type::ReadFiles)
? QFileDialog::ExistingFiles
: QFileDialog::ExistingFile);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
} else if (type == Type::ReadFolder) {
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setFileMode(QFileDialog::Directory);
dialog.setOption(QFileDialog::ShowDirsOnly);
} else {
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setAcceptMode(QFileDialog::AcceptSave);
}
if (startFile.isEmpty() || startFile.at(0) != '/') {
startFile = cDialogLastPath() + '/' + startFile;
}
dialog.setDirectory(QFileInfo(startFile).absoluteDir().absolutePath());
if (type == Type::WriteFile) {
dialog.selectFile(startFile);
}
const auto res = dialog.exec();
if (type != Type::ReadFolder) {
// Save last used directory for all queries except directory choosing.
const auto path = dialog.directory().absolutePath();
if (!path.isEmpty() && path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeSettings();
}
}
if (res == QDialog::Accepted) {
if (type == Type::ReadFiles) {
files = dialog.selectedFiles();
} else {
files = dialog.selectedFiles().mid(0, 1);
}
return true;
}
files = QStringList();
remoteContent = QByteArray();
return false;
}
}
bool Get(
QPointer<QWidget> parent,
@@ -186,32 +117,6 @@ bool Get(
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (const auto integration = GtkIntegration::Instance()) {
const auto result = integration->getFileDialog(
parent,
files,
remoteContent,
caption,
filter,
type,
startFile);
if (result.has_value()) {
return *result;
}
}
// avoid situation when portals don't work
// and Qt tries to use portals as well
if (InFlatpak() || InSnap()) {
return GetQt(
parent,
files,
remoteContent,
caption,
filter,
type,
startFile);
}
return ::FileDialog::internal::GetDefault(
parent,
files,

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/crash_reports.h"
#include "core/update_checker.h"
#include "platform/linux/linux_gtk_integration.h"
#include <QtWidgets/QApplication>
@@ -23,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace {
using Platform::internal::GtkIntegration;
class Arguments {
public:
void push(QByteArray argument) {
@@ -45,7 +48,32 @@ private:
} // namespace
Launcher::Launcher(int argc, char *argv[])
: Core::Launcher(argc, argv) {
: Core::Launcher(argc, argv)
, _arguments(argv, argv + argc) {
}
int Launcher::exec() {
for (auto i = begin(_arguments), e = end(_arguments); i != e; ++i) {
if (*i == "-basegtkintegration" && std::distance(i, e) > 2) {
return GtkIntegration::Exec(
GtkIntegration::Type::Base,
QString::fromStdString(*(i + 1)),
std::stoi(*(i + 2)));
} else if (*i == "-webviewhelper" && std::distance(i, e) > 3) {
return GtkIntegration::Exec(
GtkIntegration::Type::Webview,
QString::fromStdString(*(i + 1)),
std::stoi(*(i + 2)),
std::stoi(*(i + 3)));
} else if (*i == "-gtkintegration" && std::distance(i, e) > 2) {
return GtkIntegration::Exec(
GtkIntegration::Type::TDesktop,
QString::fromStdString(*(i + 1)),
std::stoi(*(i + 2)));
}
}
return Core::Launcher::exec();
}
void Launcher::initHook() {

View File

@@ -15,10 +15,14 @@ class Launcher : public Core::Launcher {
public:
Launcher(int argc, char *argv[]);
int exec();
private:
void initHook() override;
bool launchUpdater(UpdaterLaunch action) override;
std::vector<std::string> _arguments;
};
} // namespace Platform

View File

@@ -10,9 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/linux/base_linux_gtk_integration.h"
#include "base/platform/linux/base_linux_gtk_integration_p.h"
#include "platform/linux/linux_gtk_integration_p.h"
#include "platform/linux/linux_wayland_integration.h"
#include <QtGui/QWindow>
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
extern "C" {
@@ -98,21 +95,17 @@ void GdkHelperLoad(QLibrary &lib) {
}
}
void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
void GdkSetTransientFor(GdkWindow *window, const QString &parent) {
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
if (gdk_wayland_window_get_type != nullptr
&& gdk_wayland_window_set_transient_for_exported != nullptr
&& GDK_IS_WAYLAND_WINDOW(window)) {
if (const auto integration = WaylandIntegration::Instance()) {
if (const auto handle = integration->nativeHandle(parent)
; !handle.isEmpty()) {
auto handleUtf8 = handle.toUtf8();
gdk_wayland_window_set_transient_for_exported(
window,
handleUtf8.data());
return;
}
}
&& GDK_IS_WAYLAND_WINDOW(window)
&& parent.startsWith("wayland:")) {
auto handle = parent.mid(8).toUtf8();
gdk_wayland_window_set_transient_for_exported(
window,
handle.data());
return;
}
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
@@ -121,23 +114,33 @@ void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
&& gdk_x11_display_get_xdisplay != nullptr
&& gdk_x11_window_get_xid != nullptr
&& gdk_window_get_display != nullptr
&& GDK_IS_X11_WINDOW(window)) {
XSetTransientForHint(
gdk_x11_display_get_xdisplay(gdk_window_get_display(window)),
gdk_x11_window_get_xid(window),
parent->winId());
return;
&& GDK_IS_X11_WINDOW(window)
&& parent.startsWith("x11:")) {
auto ok = false;
const auto winId = parent.mid(4).toInt(&ok, 16);
if (ok) {
XSetTransientForHint(
gdk_x11_display_get_xdisplay(gdk_window_get_display(window)),
gdk_x11_window_get_xid(window),
winId);
return;
}
}
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
if (gdk_x11_drawable_get_xdisplay != nullptr
&& gdk_x11_drawable_get_xid != nullptr) {
XSetTransientForHint(
gdk_x11_drawable_get_xdisplay(window),
gdk_x11_drawable_get_xid(window),
parent->winId());
return;
&& gdk_x11_drawable_get_xid != nullptr
&& parent.startsWith("x11:")) {
auto ok = false;
const auto winId = parent.mid(4).toInt(&ok, 16);
if (ok) {
XSetTransientForHint(
gdk_x11_drawable_get_xdisplay(window),
gdk_x11_drawable_get_xid(window),
winId);
return;
}
}
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
}

View File

@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
class QLibrary;
class QWindow;
extern "C" {
#include <gtk/gtk.h>
@@ -19,7 +18,7 @@ namespace Platform {
namespace internal {
void GdkHelperLoad(QLibrary &lib);
void GdkSetTransientFor(GdkWindow *window, QWindow *parent);
void GdkSetTransientFor(GdkWindow *window, const QString &parent);
} // namespace internal
} // namespace Platform

View File

@@ -1,708 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/linux_gtk_file_dialog.h"
#include "platform/platform_file_utilities.h"
#include "platform/linux/linux_gtk_integration_p.h"
#include "platform/linux/linux_gdk_helper.h"
#include "platform/linux/linux_desktop_environment.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "base/qt_adapters.h"
#include <QtGui/QWindow>
#include <QtWidgets/QFileDialog>
#include <private/qguiapplication_p.h>
namespace Platform {
namespace FileDialog {
namespace Gtk {
namespace {
using namespace Platform::Gtk;
using Type = ::FileDialog::internal::Type;
// GTK file chooser image preview: thanks to Chromium
// The size of the preview we display for selected image files. We set height
// larger than width because generally there is more free space vertically
// than horiztonally (setting the preview image will alway expand the width of
// the dialog, but usually not the height). The image's aspect ratio will always
// be preserved.
constexpr auto kPreviewWidth = 256;
constexpr auto kPreviewHeight = 512;
const char *filterRegExp =
"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
QStringList makeFilterList(const QString &filter) {
QString f(filter);
if (f.isEmpty())
return QStringList();
QString sep(QLatin1String(";;"));
int i = f.indexOf(sep, 0);
if (i == -1) {
if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
sep = QLatin1Char('\n');
i = f.indexOf(sep, 0);
}
}
return f.split(sep);
}
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
QStringList cleanFilterList(const QString &filter) {
QRegExp regexp(QString::fromLatin1(filterRegExp));
Assert(regexp.isValid());
QString f = filter;
int i = regexp.indexIn(f);
if (i >= 0)
f = regexp.cap(2);
return f.split(QLatin1Char(' '), base::QStringSkipEmptyParts);
}
bool Supported() {
return (gtk_widget_hide_on_delete != nullptr)
&& (gtk_clipboard_store != nullptr)
&& (gtk_clipboard_get != nullptr)
&& (gtk_widget_destroy != nullptr)
&& (gtk_dialog_get_type != nullptr)
&& (gtk_dialog_run != nullptr)
&& (gtk_widget_realize != nullptr)
&& (gdk_window_set_modal_hint != nullptr)
&& (gtk_widget_show != nullptr)
&& (gdk_window_focus != nullptr)
&& (gtk_widget_hide != nullptr)
&& (gtk_widget_hide_on_delete != nullptr)
&& (gtk_file_chooser_dialog_new != nullptr)
&& (gtk_file_chooser_get_type != nullptr)
&& (gtk_file_chooser_set_current_folder != nullptr)
&& (gtk_file_chooser_get_current_folder != nullptr)
&& (gtk_file_chooser_set_current_name != nullptr)
&& (gtk_file_chooser_select_filename != nullptr)
&& (gtk_file_chooser_get_filenames != nullptr)
&& (gtk_file_chooser_set_filter != nullptr)
&& (gtk_file_chooser_get_filter != nullptr)
&& (gtk_window_get_type != nullptr)
&& (gtk_window_set_title != nullptr)
&& (gtk_file_chooser_set_local_only != nullptr)
&& (gtk_file_chooser_set_action != nullptr)
&& (gtk_file_chooser_set_select_multiple != nullptr)
&& (gtk_file_chooser_set_do_overwrite_confirmation != nullptr)
&& (gtk_file_chooser_remove_filter != nullptr)
&& (gtk_file_filter_set_name != nullptr)
&& (gtk_file_filter_add_pattern != nullptr)
&& (gtk_file_chooser_add_filter != nullptr)
&& (gtk_file_filter_new != nullptr);
}
bool PreviewSupported() {
return (gdk_pixbuf_new_from_file_at_size != nullptr);
}
bool CustomButtonsSupported() {
return (gtk_dialog_get_widget_for_response != nullptr)
&& (gtk_button_set_label != nullptr)
&& (gtk_button_get_type != nullptr);
}
// This is a patched copy of qgtk2 theme plugin.
// We need to use our own gtk file dialog instead of
// styling Qt file dialog, because Qt only works with gtk2.
// We need to be able to work with gtk2 and gtk3, because
// we use gtk3 to work with appindicator3.
class QGtkDialog : public QWindow {
public:
QGtkDialog(GtkWidget *gtkWidget);
~QGtkDialog();
GtkDialog *gtkDialog() const;
void exec();
void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
void hide();
rpl::producer<> accept();
rpl::producer<> reject();
protected:
static void onResponse(QGtkDialog *dialog, int response);
private:
void onParentWindowDestroyed();
GtkWidget *gtkWidget = nullptr;
rpl::event_stream<> _accept;
rpl::event_stream<> _reject;
bool _destroyedConnected = false;
};
class GtkFileDialog : public QDialog {
public:
GtkFileDialog(
QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &directory = QString(),
const QString &filter = QString());
~GtkFileDialog();
void setVisible(bool visible) override;
void setWindowTitle(const QString &windowTitle) {
_windowTitle = windowTitle;
}
void setAcceptMode(QFileDialog::AcceptMode acceptMode) {
_acceptMode = acceptMode;
}
void setFileMode(QFileDialog::FileMode fileMode) {
_fileMode = fileMode;
}
void setOption(QFileDialog::Option option, bool on = true) {
if (on) {
_options |= option;
} else {
_options &= ~option;
}
}
int exec() override;
bool defaultNameFilterDisables() const;
void setDirectory(const QString &directory);
QDir directory() const;
void selectFile(const QString &filename);
QStringList selectedFiles() const;
void setFilter();
void selectNameFilter(const QString &filter);
QString selectedNameFilter() const;
private:
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
static void onCurrentFolderChanged(GtkFileDialog *helper);
static void onUpdatePreview(GtkDialog *gtkDialog, GtkFileDialog *helper);
void applyOptions();
void setNameFilters(const QStringList &filters);
void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
void hideHelper();
void onAccepted();
void onRejected();
// Options
QFileDialog::Options _options;
QString _windowTitle = "Choose file";
QString _initialDirectory;
QStringList _initialFiles;
QStringList _nameFilters;
QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen;
QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile;
QString _dir;
QStringList _selection;
QHash<QString, GtkFileFilter*> _filters;
QHash<GtkFileFilter*, QString> _filterNames;
QScopedPointer<QGtkDialog> d;
GtkWidget *_preview = nullptr;
rpl::lifetime _lifetime;
};
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this);
g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
}
QGtkDialog::~QGtkDialog() {
gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
gtk_widget_destroy(gtkWidget);
}
GtkDialog *QGtkDialog::gtkDialog() const {
return GTK_DIALOG(gtkWidget);
}
void QGtkDialog::exec() {
if (modality() == Qt::ApplicationModal) {
// block input to the whole app, including other GTK dialogs
gtk_dialog_run(gtkDialog());
} else {
// block input to the window, allow input to other GTK dialogs
QEventLoop loop;
rpl::lifetime lifetime;
accept(
) | rpl::start_with_next([&] {
loop.quit();
}, lifetime);
reject(
) | rpl::start_with_next([&] {
loop.quit();
}, lifetime);
loop.exec();
}
}
void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
if (!std::exchange(_destroyedConnected, true)) {
connect(parent, &QWindow::destroyed, this, [=] { onParentWindowDestroyed(); });
}
setParent(parent);
setFlags(flags);
setModality(modality);
gtk_widget_realize(gtkWidget); // creates X window
if (parent) {
internal::GdkSetTransientFor(gtk_widget_get_window(gtkWidget), parent);
}
if (modality != Qt::NonModal) {
gdk_window_set_modal_hint(gtk_widget_get_window(gtkWidget), true);
QGuiApplicationPrivate::showModalWindow(this);
}
gtk_widget_show(gtkWidget);
gdk_window_focus(gtk_widget_get_window(gtkWidget), 0);
}
void QGtkDialog::hide() {
QGuiApplicationPrivate::hideModalWindow(this);
gtk_widget_hide(gtkWidget);
}
rpl::producer<> QGtkDialog::accept() {
return _accept.events();
}
rpl::producer<> QGtkDialog::reject() {
return _reject.events();
}
void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
if (response == GTK_RESPONSE_OK)
dialog->_accept.fire({});
else
dialog->_reject.fire({});
}
void QGtkDialog::onParentWindowDestroyed() {
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
setParent(nullptr);
}
GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : QDialog(parent)
, _windowTitle(caption)
, _initialDirectory(directory) {
auto filters = makeFilterList(filter);
const int numFilters = filters.count();
_nameFilters.reserve(numFilters);
for (int i = 0; i < numFilters; ++i) {
_nameFilters << filters[i].simplified();
}
d.reset(new QGtkDialog(gtk_file_chooser_dialog_new("", nullptr,
GTK_FILE_CHOOSER_ACTION_OPEN,
tr::lng_cancel(tr::now).toUtf8().constData(), GTK_RESPONSE_CANCEL,
tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr)));
d.data()->accept(
) | rpl::start_with_next([=] {
onAccepted();
}, _lifetime);
d.data()->reject(
) | rpl::start_with_next([=] {
onRejected();
}, _lifetime);
g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
if (PreviewSupported()) {
_preview = gtk_image_new();
g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this);
gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), _preview);
}
}
GtkFileDialog::~GtkFileDialog() {
}
void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
_dir.clear();
_selection.clear();
applyOptions();
return d->show(flags, modality, parent);
}
void GtkFileDialog::setVisible(bool visible) {
if (visible) {
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) {
return;
}
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) {
return;
}
if (visible) {
showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr);
} else {
hideHelper();
}
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
// updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen);
QDialog::setVisible(visible);
}
int GtkFileDialog::exec() {
d->setModality(windowModality());
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_DeleteOnClose, false);
bool wasShowModal = testAttribute(Qt::WA_ShowModal);
setAttribute(Qt::WA_ShowModal, true);
setResult(0);
show();
QPointer<QDialog> guard = this;
d->exec();
if (guard.isNull())
return QDialog::Rejected;
setAttribute(Qt::WA_ShowModal, wasShowModal);
return result();
}
void GtkFileDialog::hideHelper() {
// After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
// & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
// values before hiding the dialog
_dir = directory().absolutePath();
_selection = selectedFiles();
d->hide();
}
bool GtkFileDialog::defaultNameFilterDisables() const {
return false;
}
void GtkFileDialog::setDirectory(const QString &directory) {
GtkDialog *gtkDialog = d->gtkDialog();
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toUtf8().constData());
}
QDir GtkFileDialog::directory() const {
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
// returns a bogus value -> return the cached value before hiding
if (!_dir.isEmpty())
return _dir;
QString ret;
GtkDialog *gtkDialog = d->gtkDialog();
gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog));
if (folder) {
ret = QString::fromUtf8(folder);
g_free(folder);
}
return QDir(ret);
}
void GtkFileDialog::selectFile(const QString &filename) {
_initialFiles.clear();
_initialFiles.append(filename);
}
QStringList GtkFileDialog::selectedFiles() const {
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
// returns a bogus value -> return the cached value before hiding
if (!_selection.isEmpty())
return _selection;
QStringList selection;
GtkDialog *gtkDialog = d->gtkDialog();
GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog));
for (GSList *it = filenames; it; it = it->next)
selection += QString::fromUtf8((const char*)it->data);
g_slist_free(filenames);
return selection;
}
void GtkFileDialog::setFilter() {
applyOptions();
}
void GtkFileDialog::selectNameFilter(const QString &filter) {
GtkFileFilter *gtkFilter = _filters.value(filter);
if (gtkFilter) {
GtkDialog *gtkDialog = d->gtkDialog();
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
}
}
QString GtkFileDialog::selectedNameFilter() const {
GtkDialog *gtkDialog = d->gtkDialog();
GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog));
return _filterNames.value(gtkFilter);
}
void GtkFileDialog::onAccepted() {
accept();
// QString filter = selectedNameFilter();
// if (filter.isEmpty())
// emit filterSelected(filter);
// QList<QUrl> files = selectedFiles();
// emit filesSelected(files);
// if (files.count() == 1)
// emit fileSelected(files.first());
}
void GtkFileDialog::onRejected() {
reject();
//
}
void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) {
// QString selection;
// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
// if (filename) {
// selection = QString::fromUtf8(filename);
// g_free(filename);
// }
// emit helper->currentChanged(QUrl::fromLocalFile(selection));
}
void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) {
// emit dialog->directoryEntered(dialog->directory());
}
void GtkFileDialog::onUpdatePreview(GtkDialog *gtkDialog, GtkFileDialog *helper) {
auto filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog));
if (!filename) {
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
return;
}
// Don't attempt to open anything which isn't a regular file. If a named pipe,
// this may hang. See https://crbug.com/534754.
struct stat stat_buf;
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
g_free(filename);
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
return;
}
// This will preserve the image's aspect ratio.
auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
g_free(filename);
if (pixbuf) {
gtk_image_set_from_pixbuf(GTK_IMAGE(helper->_preview), pixbuf);
g_object_unref(pixbuf);
}
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false);
}
GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) {
switch (fileMode) {
case QFileDialog::AnyFile:
case QFileDialog::ExistingFile:
case QFileDialog::ExistingFiles:
if (acceptMode == QFileDialog::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_OPEN;
else
return GTK_FILE_CHOOSER_ACTION_SAVE;
case QFileDialog::Directory:
default:
if (acceptMode == QFileDialog::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
else
return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
}
}
void GtkFileDialog::applyOptions() {
GtkDialog *gtkDialog = d->gtkDialog();
gtk_window_set_title(GTK_WINDOW(gtkDialog), _windowTitle.toUtf8().constData());
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true);
const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode);
gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action);
const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple);
const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite);
if (!_nameFilters.isEmpty())
setNameFilters(_nameFilters);
if (!_initialDirectory.isEmpty())
setDirectory(_initialDirectory);
for_const (const auto &filename, _initialFiles) {
if (_acceptMode == QFileDialog::AcceptSave) {
QFileInfo fi(filename);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8().constData());
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8().constData());
} else if (filename.endsWith('/')) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8().constData());
} else {
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8().constData());
}
}
const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front();
if (!initialNameFilter.isEmpty())
selectNameFilter(initialNameFilter);
if (CustomButtonsSupported()) {
GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
if (acceptButton) {
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8().constData());
else*/ if (_acceptMode == QFileDialog::AcceptOpen)
gtk_button_set_label(GTK_BUTTON(acceptButton), tr::lng_open_link(tr::now).toUtf8().constData());
else
gtk_button_set_label(GTK_BUTTON(acceptButton), tr::lng_settings_save(tr::now).toUtf8().constData());
}
GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);
if (rejectButton) {
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8().constData());
else*/
gtk_button_set_label(GTK_BUTTON(rejectButton), tr::lng_cancel(tr::now).toUtf8().constData());
}
}
}
void GtkFileDialog::setNameFilters(const QStringList &filters) {
GtkDialog *gtkDialog = d->gtkDialog();
Q_FOREACH (GtkFileFilter *filter, _filters)
gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);
_filters.clear();
_filterNames.clear();
for_const (auto &filter, filters) {
GtkFileFilter *gtkFilter = gtk_file_filter_new();
auto name = filter;//.left(filter.indexOf(QLatin1Char('(')));
auto extensions = cleanFilterList(filter);
gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8().constData() : name.toUtf8().constData());
for_const (auto &ext, extensions) {
auto caseInsensitiveExt = QString();
caseInsensitiveExt.reserve(4 * ext.size());
for_const (auto ch, ext) {
auto chLower = ch.toLower();
auto chUpper = ch.toUpper();
if (chLower != chUpper) {
caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']');
} else {
caseInsensitiveExt.append(ch);
}
}
gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8().constData());
}
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
_filters.insert(filter, gtkFilter);
_filterNames.insert(gtkFilter, filter);
}
}
} // namespace
std::optional<bool> Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
Type type,
QString startFile) {
if (!Supported()
|| (!qEnvironmentVariableIsSet("TDESKTOP_USE_GTK_FILE_DIALOG")
&& !DesktopEnvironment::IsGtkBased())) {
return std::nullopt;
}
if (cDialogLastPath().isEmpty()) {
InitLastPath();
}
GtkFileDialog dialog(parent, caption, QString(), filter);
dialog.setModal(true);
if (type == Type::ReadFile || type == Type::ReadFiles) {
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
} else if (type == Type::ReadFolder) {
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setFileMode(QFileDialog::Directory);
dialog.setOption(QFileDialog::ShowDirsOnly);
} else {
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setAcceptMode(QFileDialog::AcceptSave);
}
if (startFile.isEmpty() || startFile.at(0) != '/') {
startFile = cDialogLastPath() + '/' + startFile;
}
dialog.selectFile(startFile);
const auto res = dialog.exec();
if (type != Type::ReadFolder) {
// Save last used directory for all queries except directory choosing.
const auto path = dialog.directory().absolutePath();
if (!path.isEmpty() && path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeSettings();
}
}
if (res == QDialog::Accepted) {
if (type == Type::ReadFiles) {
files = dialog.selectedFiles();
} else {
files = dialog.selectedFiles().mid(0, 1);
}
return true;
}
files = QStringList();
remoteContent = QByteArray();
return false;
}
} // namespace Gtk
} // namespace FileDialog
} // namespace Platform

View File

@@ -1,27 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/file_utilities.h"
namespace Platform {
namespace FileDialog {
namespace Gtk {
std::optional<bool> Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
::FileDialog::internal::Type type,
QString startFile);
} // namespace Gtk
} // namespace FileDialog
} // namespace Platform

View File

@@ -7,12 +7,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/linux_gtk_integration.h"
#ifdef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#error "GTK integration depends on D-Bus integration."
#endif // DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include "base/platform/linux/base_linux_gtk_integration.h"
#include "base/platform/linux/base_linux_gtk_integration_p.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "base/platform/linux/base_linux_dbus_utilities.h"
#include "base/platform/base_platform_info.h"
#include "platform/linux/linux_gtk_integration_p.h"
#include "platform/linux/linux_gdk_helper.h"
#include "platform/linux/linux_gtk_file_dialog.h"
#include "platform/linux/linux_gtk_open_with_dialog.h"
#include "platform/linux/linux_wayland_integration.h"
#include "webview/webview_interface.h"
#include "window/window_controller.h"
#include "core/application.h"
#include <QtCore/QProcess>
#include <private/qguiapplication_p.h>
#include <giomm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace Platform {
namespace internal {
@@ -22,6 +41,31 @@ using BaseGtkIntegration = base::Platform::GtkIntegration;
namespace {
constexpr auto kService = "org.telegram.desktop.GtkIntegration-%1"_cs;
constexpr auto kObjectPath = "/org/telegram/desktop/GtkIntegration"_cs;
constexpr auto kInterface = "org.telegram.desktop.GtkIntegration"_cs;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
constexpr auto kGifcShmId = "tdesktop-gtk-gifc"_cs;
constexpr auto kIntrospectionXML = R"INTROSPECTION(<node>
<interface name='org.telegram.desktop.GtkIntegration'>
<method name='Load'>
<arg type='s' name='allowed-backends' direction='in'/>
</method>
<method name='ShowOpenWithDialog'>
<arg type='s' name='parent' direction='in'/>
<arg type='s' name='filepath' direction='in'/>
</method>
<method name='GetImageFromClipboard'>
<arg type='h' name='shm-descriptor' direction='out'/>
<arg type='i' name='shm-size' direction='out'/>
</method>
<signal name='OpenWithDialogResponse'>
<arg type='b' name='result' direction='out'/>
</signal>
</interface>
</node>)INTROSPECTION"_cs;
bool GetImageFromClipboardSupported() {
return (gtk_clipboard_get != nullptr)
&& (gtk_clipboard_wait_for_contents != nullptr)
@@ -38,7 +82,182 @@ bool GetImageFromClipboardSupported() {
} // namespace
GtkIntegration::GtkIntegration() {
class GtkIntegration::Private {
public:
Private()
: dbusConnection([] {
try {
return Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
} catch (...) {
return Glib::RefPtr<Gio::DBus::Connection>();
}
}())
, interfaceVTable(sigc::mem_fun(this, &Private::handleMethodCall))
, serviceName(kService.utf16().arg(getpid()).toStdString()) {
}
void handleMethodCall(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &method_name,
const Glib::VariantContainerBase &parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> &invocation);
const Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
const Gio::DBus::InterfaceVTable interfaceVTable;
Glib::RefPtr<Gio::DBus::NodeInfo> introspectionData;
Glib::ustring serviceName;
Glib::ustring parentDBusName;
bool remoting = true;
uint registerId = 0;
uint parentServiceWatcherId = 0;
};
void GtkIntegration::Private::handleMethodCall(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &method_name,
const Glib::VariantContainerBase &parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> &invocation) {
if (sender != parentDBusName) {
Gio::DBus::Error error(
Gio::DBus::Error::ACCESS_DENIED,
"Access denied.");
invocation->return_error(error);
}
try {
const auto integration = Instance();
if (!integration) {
throw std::exception();
}
auto parametersCopy = parameters;
if (method_name == "Load") {
const auto allowedBackends = base::Platform::GlibVariantCast<
Glib::ustring>(parametersCopy.get_child(0));
integration->load(QString::fromStdString(allowedBackends));
invocation->return_value({});
return;
} else if (method_name == "ShowOpenWithDialog") {
const auto parent = base::Platform::GlibVariantCast<
Glib::ustring>(parametersCopy.get_child(0));
const auto filepath = base::Platform::GlibVariantCast<
Glib::ustring>(parametersCopy.get_child(1));
const auto result = File::internal::ShowGtkOpenWithDialog(
QString::fromStdString(parent),
QString::fromStdString(filepath));
if (result) {
invocation->return_value({});
return;
}
} else if (method_name == "GetImageFromClipboard") {
const auto image = integration->getImageFromClipboard();
if (!image.isNull()) {
const auto bitsPerSample = 8;
const auto channels = image.hasAlphaChannel() ? 4 : 3;
QVector<uchar> dataVector(
image.constBits(),
image.constBits() + image.sizeInBytes());
QByteArray streamData;
QDataStream stream(&streamData, QIODevice::WriteOnly);
stream
<< image.width()
<< image.height()
<< image.bytesPerLine()
<< image.hasAlphaChannel()
<< bitsPerSample
<< channels
<< dataVector;
const auto fd = shm_open(
kGifcShmId.utf8().constData(),
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR);
if (fd == -1) {
throw std::exception();
}
const auto fdGuard = gsl::finally([&] {
close(fd);
shm_unlink(kGifcShmId.utf8().constData());
});
if (ftruncate(fd, streamData.size())) {
throw std::exception();
}
const auto mapped = mmap(
nullptr,
streamData.size(),
PROT_WRITE,
MAP_SHARED,
fd,
0);
if (mapped == MAP_FAILED) {
throw std::exception();
}
const auto mappedGuard = gsl::finally([&] {
munmap(mapped, streamData.size());
});
memcpy(mapped, streamData.constData(), streamData.size());
const auto fdList = Gio::UnixFDList::create();
fdList->append(fd);
invocation->return_value(
Glib::VariantContainerBase::create_tuple({
Glib::wrap(g_variant_new_handle(0)),
Glib::Variant<int>::create(streamData.size()),
}),
fdList);
return;
}
}
} catch (...) {
}
Gio::DBus::Error error(
Gio::DBus::Error::UNKNOWN_METHOD,
"Method does not exist.");
invocation->return_error(error);
}
GtkIntegration::GtkIntegration()
: _private(std::make_unique<Private>()) {
}
GtkIntegration::~GtkIntegration() {
if (_private->dbusConnection) {
if (_private->parentServiceWatcherId != 0) {
_private->dbusConnection->signal_unsubscribe(
_private->parentServiceWatcherId);
}
if (_private->registerId != 0) {
_private->dbusConnection->unregister_object(
_private->registerId);
}
}
}
GtkIntegration *GtkIntegration::Instance() {
@@ -50,10 +269,31 @@ GtkIntegration *GtkIntegration::Instance() {
return &instance;
}
void GtkIntegration::load() {
void GtkIntegration::load(const QString &allowedBackends) {
static bool Loaded = false;
Expects(!Loaded);
if (_private->remoting) {
if (!_private->dbusConnection) {
return;
}
try {
auto reply = _private->dbusConnection->call_sync(
std::string(kObjectPath),
std::string(kInterface),
"Load",
base::Platform::MakeGlibVariant(std::tuple{
Glib::ustring(allowedBackends.toStdString()),
}),
_private->serviceName);
} catch (...) {
}
return;
}
BaseGtkIntegration::Instance()->load(allowedBackends, true);
if (!BaseGtkIntegration::Instance()->loaded()) {
return;
}
@@ -61,57 +301,17 @@ void GtkIntegration::load() {
auto &lib = BaseGtkIntegration::Instance()->library();
LOAD_GTK_SYMBOL(lib, gtk_widget_show);
LOAD_GTK_SYMBOL(lib, gtk_widget_hide);
LOAD_GTK_SYMBOL(lib, gtk_widget_get_window);
LOAD_GTK_SYMBOL(lib, gtk_widget_realize);
LOAD_GTK_SYMBOL(lib, gtk_widget_hide_on_delete);
LOAD_GTK_SYMBOL(lib, gtk_widget_destroy);
LOAD_GTK_SYMBOL(lib, gtk_clipboard_get);
LOAD_GTK_SYMBOL(lib, gtk_clipboard_store);
LOAD_GTK_SYMBOL(lib, gtk_clipboard_wait_for_contents);
LOAD_GTK_SYMBOL(lib, gtk_clipboard_wait_for_image);
LOAD_GTK_SYMBOL(lib, gtk_selection_data_targets_include_image);
LOAD_GTK_SYMBOL(lib, gtk_selection_data_free);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_dialog_new);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_get_type);
LOAD_GTK_SYMBOL(lib, gtk_image_get_type);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_current_folder);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_get_current_folder);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_current_name);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_select_filename);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_get_filenames);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_filter);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_get_filter);
LOAD_GTK_SYMBOL(lib, gtk_window_get_type);
LOAD_GTK_SYMBOL(lib, gtk_window_set_title);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_local_only);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_action);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_select_multiple);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_do_overwrite_confirmation);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_remove_filter);
LOAD_GTK_SYMBOL(lib, gtk_file_filter_set_name);
LOAD_GTK_SYMBOL(lib, gtk_file_filter_add_pattern);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_add_filter);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_preview_widget);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_get_preview_filename);
LOAD_GTK_SYMBOL(lib, gtk_file_chooser_set_preview_widget_active);
LOAD_GTK_SYMBOL(lib, gtk_file_filter_new);
LOAD_GTK_SYMBOL(lib, gtk_image_new);
LOAD_GTK_SYMBOL(lib, gtk_image_set_from_pixbuf);
LOAD_GTK_SYMBOL(lib, gdk_window_set_modal_hint);
LOAD_GTK_SYMBOL(lib, gdk_window_focus);
LOAD_GTK_SYMBOL(lib, gtk_dialog_get_type);
LOAD_GTK_SYMBOL(lib, gtk_dialog_run);
LOAD_GTK_SYMBOL(lib, gdk_atom_intern);
LOAD_GTK_SYMBOL(lib, gdk_display_get_default);
LOAD_GTK_SYMBOL(lib, gdk_display_get_monitor);
LOAD_GTK_SYMBOL(lib, gdk_display_get_primary_monitor);
LOAD_GTK_SYMBOL(lib, gdk_monitor_get_scale_factor);
LOAD_GTK_SYMBOL(lib, gdk_pixbuf_new_from_file_at_size);
LOAD_GTK_SYMBOL(lib, gdk_pixbuf_get_has_alpha);
LOAD_GTK_SYMBOL(lib, gdk_pixbuf_get_pixels);
LOAD_GTK_SYMBOL(lib, gdk_pixbuf_get_width);
@@ -120,10 +320,6 @@ void GtkIntegration::load() {
GdkHelperLoad(lib);
LOAD_GTK_SYMBOL(lib, gtk_dialog_get_widget_for_response);
LOAD_GTK_SYMBOL(lib, gtk_button_set_label);
LOAD_GTK_SYMBOL(lib, gtk_button_get_type);
LOAD_GTK_SYMBOL(lib, gtk_app_chooser_dialog_new);
LOAD_GTK_SYMBOL(lib, gtk_app_chooser_get_app_info);
LOAD_GTK_SYMBOL(lib, gtk_app_chooser_get_type);
@@ -131,58 +327,233 @@ void GtkIntegration::load() {
Loaded = true;
}
std::optional<int> GtkIntegration::scaleFactor() const {
if ((gdk_display_get_default == nullptr)
|| (gdk_display_get_monitor == nullptr)
|| (gdk_display_get_primary_monitor == nullptr)
|| (gdk_monitor_get_scale_factor == nullptr)) {
return std::nullopt;
}
int GtkIntegration::exec(const QString &parentDBusName, int ppid) {
_private->remoting = false;
_private->serviceName = kService.utf16().arg(ppid).toStdString();
_private->parentDBusName = parentDBusName.toStdString();
const auto display = gdk_display_get_default();
if (!display) {
return std::nullopt;
}
_private->introspectionData = Gio::DBus::NodeInfo::create_for_xml(
std::string(kIntrospectionXML));
const auto monitor = [&] {
if (const auto primary = gdk_display_get_primary_monitor(display)) {
return primary;
_private->registerId = _private->dbusConnection->register_object(
std::string(kObjectPath),
_private->introspectionData->lookup_interface(),
_private->interfaceVTable);
rpl::lifetime lifetime;
File::internal::GtkOpenWithDialogResponse(
) | rpl::start_with_next([=](bool response) {
try {
_private->dbusConnection->emit_signal(
std::string(kObjectPath),
std::string(kInterface),
"OpenWithDialogResponse",
_private->parentDBusName,
base::Platform::MakeGlibVariant(std::tuple{
response,
}));
} catch (...) {
}
return gdk_display_get_monitor(display, 0);
}();
}, lifetime);
if (!monitor) {
return std::nullopt;
}
return gdk_monitor_get_scale_factor(monitor);
}
std::optional<bool> GtkIntegration::getFileDialog(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
::FileDialog::internal::Type type,
QString startFile) const {
return FileDialog::Gtk::Get(
parent,
files,
remoteContent,
caption,
filter,
type,
startFile);
const auto app = Gio::Application::create(_private->serviceName);
app->hold();
_private->parentServiceWatcherId = base::Platform::DBus::RegisterServiceWatcher(
_private->dbusConnection,
parentDBusName.toStdString(),
[=](
const Glib::ustring &service,
const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) {
if (!newOwner.empty()) {
return;
}
app->quit();
});
return app->run(0, nullptr);
}
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
return File::internal::ShowGtkOpenWithDialog(filepath);
const auto parent = [&] {
if (const auto activeWindow = Core::App().activeWindow()) {
if (const auto integration = WaylandIntegration::Instance()) {
if (const auto handle = integration->nativeHandle(
activeWindow->widget()->windowHandle())
; !handle.isEmpty()) {
return qsl("wayland:") + handle;
}
} else if (Platform::IsX11()) {
return qsl("x11:") + QString::number(
activeWindow->widget()->winId(),
16);
}
}
return QString();
}();
if (_private->remoting) {
if (!_private->dbusConnection) {
return false;
}
try {
_private->dbusConnection->call_sync(
std::string(kObjectPath),
std::string(kInterface),
"ShowOpenWithDialog",
base::Platform::MakeGlibVariant(std::tuple{
Glib::ustring(parent.toStdString()),
Glib::ustring(filepath.toStdString()),
}),
_private->serviceName);
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
bool result = false;
const auto signalId = _private->dbusConnection->signal_subscribe(
[&](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
Glib::VariantContainerBase parameters) {
try {
auto parametersCopy = parameters;
result = base::Platform::GlibVariantCast<bool>(
parametersCopy.get_child(0));
loop->quit();
} catch (...) {
}
},
_private->serviceName,
std::string(kInterface),
"OpenWithDialogResponse",
std::string(kObjectPath));
const auto signalGuard = gsl::finally([&] {
if (signalId != 0) {
_private->dbusConnection->signal_unsubscribe(signalId);
}
});
QWindow window;
QGuiApplicationPrivate::showModalWindow(&window);
loop->run();
g_main_context_pop_thread_default(context->gobj());
QGuiApplicationPrivate::hideModalWindow(&window);
return result;
} catch (...) {
}
return false;
}
if (!File::internal::ShowGtkOpenWithDialog(parent, filepath)) {
return false;
}
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
rpl::lifetime lifetime;
bool result = false;
File::internal::GtkOpenWithDialogResponse(
) | rpl::start_with_next([&](bool response) {
result = response;
loop->quit();
}, lifetime);
QWindow window;
QGuiApplicationPrivate::showModalWindow(&window);
loop->run();
g_main_context_pop_thread_default(context->gobj());
QGuiApplicationPrivate::hideModalWindow(&window);
return result;
}
QImage GtkIntegration::getImageFromClipboard() const {
QImage data;
if (_private->remoting) {
if (!_private->dbusConnection) {
return data;
}
try {
Glib::RefPtr<Gio::UnixFDList> outFdList;
auto reply = _private->dbusConnection->call_sync(
std::string(kObjectPath),
std::string(kInterface),
"GetImageFromClipboard",
{},
{},
outFdList,
_private->serviceName);
const auto streamSize = base::Platform::GlibVariantCast<int>(
reply.get_child(1));
const auto mapped = mmap(
nullptr,
streamSize,
PROT_READ,
MAP_SHARED,
outFdList->get(0),
0);
if (mapped == MAP_FAILED) {
return data;
}
QByteArray streamData;
streamData.resize(streamSize);
memcpy(streamData.data(), mapped, streamData.size());
munmap(mapped, streamData.size());
int imageWidth = 0;
int imageHeight = 0;
int imageBytesPerLine = 0;
bool imageHasAlphaChannel = false;
int imageBitsPerSample = 0;
int imageChannels = 0;
QVector<uchar> imageData;
QDataStream stream(streamData);
stream
>> imageWidth
>> imageHeight
>> imageBytesPerLine
>> imageHasAlphaChannel
>> imageBitsPerSample
>> imageChannels
>> imageData;
data = QImage(
imageData.data(),
imageWidth,
imageHeight,
imageBytesPerLine,
imageHasAlphaChannel
? QImage::Format_RGBA8888
: QImage::Format_RGB888).copy();
return data;
} catch (...) {
}
return data;
}
if (!GetImageFromClipboardSupported()) {
return data;
}
@@ -220,5 +591,113 @@ QImage GtkIntegration::getImageFromClipboard() const {
return data;
}
QString GtkIntegration::AllowedBackends() {
return Platform::IsWayland()
? qsl("wayland,x11")
: Platform::IsX11()
? qsl("x11,wayland")
: QString();
}
int GtkIntegration::Exec(
Type type,
const QString &parentDBusName,
int ppid,
uint instanceNumber) {
Glib::init();
Gio::init();
if (type == Type::Base) {
if (const auto integration = BaseGtkIntegration::Instance()) {
return integration->exec(parentDBusName, ppid);
}
} else if (type == Type::Webview) {
if (const auto instance = Webview::CreateInstance({})) {
return instance->exec(
parentDBusName.toStdString(),
ppid,
instanceNumber);
}
} else if (type == Type::TDesktop) {
if (const auto integration = Instance()) {
return integration->exec(parentDBusName, ppid);
}
}
return 1;
}
void GtkIntegration::Start(Type type) {
if (type != Type::Base && type != Type::TDesktop) {
return;
}
const auto dbusName = [] {
try {
static const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
return QString::fromStdString(connection->get_unique_name());
} catch (...) {
return QString();
}
}();
if (dbusName.isEmpty()) {
return;
}
QProcess::startDetached(cExeDir() + cExeName(), {
(type == Type::Base)
? qsl("-basegtkintegration")
: qsl("-gtkintegration"),
dbusName,
QString::number(getpid()),
});
}
void GtkIntegration::Autorestart(Type type) {
if (type != Type::Base && type != Type::TDesktop) {
return;
}
try {
static const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
const auto baseServiceName = [] {
if (const auto integration = BaseGtkIntegration::Instance()) {
return integration->serviceName();
}
return QString();
}();
base::Platform::DBus::RegisterServiceWatcher(
connection,
(type == Type::Base)
? baseServiceName.toStdString()
: kService.utf16().arg(getpid()).toStdString(),
[=](
const Glib::ustring &service,
const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) {
if (newOwner.empty()) {
Start(type);
} else {
if (type == Type::Base) {
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->load(AllowedBackends());
}
} else if (type == Type::TDesktop) {
if (const auto integration = Instance()) {
integration->load(AllowedBackends());
}
}
}
});
} catch (...) {
}
}
} // namespace internal
} // namespace Platform

View File

@@ -7,34 +7,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/file_utilities.h"
namespace Platform {
namespace internal {
class GtkIntegration {
public:
enum class Type {
Base,
Webview,
TDesktop,
};
static GtkIntegration *Instance();
void load();
[[nodiscard]] std::optional<int> scaleFactor() const;
[[nodiscard]] std::optional<bool> getFileDialog(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
::FileDialog::internal::Type type,
QString startFile) const;
void load(const QString &allowedBackends);
int exec(const QString &parentDBusName, int ppid);
[[nodiscard]] bool showOpenWithDialog(const QString &filepath) const;
[[nodiscard]] QImage getImageFromClipboard() const;
static QString AllowedBackends();
static int Exec(
Type type,
const QString &parentDBusName,
int ppid,
uint instanceNumber = 0);
static void Start(Type type);
static void Autorestart(Type type);
private:
GtkIntegration();
~GtkIntegration();
class Private;
const std::unique_ptr<Private> _private;
};
} // namespace internal

View File

@@ -10,29 +10,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace internal {
class GtkIntegration::Private {
};
GtkIntegration::GtkIntegration() {
}
GtkIntegration::~GtkIntegration() = default;
GtkIntegration *GtkIntegration::Instance() {
return nullptr;
}
void GtkIntegration::load() {
void GtkIntegration::load(const QString &allowedBackends) {
}
std::optional<int> GtkIntegration::scaleFactor() const {
return std::nullopt;
}
std::optional<bool> GtkIntegration::getFileDialog(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
::FileDialog::internal::Type type,
QString startFile) const {
return std::nullopt;
int GtkIntegration::exec(const QString &parentDBusName, int ppid) {
return 1;
}
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
@@ -43,5 +37,23 @@ QImage GtkIntegration::getImageFromClipboard() const {
return {};
}
QString GtkIntegration::AllowedBackends() {
return {};
}
int GtkIntegration:Exec(
Type type,
const QString &parentDBusName,
int ppid,
uint instanceNumber) {
return 1;
}
void GtkIntegration::Start(Type type) {
}
void GtkIntegration::Autorestart(Type type) {
}
} // namespace internal
} // namespace Platform

View File

@@ -16,59 +16,18 @@ namespace Platform {
namespace Gtk {
inline void (*gtk_widget_show)(GtkWidget *widget) = nullptr;
inline void (*gtk_widget_hide)(GtkWidget *widget) = nullptr;
inline GdkWindow* (*gtk_widget_get_window)(GtkWidget *widget) = nullptr;
inline void (*gtk_widget_realize)(GtkWidget *widget) = nullptr;
inline gboolean (*gtk_widget_hide_on_delete)(GtkWidget *widget) = nullptr;
inline void (*gtk_widget_destroy)(GtkWidget *widget) = nullptr;
inline GtkClipboard* (*gtk_clipboard_get)(GdkAtom selection) = nullptr;
inline void (*gtk_clipboard_store)(GtkClipboard *clipboard) = nullptr;
inline GtkSelectionData* (*gtk_clipboard_wait_for_contents)(GtkClipboard *clipboard, GdkAtom target) = nullptr;
inline GdkPixbuf* (*gtk_clipboard_wait_for_image)(GtkClipboard *clipboard) = nullptr;
inline gboolean (*gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable) = nullptr;
inline void (*gtk_selection_data_free)(GtkSelectionData *data) = nullptr;
inline GType (*gtk_file_chooser_get_type)(void) G_GNUC_CONST = nullptr;
inline GtkWidget* (*gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED = nullptr;
inline gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename) = nullptr;
inline gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser) = nullptr;
inline void (*gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name) = nullptr;
inline gboolean (*gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename) = nullptr;
inline GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser) = nullptr;
inline void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
inline GtkFileFilter* (*gtk_file_chooser_get_filter)(GtkFileChooser *chooser) = nullptr;
inline GType (*gtk_window_get_type)(void) G_GNUC_CONST = nullptr;
inline void (*gtk_window_set_title)(GtkWindow *window, const gchar *title) = nullptr;
inline void (*gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only) = nullptr;
inline void (*gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action) = nullptr;
inline void (*gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple) = nullptr;
inline void (*gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation) = nullptr;
inline GType (*gtk_dialog_get_type)(void) G_GNUC_CONST = nullptr;
inline GtkWidget* (*gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id) = nullptr;
inline GType (*gtk_button_get_type)(void) G_GNUC_CONST = nullptr;
inline void (*gtk_button_set_label)(GtkButton *button, const gchar *label) = nullptr;
inline void (*gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
inline void (*gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name) = nullptr;
inline void (*gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern) = nullptr;
inline void (*gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
inline void (*gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget) = nullptr;
inline gchar* (*gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser) = nullptr;
inline void (*gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active) = nullptr;
inline GtkFileFilter* (*gtk_file_filter_new)(void) = nullptr;
inline GType (*gtk_image_get_type)(void) G_GNUC_CONST = nullptr;
inline GtkWidget* (*gtk_image_new)(void) = nullptr;
inline void (*gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf) = nullptr;
inline GType (*gtk_app_chooser_get_type)(void) G_GNUC_CONST = nullptr;
inline GtkWidget* (*gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file) = nullptr;
inline GAppInfo* (*gtk_app_chooser_get_app_info)(GtkAppChooser *self) = nullptr;
inline void (*gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal) = nullptr;
inline void (*gdk_window_focus)(GdkWindow *window, guint32 timestamp) = nullptr;
inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr;
inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr;
inline GdkDisplay* (*gdk_display_get_default)(void) = nullptr;
inline GdkMonitor* (*gdk_display_get_monitor)(GdkDisplay *display, int monitor_num) = nullptr;
inline GdkMonitor* (*gdk_display_get_primary_monitor)(GdkDisplay *display) = nullptr;
inline int (*gdk_monitor_get_scale_factor)(GdkMonitor *monitor) = nullptr;
inline GdkPixbuf* (*gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error) = nullptr;
inline gboolean (*gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf) = nullptr;
inline guchar* (*gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf) = nullptr;
inline int (*gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf) = nullptr;

View File

@@ -9,10 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_gtk_integration_p.h"
#include "platform/linux/linux_gdk_helper.h"
#include "window/window_controller.h"
#include "core/application.h"
#include <private/qguiapplication_p.h>
#include <giomm.h>
namespace Platform {
@@ -22,6 +19,8 @@ namespace {
using namespace Platform::Gtk;
rpl::event_stream<bool> GtkOpenWithDialogResponseStream;
struct GtkWidgetDeleter {
void operator()(GtkWidget *widget) {
gtk_widget_destroy(widget);
@@ -38,22 +37,22 @@ bool Supported() {
&& (gtk_widget_destroy != nullptr);
}
class GtkOpenWithDialog : public QWindow {
class GtkOpenWithDialog {
public:
GtkOpenWithDialog(const QString &filepath);
bool exec();
GtkOpenWithDialog(
const QString &parent,
const QString &filepath);
private:
static void handleResponse(GtkOpenWithDialog *dialog, int responseId);
const Glib::RefPtr<Gio::File> _file;
const std::unique_ptr<GtkWidget, GtkWidgetDeleter> _gtkWidget;
QEventLoop _loop;
std::optional<bool> _result;
};
GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath)
GtkOpenWithDialog::GtkOpenWithDialog(
const QString &parent,
const QString &filepath)
: _file(Gio::File::create_for_path(filepath.toStdString()))
, _gtkWidget(gtk_app_chooser_dialog_new(
nullptr,
@@ -64,31 +63,19 @@ GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath)
"response",
G_CALLBACK(handleResponse),
this);
}
bool GtkOpenWithDialog::exec() {
gtk_widget_realize(_gtkWidget.get());
if (const auto activeWindow = Core::App().activeWindow()) {
Platform::internal::GdkSetTransientFor(
gtk_widget_get_window(_gtkWidget.get()),
activeWindow->widget()->windowHandle());
}
Platform::internal::GdkSetTransientFor(
gtk_widget_get_window(_gtkWidget.get()),
parent);
QGuiApplicationPrivate::showModalWindow(this);
gtk_widget_show(_gtkWidget.get());
if (!_result.has_value()) {
_loop.exec();
}
QGuiApplicationPrivate::hideModalWindow(this);
return *_result;
}
void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId) {
Glib::RefPtr<Gio::AppInfo> chosenAppInfo;
dialog->_result = true;
bool result = true;
switch (responseId) {
case GTK_RESPONSE_OK:
@@ -110,21 +97,28 @@ void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId
break;
default:
dialog->_result = false;
result = false;
break;
}
dialog->_loop.quit();
GtkOpenWithDialogResponseStream.fire_copy(result);
delete dialog;
}
} // namespace
bool ShowGtkOpenWithDialog(const QString &filepath) {
bool ShowGtkOpenWithDialog(
const QString &parent,
const QString &filepath) {
if (!Supported()) {
return false;
}
return GtkOpenWithDialog(filepath).exec();
return new GtkOpenWithDialog(parent, filepath);
}
rpl::producer<bool> GtkOpenWithDialogResponse() {
return GtkOpenWithDialogResponseStream.events();
}
} // namespace internal

View File

@@ -11,7 +11,11 @@ namespace Platform {
namespace File {
namespace internal {
bool ShowGtkOpenWithDialog(const QString &filepath);
bool ShowGtkOpenWithDialog(
const QString &parent,
const QString &filepath);
[[nodiscard]] rpl::producer<bool> GtkOpenWithDialogResponse();
} // namespace internal
} // namespace File

View File

@@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <surface.h>
#include <xdgforeign.h>
#include <plasmashell.h>
#include <appmenu.h>
using namespace KWayland::Client;
@@ -37,10 +36,6 @@ public:
return _plasmaShell.get();
}
[[nodiscard]] AppMenuManager *appMenuManager() {
return _appMenuManager.get();
}
[[nodiscard]] QEventLoop &interfacesLoop() {
return _interfacesLoop;
}
@@ -56,7 +51,6 @@ private:
Registry _applicationRegistry;
std::unique_ptr<XdgExporter> _xdgExporter;
std::unique_ptr<PlasmaShell> _plasmaShell;
std::unique_ptr<AppMenuManager> _appMenuManager;
QEventLoop _interfacesLoop;
bool _interfacesAnnounced = false;
};
@@ -123,21 +117,6 @@ WaylandIntegration::Private::Private()
&PlasmaShell::destroy);
});
connect(
&_applicationRegistry,
&Registry::appMenuAnnounced,
[=](uint name, uint version) {
_appMenuManager = std::unique_ptr<AppMenuManager>{
_applicationRegistry.createAppMenuManager(name, version),
};
connect(
_applicationConnection,
&ConnectionThread::connectionDied,
_appMenuManager.get(),
&AppMenuManager::destroy);
});
_connection.initConnection();
}
@@ -208,27 +187,5 @@ void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) {
plasmaSurface->setSkipTaskbar(skip);
}
void WaylandIntegration::registerAppMenu(
QWindow *window,
const QString &serviceName,
const QString &objectPath) {
const auto manager = _private->appMenuManager();
if (!manager) {
return;
}
const auto surface = Surface::fromWindow(window);
if (!surface) {
return;
}
const auto appMenu = manager->create(surface, surface);
if (!appMenu) {
return;
}
appMenu->setAddress(serviceName, objectPath);
}
} // namespace internal
} // namespace Platform

View File

@@ -18,10 +18,6 @@ public:
[[nodiscard]] QString nativeHandle(QWindow *window);
[[nodiscard]] bool skipTaskbarSupported();
void skipTaskbar(QWindow *window, bool skip);
void registerAppMenu(
QWindow *window,
const QString &serviceName,
const QString &objectPath);
private:
WaylandIntegration();

View File

@@ -44,11 +44,5 @@ bool WaylandIntegration::skipTaskbarSupported() {
void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) {
}
void WaylandIntegration::registerAppMenu(
QWindow *window,
const QString &serviceName,
const QString &objectPath) {
}
} // namespace internal
} // namespace Platform

View File

@@ -578,20 +578,23 @@ int XDPFileDialog::exec() {
// HACK we have to avoid returning until we emit
// that the dialog was accepted or rejected
QEventLoop loop;
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
rpl::lifetime lifetime;
accepted(
) | rpl::start_with_next([&] {
loop.quit();
loop->quit();
}, lifetime);
rejected(
) | rpl::start_with_next([&] {
loop.quit();
loop->quit();
}, lifetime);
loop.exec();
loop->run();
g_main_context_pop_thread_default(context->gobj());
if (guard.isNull()) {
return QDialog::Rejected;

View File

@@ -33,19 +33,9 @@ constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_c
constexpr auto kXDGDesktopPortalOpenURIInterface = "org.freedesktop.portal.OpenURI"_cs;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
class XDPOpenWithDialog : public QWindow {
public:
XDPOpenWithDialog(const QString &filepath)
: _filepath(filepath.toStdString()) {
}
} // namespace
bool exec();
private:
Glib::ustring _filepath;
};
bool XDPOpenWithDialog::exec() {
bool ShowXDPOpenWithDialog(const QString &filepath) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
@@ -69,8 +59,10 @@ bool XDPOpenWithDialog::exec() {
return false;
}
const auto filepathUtf8 = filepath.toUtf8();
const auto fd = open(
_filepath.c_str(),
filepathUtf8.constData(),
O_RDONLY);
if (fd == -1) {
@@ -113,7 +105,9 @@ bool XDPOpenWithDialog::exec() {
+ '/'
+ handleToken;
QEventLoop loop;
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
const auto signalId = connection->signal_subscribe(
[&](
@@ -123,7 +117,7 @@ bool XDPOpenWithDialog::exec() {
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
loop.quit();
loop->quit();
},
std::string(kXDGDesktopPortalService),
"org.freedesktop.portal.Request",
@@ -166,9 +160,11 @@ bool XDPOpenWithDialog::exec() {
std::string(kXDGDesktopPortalService));
if (signalId != 0) {
QGuiApplicationPrivate::showModalWindow(this);
loop.exec();
QGuiApplicationPrivate::hideModalWindow(this);
QWindow window;
QGuiApplicationPrivate::showModalWindow(&window);
loop->run();
g_main_context_pop_thread_default(context->gobj());
QGuiApplicationPrivate::hideModalWindow(&window);
}
return true;
@@ -178,12 +174,6 @@ bool XDPOpenWithDialog::exec() {
return false;
}
} // namespace
bool ShowXDPOpenWithDialog(const QString &filepath) {
return XDPOpenWithDialog(filepath).exec();
}
} // namespace internal
} // namespace File
} // namespace Platform

View File

@@ -43,15 +43,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QSize>
#include <QtCore/QTemporaryFile>
#include <QtGui/QWindow>
#include <QtWidgets/QMenuBar>
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusObjectPath>
#include <QtDBus/QDBusMetaType>
#include <statusnotifieritem.h>
#include <dbusmenuexporter.h>
#include <glibmm.h>
#include <giomm.h>
@@ -74,12 +71,6 @@ constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
constexpr auto kSNIWatcherObjectPath = "/StatusNotifierWatcher"_cs;
constexpr auto kSNIWatcherInterface = kSNIWatcherService;
constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs;
constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs;
constexpr auto kAppMenuInterface = kAppMenuService;
constexpr auto kMainMenuObjectPath = "/MenuBar"_cs;
bool TrayIconMuted = true;
int32 TrayIconCount = 0;
base::flat_map<int, QImage> TrayIconImageBack;
@@ -364,6 +355,31 @@ QIcon TrayIconGen(int counter, bool muted) {
return result;
}
void SendKeySequence(
Qt::Key key,
Qt::KeyboardModifiers modifiers = Qt::NoModifier) {
const auto focused = QApplication::focusWidget();
if (qobject_cast<QLineEdit*>(focused)
|| qobject_cast<QTextEdit*>(focused)
|| qobject_cast<HistoryInner*>(focused)) {
QApplication::postEvent(
focused,
new QKeyEvent(QEvent::KeyPress, key, modifiers));
QApplication::postEvent(
focused,
new QKeyEvent(QEvent::KeyRelease, key, modifiers));
}
}
void ForceDisabled(QAction *action, bool disabled) {
if (action->isEnabled()) {
if (disabled) action->setDisabled(true);
} else if (!disabled) {
action->setDisabled(false);
}
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
bool IsIndicatorApplication() {
// Hack for indicator-application,
@@ -520,104 +536,161 @@ uint djbStringHash(const std::string &string) {
}
return hash;
}
bool IsAppMenuSupported() {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
return base::Platform::DBus::NameHasOwner(
connection,
std::string(kAppMenuService));
} catch (...) {
}
return false;
}
// This call must be made from the same bus connection as DBusMenuExporter
// So it must use QDBusConnection
void RegisterAppMenu(QWindow *window, const QString &menuPath) {
if (const auto integration = WaylandIntegration::Instance()) {
integration->registerAppMenu(
window,
QDBusConnection::sessionBus().baseService(),
menuPath);
return;
}
auto message = QDBusMessage::createMethodCall(
kAppMenuService.utf16(),
kAppMenuObjectPath.utf16(),
kAppMenuInterface.utf16(),
qsl("RegisterWindow"));
message.setArguments({
window->winId(),
QVariant::fromValue(QDBusObjectPath(menuPath))
});
QDBusConnection::sessionBus().send(message);
}
// This call must be made from the same bus connection as DBusMenuExporter
// So it must use QDBusConnection
void UnregisterAppMenu(QWindow *window) {
if (const auto integration = WaylandIntegration::Instance()) {
return;
}
auto message = QDBusMessage::createMethodCall(
kAppMenuService.utf16(),
kAppMenuObjectPath.utf16(),
kAppMenuInterface.utf16(),
qsl("UnregisterWindow"));
message.setArguments({
window->winId()
});
QDBusConnection::sessionBus().send(message);
}
void SendKeySequence(
Qt::Key key,
Qt::KeyboardModifiers modifiers = Qt::NoModifier) {
const auto focused = QApplication::focusWidget();
if (qobject_cast<QLineEdit*>(focused)
|| qobject_cast<QTextEdit*>(focused)
|| qobject_cast<HistoryInner*>(focused)) {
QApplication::postEvent(
focused,
new QKeyEvent(QEvent::KeyPress, key, modifiers));
QApplication::postEvent(
focused,
new QKeyEvent(QEvent::KeyRelease, key, modifiers));
}
}
void ForceDisabled(QAction *action, bool disabled) {
if (action->isEnabled()) {
if (disabled) action->setDisabled(true);
} else if (!disabled) {
action->setDisabled(false);
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
} // namespace
class MainWindow::Private {
public:
explicit Private(not_null<MainWindow*> window)
: _public(window) {
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
StatusNotifierItem *sniTrayIcon = nullptr;
uint sniRegisteredSignalId = 0;
uint sniWatcherId = 0;
std::unique_ptr<QTemporaryFile> trayIconFile;
void setSNITrayIcon(int counter, bool muted);
void attachToSNITrayIcon();
void handleSNIHostRegistered();
void handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner);
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
private:
not_null<MainWindow*> _public;
};
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::Private::setSNITrayIcon(int counter, bool muted) {
const auto iconName = GetTrayIconName(counter, muted);
const auto panelIconName = GetPanelIconName(counter, muted);
if (iconName == panelIconName) {
if (sniTrayIcon->iconName() == iconName) {
return;
}
sniTrayIcon->setIconByName(iconName);
sniTrayIcon->setToolTipIconByName(iconName);
} else if (IsIndicatorApplication()) {
if (!IsIconRegenerationNeeded(counter, muted)
&& trayIconFile
&& sniTrayIcon->iconName() == trayIconFile->fileName()) {
return;
}
const auto icon = TrayIconGen(counter, muted);
trayIconFile = TrayIconFile(icon, _public);
if (trayIconFile) {
// indicator-application doesn't support tooltips
sniTrayIcon->setIconByName(trayIconFile->fileName());
}
} else {
if (!IsIconRegenerationNeeded(counter, muted)
&& !sniTrayIcon->iconPixmap().isEmpty()
&& sniTrayIcon->iconName().isEmpty()) {
return;
}
const auto icon = TrayIconGen(counter, muted);
sniTrayIcon->setIconByPixmap(icon);
sniTrayIcon->setToolTipIconByPixmap(icon);
}
}
void MainWindow::Private::attachToSNITrayIcon() {
sniTrayIcon->setToolTipTitle(AppName.utf16());
connect(sniTrayIcon,
&StatusNotifierItem::activateRequested,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
_public->handleTrayIconActication(QSystemTrayIcon::Trigger);
});
});
connect(sniTrayIcon,
&StatusNotifierItem::secondaryActivateRequested,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
_public->handleTrayIconActication(QSystemTrayIcon::MiddleClick);
});
});
}
void MainWindow::Private::handleSNIHostRegistered() {
if (_public->_sniAvailable) {
return;
}
_public->_sniAvailable = true;
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
return;
}
LOG(("Switching to SNI tray icon..."));
if (_public->trayIcon) {
_public->trayIcon->setContextMenu(nullptr);
_public->trayIcon->deleteLater();
}
_public->trayIcon = nullptr;
_public->psSetupTrayIcon();
SkipTaskbar(
_public->windowHandle(),
Core::App().settings().workMode() == WorkMode::TrayOnly);
}
void MainWindow::Private::handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
_public->_sniAvailable = IsSNIAvailable();
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
return;
}
if (oldOwner.isEmpty() && !newOwner.isEmpty() && _public->_sniAvailable) {
LOG(("Switching to SNI tray icon..."));
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
LOG(("Switching to Qt tray icon..."));
} else {
return;
}
if (_public->trayIcon) {
_public->trayIcon->setContextMenu(0);
_public->trayIcon->deleteLater();
}
_public->trayIcon = nullptr;
if (_public->trayAvailable()) {
_public->psSetupTrayIcon();
} else {
LOG(("System tray is not available."));
}
SkipTaskbar(
_public->windowHandle(),
(Core::App().settings().workMode() == WorkMode::TrayOnly)
&& _public->trayAvailable());
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Window::MainWindow(controller)
, _private(std::make_unique<Private>()) {
, _private(std::make_unique<Private>(this)) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
qDBusRegisterMetaType<ToolTip>();
qDBusRegisterMetaType<IconPixmap>();
@@ -626,63 +699,6 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
}
void MainWindow::initHook() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
_sniAvailable = IsSNIAvailable();
_appMenuSupported = IsAppMenuSupported();
try {
_private->dbusConnection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
_sniRegisteredSignalId = _private->dbusConnection->signal_subscribe(
[](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
if (signal_name == "StatusNotifierHostRegistered") {
crl::on_main([] {
if (const auto window = App::wnd()) {
window->handleSNIHostRegistered();
}
});
}
},
std::string(kSNIWatcherService),
std::string(kSNIWatcherInterface),
"StatusNotifierHostRegistered",
std::string(kSNIWatcherObjectPath));
_sniWatcherId = base::Platform::DBus::RegisterServiceWatcher(
_private->dbusConnection,
std::string(kSNIWatcherService),
[=](
const Glib::ustring &service,
const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) {
handleSNIOwnerChanged(
QString::fromStdString(service),
QString::fromStdString(oldOwner),
QString::fromStdString(newOwner));
});
_appMenuWatcherId = base::Platform::DBus::RegisterServiceWatcher(
_private->dbusConnection,
std::string(kAppMenuService),
[=](
const Glib::ustring &service,
const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) {
handleAppMenuOwnerChanged(
QString::fromStdString(service),
QString::fromStdString(oldOwner),
QString::fromStdString(newOwner));
});
} catch (...) {
}
base::install_event_filter(windowHandle(), [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Expose) {
auto ee = static_cast<QExposeEvent*>(e.get());
@@ -704,10 +720,47 @@ void MainWindow::initHook() {
return base::EventFilterResult::Continue;
});
if (_appMenuSupported) {
LOG(("Using D-Bus global menu."));
} else {
LOG(("Not using D-Bus global menu."));
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
_sniAvailable = IsSNIAvailable();
try {
_private->dbusConnection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
_private->sniRegisteredSignalId = _private->dbusConnection->signal_subscribe(
[](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
if (signal_name == "StatusNotifierHostRegistered") {
crl::on_main([] {
if (const auto window = App::wnd()) {
window->_private->handleSNIHostRegistered();
}
});
}
},
std::string(kSNIWatcherService),
std::string(kSNIWatcherInterface),
"StatusNotifierHostRegistered",
std::string(kSNIWatcherObjectPath));
_private->sniWatcherId = base::Platform::DBus::RegisterServiceWatcher(
_private->dbusConnection,
std::string(kSNIWatcherService),
[=](
const Glib::ustring &service,
const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) {
_private->handleSNIOwnerChanged(
QString::fromStdString(service),
QString::fromStdString(oldOwner),
QString::fromStdString(newOwner));
});
} catch (...) {
}
if (UseUnityCounter()) {
@@ -726,7 +779,7 @@ void MainWindow::initHook() {
bool MainWindow::hasTrayIcon() const {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
return trayIcon || (_sniAvailable && _sniTrayIcon);
return trayIcon || (_sniAvailable && _private->sniTrayIcon);
#else
return trayIcon;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -744,147 +797,6 @@ void MainWindow::psShowTrayMenu() {
void MainWindow::psTrayMenuUpdated() {
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::setSNITrayIcon(int counter, bool muted) {
const auto iconName = GetTrayIconName(counter, muted);
const auto panelIconName = GetPanelIconName(counter, muted);
if (iconName == panelIconName) {
if (_sniTrayIcon->iconName() == iconName) {
return;
}
_sniTrayIcon->setIconByName(iconName);
_sniTrayIcon->setToolTipIconByName(iconName);
} else if (IsIndicatorApplication()) {
if (!IsIconRegenerationNeeded(counter, muted)
&& _trayIconFile
&& _sniTrayIcon->iconName() == _trayIconFile->fileName()) {
return;
}
const auto icon = TrayIconGen(counter, muted);
_trayIconFile = TrayIconFile(icon, this);
if (_trayIconFile) {
// indicator-application doesn't support tooltips
_sniTrayIcon->setIconByName(_trayIconFile->fileName());
}
} else {
if (!IsIconRegenerationNeeded(counter, muted)
&& !_sniTrayIcon->iconPixmap().isEmpty()
&& _sniTrayIcon->iconName().isEmpty()) {
return;
}
const auto icon = TrayIconGen(counter, muted);
_sniTrayIcon->setIconByPixmap(icon);
_sniTrayIcon->setToolTipIconByPixmap(icon);
}
}
void MainWindow::attachToSNITrayIcon() {
_sniTrayIcon->setToolTipTitle(AppName.utf16());
connect(_sniTrayIcon,
&StatusNotifierItem::activateRequested,
this,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
handleTrayIconActication(QSystemTrayIcon::Trigger);
});
});
connect(_sniTrayIcon,
&StatusNotifierItem::secondaryActivateRequested,
this,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
handleTrayIconActication(QSystemTrayIcon::MiddleClick);
});
});
}
void MainWindow::handleSNIHostRegistered() {
if (_sniAvailable) {
return;
}
_sniAvailable = true;
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
return;
}
LOG(("Switching to SNI tray icon..."));
if (trayIcon) {
trayIcon->setContextMenu(nullptr);
trayIcon->deleteLater();
}
trayIcon = nullptr;
psSetupTrayIcon();
SkipTaskbar(
windowHandle(),
Core::App().settings().workMode() == WorkMode::TrayOnly);
}
void MainWindow::handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
_sniAvailable = IsSNIAvailable();
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
return;
}
if (oldOwner.isEmpty() && !newOwner.isEmpty() && _sniAvailable) {
LOG(("Switching to SNI tray icon..."));
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
LOG(("Switching to Qt tray icon..."));
} else {
return;
}
if (trayIcon) {
trayIcon->setContextMenu(0);
trayIcon->deleteLater();
}
trayIcon = nullptr;
if (trayAvailable()) {
psSetupTrayIcon();
} else {
LOG(("System tray is not available."));
}
SkipTaskbar(
windowHandle(),
(Core::App().settings().workMode() == WorkMode::TrayOnly)
&& trayAvailable());
}
void MainWindow::handleAppMenuOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
if (oldOwner.isEmpty() && !newOwner.isEmpty()) {
_appMenuSupported = true;
LOG(("Using D-Bus global menu."));
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
_appMenuSupported = false;
LOG(("Not using D-Bus global menu."));
}
if (_appMenuSupported && _mainMenuExporter) {
RegisterAppMenu(windowHandle(), kMainMenuObjectPath.utf16());
} else {
UnregisterAppMenu(windowHandle());
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::psSetupTrayIcon() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
@@ -892,16 +804,16 @@ void MainWindow::psSetupTrayIcon() {
if (_sniAvailable) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
LOG(("Using SNI tray icon."));
if (!_sniTrayIcon) {
_sniTrayIcon = new StatusNotifierItem(
if (!_private->sniTrayIcon) {
_private->sniTrayIcon = new StatusNotifierItem(
QCoreApplication::applicationName(),
this);
_sniTrayIcon->setTitle(AppName.utf16());
_sniTrayIcon->setContextMenu(trayIconMenu);
setSNITrayIcon(counter, muted);
_private->sniTrayIcon->setTitle(AppName.utf16());
_private->sniTrayIcon->setContextMenu(trayIconMenu);
_private->setSNITrayIcon(counter, muted);
attachToSNITrayIcon();
_private->attachToSNITrayIcon();
}
updateIconCounters();
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -924,11 +836,11 @@ void MainWindow::workmodeUpdated(Core::Settings::WorkMode mode) {
return;
} else if (mode == WorkMode::WindowOnly) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (_sniTrayIcon) {
_sniTrayIcon->setContextMenu(0);
_sniTrayIcon->deleteLater();
if (_private->sniTrayIcon) {
_private->sniTrayIcon->setContextMenu(0);
_private->sniTrayIcon->deleteLater();
}
_sniTrayIcon = nullptr;
_private->sniTrayIcon = nullptr;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (trayIcon) {
@@ -992,8 +904,8 @@ void MainWindow::updateIconCounters() {
}
}
if (_sniTrayIcon) {
setSNITrayIcon(counter, muted);
if (_private->sniTrayIcon) {
_private->setSNITrayIcon(counter, muted);
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -1007,16 +919,6 @@ void MainWindow::initTrayMenuHook() {
_trayIconMenuXEmbed->deleteOnHide(false);
}
#ifdef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::createGlobalMenu() {
}
void MainWindow::updateGlobalMenuHook() {
}
#else // DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::createGlobalMenu() {
const auto ensureWindowShown = [=] {
if (isHidden()) {
@@ -1024,7 +926,8 @@ void MainWindow::createGlobalMenu() {
}
};
psMainMenu = new QMenu(this);
psMainMenu = new QMenuBar(this);
psMainMenu->hide();
auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now));
@@ -1201,14 +1104,6 @@ void MainWindow::createGlobalMenu() {
about->setMenuRole(QAction::AboutQtRole);
_mainMenuExporter = new DBusMenuExporter(
kMainMenuObjectPath.utf16(),
psMainMenu);
if (_appMenuSupported) {
RegisterAppMenu(windowHandle(), kMainMenuObjectPath.utf16());
}
updateGlobalMenu();
}
@@ -1327,8 +1222,6 @@ void MainWindow::updateGlobalMenuHook() {
ForceDisabled(psClearFormat, !markdownEnabled);
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void MainWindow::handleNativeSurfaceChanged(bool exist) {
if (exist) {
SkipTaskbar(
@@ -1336,45 +1229,21 @@ void MainWindow::handleNativeSurfaceChanged(bool exist) {
(Core::App().settings().workMode() == WorkMode::TrayOnly)
&& trayAvailable());
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (_appMenuSupported && _mainMenuExporter) {
if (exist) {
RegisterAppMenu(windowHandle(), kMainMenuObjectPath.utf16());
} else {
UnregisterAppMenu(windowHandle());
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}
MainWindow::~MainWindow() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (_private->dbusConnection) {
if (_sniRegisteredSignalId != 0) {
if (_private->sniRegisteredSignalId != 0) {
_private->dbusConnection->signal_unsubscribe(
_sniRegisteredSignalId);
_private->sniRegisteredSignalId);
}
if (_sniWatcherId != 0) {
if (_private->sniWatcherId != 0) {
_private->dbusConnection->signal_unsubscribe(
_sniWatcherId);
}
if (_appMenuWatcherId != 0) {
_private->dbusConnection->signal_unsubscribe(
_appMenuWatcherId);
_private->sniWatcherId);
}
}
delete _sniTrayIcon;
if (_appMenuSupported) {
UnregisterAppMenu(windowHandle());
}
delete _mainMenuExporter;
delete psMainMenu;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}

View File

@@ -10,16 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_main_window.h"
#include "base/unique_qptr.h"
class QMenuBar;
namespace Ui {
class PopupMenu;
} // namespace Ui
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
class QTemporaryFile;
class DBusMenuExporter;
class StatusNotifierItem;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
namespace Platform {
class MainWindow : public Window::MainWindow {
@@ -70,24 +66,13 @@ protected:
private:
class Private;
friend class Private;
const std::unique_ptr<Private> _private;
bool _sniAvailable = false;
base::unique_qptr<Ui::PopupMenu> _trayIconMenuXEmbed;
void updateIconCounters();
void handleNativeSurfaceChanged(bool exist);
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
StatusNotifierItem *_sniTrayIcon = nullptr;
uint _sniRegisteredSignalId = 0;
uint _sniWatcherId = 0;
uint _appMenuWatcherId = 0;
std::unique_ptr<QTemporaryFile> _trayIconFile;
bool _appMenuSupported = false;
DBusMenuExporter *_mainMenuExporter = nullptr;
QMenu *psMainMenu = nullptr;
QMenuBar *psMainMenu = nullptr;
QAction *psLogout = nullptr;
QAction *psUndo = nullptr;
QAction *psRedo = nullptr;
@@ -108,19 +93,8 @@ private:
QAction *psMonospace = nullptr;
QAction *psClearFormat = nullptr;
void setSNITrayIcon(int counter, bool muted);
void attachToSNITrayIcon();
void handleSNIHostRegistered();
void handleSNIOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner);
void handleAppMenuOwnerChanged(
const QString &service,
const QString &oldOwner,
const QString &newOwner);
void updateIconCounters();
void handleNativeSurfaceChanged(bool exist);
void psLinuxUndo();
void psLinuxRedo();
@@ -136,7 +110,6 @@ private:
void psLinuxStrikeOut();
void psLinuxMonospace();
void psLinuxClearFormat();
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
};

View File

@@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <gio/gio.h>
#include <glibmm.h>
#include <giomm.h>
#include <jemalloc/jemalloc.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -85,12 +86,7 @@ constexpr auto kSnapcraftSettingsInterface = kSnapcraftSettingsService;
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
std::unique_ptr<internal::NotificationServiceWatcher> NSWInstance;
class PortalAutostart : public QWindow {
public:
PortalAutostart(bool start, bool silent = false);
};
PortalAutostart::PortalAutostart(bool start, bool silent) {
void PortalAutostart(bool start, bool silent) {
if (cExeName().isEmpty()) {
return;
}
@@ -149,7 +145,9 @@ PortalAutostart::PortalAutostart(bool start, bool silent) {
+ '/'
+ handleToken;
QEventLoop loop;
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
const auto signalId = connection->signal_subscribe(
[&](
@@ -175,7 +173,7 @@ PortalAutostart::PortalAutostart(bool start, bool silent) {
}
}
loop.quit();
loop->quit();
},
std::string(kXDGDesktopPortalService),
"org.freedesktop.portal.Request",
@@ -199,9 +197,11 @@ PortalAutostart::PortalAutostart(bool start, bool silent) {
std::string(kXDGDesktopPortalService));
if (signalId != 0) {
QGuiApplicationPrivate::showModalWindow(this);
loop.exec();
QGuiApplicationPrivate::hideModalWindow(this);
QWindow window;
QGuiApplicationPrivate::showModalWindow(&window);
loop->run();
g_main_context_pop_thread_default(context->gobj());
QGuiApplicationPrivate::hideModalWindow(&window);
}
} catch (const Glib::Error &e) {
if (!silent) {
@@ -211,12 +211,7 @@ PortalAutostart::PortalAutostart(bool start, bool silent) {
}
}
class SnapDefaultHandler : public QWindow {
public:
SnapDefaultHandler(const QString &protocol);
};
SnapDefaultHandler::SnapDefaultHandler(const QString &protocol) {
void SnapDefaultHandler(const QString &protocol) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
@@ -241,7 +236,9 @@ SnapDefaultHandler::SnapDefaultHandler(const QString &protocol) {
return;
}
QEventLoop loop;
const auto context = Glib::MainContext::create();
const auto loop = Glib::MainLoop::create(context);
g_main_context_push_thread_default(context->gobj());
connection->call(
std::string(kSnapcraftSettingsObjectPath),
@@ -260,13 +257,15 @@ SnapDefaultHandler::SnapDefaultHandler(const QString &protocol) {
QString::fromStdString(e.what())));
}
loop.quit();
loop->quit();
},
std::string(kSnapcraftSettingsService));
QGuiApplicationPrivate::showModalWindow(this);
loop.exec();
QGuiApplicationPrivate::hideModalWindow(this);
QWindow window;
QGuiApplicationPrivate::showModalWindow(&window);
loop->run();
g_main_context_pop_thread_default(context->gobj());
QGuiApplicationPrivate::hideModalWindow(&window);
} catch (const Glib::Error &e) {
LOG(("Snap Default Handler Error: %1").arg(
QString::fromStdString(e.what())));
@@ -404,22 +403,6 @@ bool GenerateDesktopFile(
}
}
void SetGtkScaleFactor() {
const auto integration = GtkIntegration::Instance();
const auto ratio = Core::Sandbox::Instance().devicePixelRatio();
if (!integration || ratio > 1.) {
return;
}
const auto scaleFactor = integration->scaleFactor().value_or(1);
if (scaleFactor == 1) {
return;
}
LOG(("GTK scale factor: %1").arg(scaleFactor));
cSetScreenScale(style::CheckScale(scaleFactor * 100));
}
void SetDarkMode() {
static const auto Inited = [] {
QObject::connect(
@@ -742,6 +725,9 @@ int psFixPrevious() {
namespace Platform {
void start() {
auto backgroundThread = true;
mallctl("background_thread", nullptr, nullptr, &backgroundThread, sizeof(bool));
LOG(("Launcher filename: %1").arg(QGuiApplication::desktopFileName()));
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
@@ -757,11 +743,8 @@ void start() {
Glib::set_prgname(cExeName().toStdString());
Glib::set_application_name(std::string(AppName));
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->prepareEnvironment();
} else {
g_warning("GTK integration is disabled, some features unavailable.");
}
GtkIntegration::Start(GtkIntegration::Type::Base);
GtkIntegration::Start(GtkIntegration::Type::TDesktop);
#ifdef DESKTOP_APP_USE_PACKAGED_RLOTTIE
g_warning(
@@ -958,13 +941,16 @@ bool OpenSystemSettings(SystemSettingsType type) {
namespace ThirdParty {
void start() {
GtkIntegration::Autorestart(GtkIntegration::Type::Base);
GtkIntegration::Autorestart(GtkIntegration::Type::TDesktop);
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->load();
integration->load(GtkIntegration::AllowedBackends());
integration->initializeSettings();
}
if (const auto integration = GtkIntegration::Instance()) {
integration->load();
integration->load(GtkIntegration::AllowedBackends());
}
// wait for interface announce to know if native window frame is supported
@@ -972,7 +958,6 @@ void start() {
integration->waitForInterfaceAnnounce();
}
SetGtkScaleFactor();
crl::async(SetDarkMode);
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION

View File

@@ -75,14 +75,6 @@ bool finished = true;
QMargins simpleMargins, margins;
HICON bigIcon = 0, smallIcon = 0, overlayIcon = 0;
class _PsInitializer {
public:
_PsInitializer() {
Dlls::start();
}
};
_PsInitializer _psInitializer;
BOOL CALLBACK _ActivateProcess(HWND hWnd, LPARAM lParam) {
uint64 &processId(*(uint64*)lParam);
@@ -104,7 +96,7 @@ BOOL CALLBACK _ActivateProcess(HWND hWnd, LPARAM lParam) {
return TRUE;
}
}
} // namespace
void psActivateProcess(uint64 pid) {
if (pid) {
@@ -244,7 +236,6 @@ void start() {
} // namespace ThirdParty
void start() {
Dlls::init();
}
void finish() {

View File

@@ -12,72 +12,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <VersionHelpers.h>
#include <QtCore/QSysInfo>
#include <d3d11.h>
#define LOAD_SYMBOL(lib, name) ::base::Platform::LoadMethod(lib, #name, name)
namespace Platform {
namespace Dlls {
namespace {
using base::Platform::SafeLoadLibrary;
using base::Platform::LoadMethod;
struct SafeIniter {
SafeIniter();
};
void init() {
static bool inited = false;
if (inited) return;
inited = true;
SafeIniter::SafeIniter() {
base::Platform::InitDynamicLibraries();
// Remove the current directory from the DLL search order.
::SetDllDirectory(L"");
const auto list = {
u"dbghelp.dll"_q,
u"dbgcore.dll"_q,
u"propsys.dll"_q,
u"winsta.dll"_q,
u"textinputframework.dll"_q,
u"uxtheme.dll"_q,
u"igdumdim32.dll"_q,
u"amdhdl32.dll"_q,
u"wtsapi32.dll"_q,
u"propsys.dll"_q,
u"combase.dll"_q,
u"dwmapi.dll"_q,
u"rstrtmgr.dll"_q,
u"psapi.dll"_q,
u"user32.dll"_q,
u"d3d11.dll"_q,
u"dxgi.dll"_q,
};
for (const auto &lib : list) {
SafeLoadLibrary(lib);
}
}
// D3D11.DLL
HRESULT (__stdcall *D3D11CreateDevice)(
_In_opt_ IDXGIAdapter* pAdapter,
D3D_DRIVER_TYPE DriverType,
HMODULE Software,
UINT Flags,
_In_reads_opt_(FeatureLevels) CONST D3D_FEATURE_LEVEL* pFeatureLevels,
UINT FeatureLevels,
UINT SDKVersion,
_COM_Outptr_opt_ ID3D11Device** ppDevice,
_Out_opt_ D3D_FEATURE_LEVEL* pFeatureLevel,
_COM_Outptr_opt_ ID3D11DeviceContext** ppImmediateContext);
// DXGI.DLL
HRESULT (__stdcall *CreateDXGIFactory1)(
REFIID riid,
_COM_Outptr_ void **ppFactory);
void start() {
init();
const auto LibShell32 = SafeLoadLibrary(u"shell32.dll"_q);
const auto LibShell32 = LoadLibrary(L"shell32.dll");
LOAD_SYMBOL(LibShell32, SHAssocEnumHandlers);
LOAD_SYMBOL(LibShell32, SHCreateItemFromParsingName);
LOAD_SYMBOL(LibShell32, SHOpenWithDialog);
@@ -86,7 +34,7 @@ void start() {
LOAD_SYMBOL(LibShell32, SHChangeNotify);
LOAD_SYMBOL(LibShell32, SetCurrentProcessExplicitAppUserModelID);
const auto LibUxTheme = SafeLoadLibrary(u"uxtheme.dll"_q);
const auto LibUxTheme = LoadLibrary(L"uxtheme.dll");
LOAD_SYMBOL(LibUxTheme, SetWindowTheme);
//if (IsWindows10OrGreater()) {
// static const auto kSystemVersion = QOperatingSystemVersion::current();
@@ -104,66 +52,27 @@ void start() {
// }
//}
if (IsWindowsVistaOrGreater()) {
const auto LibWtsApi32 = SafeLoadLibrary(u"wtsapi32.dll"_q);
LOAD_SYMBOL(LibWtsApi32, WTSRegisterSessionNotification);
LOAD_SYMBOL(LibWtsApi32, WTSUnRegisterSessionNotification);
const auto LibWtsApi32 = LoadLibrary(L"wtsapi32.dll");
LOAD_SYMBOL(LibWtsApi32, WTSRegisterSessionNotification);
LOAD_SYMBOL(LibWtsApi32, WTSUnRegisterSessionNotification);
const auto LibPropSys = SafeLoadLibrary(u"propsys.dll"_q);
LOAD_SYMBOL(LibPropSys, PropVariantToString);
LOAD_SYMBOL(LibPropSys, PSStringFromPropertyKey);
const auto LibPropSys = LoadLibrary(L"propsys.dll");
LOAD_SYMBOL(LibPropSys, PropVariantToString);
LOAD_SYMBOL(LibPropSys, PSStringFromPropertyKey);
const auto LibDwmApi = SafeLoadLibrary(u"dwmapi.dll"_q);
LOAD_SYMBOL(LibDwmApi, DwmIsCompositionEnabled);
LOAD_SYMBOL(LibDwmApi, DwmSetWindowAttribute);
}
const auto LibDwmApi = LoadLibrary(L"dwmapi.dll");
LOAD_SYMBOL(LibDwmApi, DwmIsCompositionEnabled);
LOAD_SYMBOL(LibDwmApi, DwmSetWindowAttribute);
const auto LibPsApi = SafeLoadLibrary(u"psapi.dll"_q);
const auto LibPsApi = LoadLibrary(L"psapi.dll");
LOAD_SYMBOL(LibPsApi, GetProcessMemoryInfo);
const auto LibUser32 = SafeLoadLibrary(u"user32.dll"_q);
const auto LibUser32 = LoadLibrary(L"user32.dll");
LOAD_SYMBOL(LibUser32, SetWindowCompositionAttribute);
const auto LibD3D11 = SafeLoadLibrary(u"d3d11.dll"_q);
LOAD_SYMBOL(LibD3D11, D3D11CreateDevice);
const auto LibDXGI = SafeLoadLibrary(u"dxgi.dll"_q);
LOAD_SYMBOL(LibDXGI, CreateDXGIFactory1);
}
SafeIniter kSafeIniter;
} // namespace
} // namespace Dlls
} // namespace Platform
HRESULT WINAPI D3D11CreateDevice(
_In_opt_ IDXGIAdapter* pAdapter,
D3D_DRIVER_TYPE DriverType,
HMODULE Software,
UINT Flags,
_In_reads_opt_(FeatureLevels) CONST D3D_FEATURE_LEVEL* pFeatureLevels,
UINT FeatureLevels,
UINT SDKVersion,
_COM_Outptr_opt_ ID3D11Device** ppDevice,
_Out_opt_ D3D_FEATURE_LEVEL* pFeatureLevel,
_COM_Outptr_opt_ ID3D11DeviceContext** ppImmediateContext) {
return Platform::Dlls::D3D11CreateDevice
? Platform::Dlls::D3D11CreateDevice(
pAdapter,
DriverType,
Software,
Flags,
pFeatureLevels,
FeatureLevels,
SDKVersion,
ppDevice,
pFeatureLevel,
ppImmediateContext)
: S_FALSE;
}
HRESULT WINAPI CreateDXGIFactory1(
REFIID riid,
_COM_Outptr_ void **ppFactory) {
return Platform::Dlls::CreateDXGIFactory1
? Platform::Dlls::CreateDXGIFactory1(riid, ppFactory)
: S_FALSE;
}

View File

@@ -22,12 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace Dlls {
void init();
void start();
// KERNEL32.DLL
inline BOOL(__stdcall *SetDllDirectory)(LPCWSTR lpPathName);
// UXTHEME.DLL
inline HRESULT(__stdcall *SetWindowTheme)(
HWND hWnd,

View File

@@ -14,8 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/gl/gl_detection.h"
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
#include "ui/text/format_values.h"
#include "ui/boxes/single_choice_box.h"
#include "boxes/connection_box.h"
#include "boxes/about_box.h"
#include "boxes/confirm_box.h"
@@ -512,6 +514,79 @@ void SetupAnimations(not_null<Ui::VerticalLayout*> container) {
}, container->lifetime());
}
#ifdef Q_OS_WIN
void SetupANGLE(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
using ANGLE = Ui::GL::ANGLE;
const auto options = std::vector{
tr::lng_settings_angle_backend_auto(tr::now),
tr::lng_settings_angle_backend_d3d11(tr::now),
tr::lng_settings_angle_backend_d3d9(tr::now),
tr::lng_settings_angle_backend_d3d11on12(tr::now),
tr::lng_settings_angle_backend_opengl(tr::now),
tr::lng_settings_angle_backend_disabled(tr::now),
};
const auto backendIndex = [] {
if (Core::App().settings().disableOpenGL()) {
return 5;
} else switch (Ui::GL::CurrentANGLE()) {
case ANGLE::Auto: return 0;
case ANGLE::D3D11: return 1;
case ANGLE::D3D9: return 2;
case ANGLE::D3D11on12: return 3;
case ANGLE::OpenGL: return 4;
}
Unexpected("Ui::GL::CurrentANGLE value in SetupANGLE.");
}();
const auto button = AddButtonWithLabel(
container,
tr::lng_settings_angle_backend(),
rpl::single(options[backendIndex]),
st::settingsButton);
button->addClickHandler([=] {
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
const auto save = [=](int index) {
if (index == backendIndex) {
return;
}
const auto confirmed = crl::guard(button, [=] {
const auto nowDisabled = (index == 5);
if (!nowDisabled) {
Ui::GL::ChangeANGLE([&] {
switch (index) {
case 0: return ANGLE::Auto;
case 1: return ANGLE::D3D11;
case 2: return ANGLE::D3D9;
case 3: return ANGLE::D3D11on12;
case 4: return ANGLE::OpenGL;
}
Unexpected("Index in SetupANGLE.");
}());
}
const auto wasDisabled = (backendIndex == 5);
if (nowDisabled != wasDisabled) {
Core::App().settings().setDisableOpenGL(nowDisabled);
Local::writeSettings();
}
App::restart();
});
controller->show(Box<ConfirmBox>(
tr::lng_settings_need_restart(tr::now),
tr::lng_settings_restart_now(tr::now),
confirmed));
};
SingleChoiceBox(box, {
.title = tr::lng_settings_angle_backend(),
.options = options,
.initialSelection = backendIndex,
.callback = save,
});
}));
});
}
#endif // Q_OS_WIN
void SetupOpenGL(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
@@ -550,9 +625,13 @@ void SetupPerformance(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
SetupAnimations(container);
if (!Platform::IsMac()) {
#ifdef Q_OS_WIN
SetupANGLE(controller, container);
#else // Q_OS_WIN
if constexpr (!Platform::IsMac()) {
SetupOpenGL(controller, container);
}
#endif // Q_OS_WIN
}
void SetupSystemIntegration(

View File

@@ -532,28 +532,30 @@ void MainWindow::initSize() {
if (position.w > w) position.w = w;
if (position.h > h) position.h = h;
const auto rightPoint = position.x + position.w;
if (rightPoint > w) {
const auto distance = rightPoint - w;
const auto screenRightPoint = x + w;
if (rightPoint > screenRightPoint) {
const auto distance = rightPoint - screenRightPoint;
const auto newXPos = position.x - distance;
if (newXPos >= x) {
position.x = newXPos;
} else {
position.x = x;
const auto newRightPoint = position.x + position.w;
const auto newDistance = newRightPoint - w;
const auto newDistance = newRightPoint - screenRightPoint;
position.w -= newDistance;
}
}
const auto bottomPoint = position.y + position.h;
if (bottomPoint > h) {
const auto distance = bottomPoint - h;
const auto screenBottomPoint = y + h;
if (bottomPoint > screenBottomPoint) {
const auto distance = bottomPoint - screenBottomPoint;
const auto newYPos = position.y - distance;
if (newYPos >= y) {
position.y = newYPos;
} else {
position.y = y;
const auto newBottomPoint = position.y + position.h;
const auto newDistance = newBottomPoint - h;
const auto newDistance = newBottomPoint - screenBottomPoint;
position.h -= newDistance;
}
}

View File

@@ -635,7 +635,6 @@ RUN echo './configure -prefix '$'\"''$QT_PREFIX'$'\"'' \
-qt-harfbuzz \
-qt-pcre \
-no-icu \
-no-gtk \
-no-feature-xcb-sm \
-no-feature-wayland-server \
-static \

View File

@@ -1,7 +1,7 @@
AppVersion 2008002
AppVersion 2008005
AppVersionStrMajor 2.8
AppVersionStrSmall 2.8.2
AppVersionStr 2.8.2
BetaChannel 0
AppVersionStrSmall 2.8.5
AppVersionStr 2.8.5
BetaChannel 1
AlphaVersion 0
AppVersionOriginal 2.8.2
AppVersionOriginal 2.8.5.beta

View File

@@ -22,7 +22,7 @@ def error(message):
print('[ERROR] ' + message)
finish(1)
if sys.platform == 'win32' and not 'COMSPEC' in os.environ:
if sys.platform == 'win32' and 'COMSPEC' not in os.environ:
error('COMSPEC environment variable is not set.')
executePath = os.getcwd()
@@ -39,9 +39,9 @@ if os.path.isfile(officialTargetFile):
officialTarget = line.strip()
arch = ''
if officialTarget == 'win' or officialTarget == 'uwp':
if officialTarget in ['win', 'uwp']:
arch = 'x86'
elif officialTarget == 'win64' or officialTarget == 'uwp64':
elif officialTarget in ['win64', 'uwp64']:
arch = 'x64'
if officialTarget != '':

View File

@@ -1,3 +1,16 @@
2.8.5 beta (02.07.21)
- Use ANGLE for OpenGL over DirectX 9 / DirectX 11 (Windows).
- Use GTK from a child process (Linux).
2.8.4 (01.07.21)
- Crash fixes in WebView on Windows.
2.8.3 (28.06.21)
- Fix crashes in OpenSSL on macOS.
2.8.2 (27.06.21)
- Attempt to fix random crashes on macOS.

2
cmake

Submodule cmake updated: 1d59503a24...8432257651

View File

@@ -172,6 +172,21 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
SET PATH=%PATH_BACKUP_%
cd ..
git clone https://chromium.googlesource.com/angle/angle
cd angle
git checkout chromium/4472
python scripts/bootstrap.py
gclient sync
git apply ../patches/angle.patch
gn gen out/Debug --args="is_component_build = false is_debug = true target_cpu = \"x64\" is_clang = false enable_iterator_debugging = true angle_enable_swiftshader=false angle_enable_vulkan=false"
gn gen out/Release --args="is_component_build = false is_debug = false target_cpu = \"x64\" is_clang = false enable_iterator_debugging = false angle_enable_swiftshader=false angle_enable_vulkan=false"
ninja -C out/Debug libANGLE_static libGLESv2_static libEGL_static
ninja -C out/Release libANGLE_static libGLESv2_static libEGL_static
SET LibrariesPath=%cd%
git clone git://code.qt.io/qt/qt5.git qt_5_15_2
cd qt_5_15_2
@@ -190,7 +205,16 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
-confirm-license ^
-static ^
-static-runtime ^
-opengl dynamic ^
-opengl es2 -no-angle ^
-I "%LibrariesPath%\angle\include" ^
-D "GL_APICALL=" ^
QMAKE_LIBS_OPENGL_ES2_DEBUG="%LibrariesPath%\angle\out\Debug\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Debug\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib" ^
QMAKE_LIBS_OPENGL_ES2_RELEASE="%LibrariesPath%\angle\out\Release\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Release\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib" ^
-egl ^
-D "EGLAPI=" ^
-D "DESKTOP_APP_QT_STATIC_ANGLE=" ^
QMAKE_LIBS_EGL_DEBUG="%LibrariesPath%\angle\out\Debug\obj\libEGL_static.lib %LibrariesPath%\angle\out\Debug\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Debug\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
QMAKE_LIBS_EGL_RELEASE="%LibrariesPath%\angle\out\Release\obj\libEGL_static.lib %LibrariesPath%\angle\out\Release\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Release\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
-openssl-linked ^
-I "%LibrariesPath%\openssl_1_1_1\include" ^
OPENSSL_LIBS_DEBUG="%LibrariesPath%\openssl_1_1_1\out64.dbg\libssl.lib %LibrariesPath%\openssl_1_1_1\out64.dbg\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^

View File

@@ -172,6 +172,21 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
SET PATH=%PATH_BACKUP_%
cd ..
git clone https://chromium.googlesource.com/angle/angle
cd angle
git checkout chromium/4472
python scripts/bootstrap.py
gclient sync
git apply ../patches/angle.patch
gn gen out/Debug --args="is_component_build = false is_debug = true target_cpu = \"x86\" is_clang = false enable_iterator_debugging = true angle_enable_swiftshader=false angle_enable_vulkan=false"
gn gen out/Release --args="is_component_build = false is_debug = false target_cpu = \"x86\" is_clang = false enable_iterator_debugging = false angle_enable_swiftshader=false angle_enable_vulkan=false"
ninja -C out/Debug libANGLE_static libGLESv2_static libEGL_static
ninja -C out/Release libANGLE_static libGLESv2_static libEGL_static
SET LibrariesPath=%cd%
git clone git://code.qt.io/qt/qt5.git qt_5_15_2
cd qt_5_15_2
@@ -190,7 +205,16 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
-confirm-license ^
-static ^
-static-runtime ^
-opengl dynamic ^
-opengl es2 -no-angle ^
-I "%LibrariesPath%\angle\include" ^
-D "GL_APICALL=" ^
QMAKE_LIBS_OPENGL_ES2_DEBUG="%LibrariesPath%\angle\out\Debug\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Debug\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib" ^
QMAKE_LIBS_OPENGL_ES2_RELEASE="%LibrariesPath%\angle\out\Release\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Release\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib" ^
-egl ^
-D "EGLAPI=" ^
-D "DESKTOP_APP_QT_STATIC_ANGLE=" ^
QMAKE_LIBS_EGL_DEBUG="%LibrariesPath%\angle\out\Debug\obj\libEGL_static.lib %LibrariesPath%\angle\out\Debug\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Debug\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
QMAKE_LIBS_EGL_RELEASE="%LibrariesPath%\angle\out\Release\obj\libEGL_static.lib %LibrariesPath%\angle\out\Release\obj\libGLESv2_static.lib %LibrariesPath%\angle\out\Release\obj\libANGLE_static.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
-openssl-linked ^
-I "%LibrariesPath%\openssl_1_1_1\include" ^
OPENSSL_LIBS_DEBUG="%LibrariesPath%\openssl_1_1_1\out32.dbg\libssl.lib %LibrariesPath%\openssl_1_1_1\out32.dbg\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^

View File

@@ -92,8 +92,8 @@ Go to ***BuildPath*** and run
git clone https://github.com/openssl/openssl openssl_1_1_1
cd openssl_1_1_1
git checkout OpenSSL_1_1_1k
./Configure --prefix=/usr/local/macos no-tests darwin64-x86_64-cc -static -g3 $MIN_VER
git checkout OpenSSL_1_1_1-stable
./Configure --prefix=/usr/local/macos no-shared no-tests darwin64-x86_64-cc $MIN_VER
make build_libs $MAKE_THREADS_CNT
cd ..

View File

@@ -117,7 +117,6 @@ parts:
- -DTDESKTOP_API_ID=611335
- -DTDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c
- -DDESKTOP_APP_USE_PACKAGED_LAZY=ON
- -DDESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES=OFF
- -DTDESKTOP_LAUNCHER_BASENAME=telegram-desktop_telegram-desktop
override-pull: |
snapcraftctl pull
@@ -303,6 +302,7 @@ parts:
- libfreetype-dev
- libgl-dev
- libglib2.0-dev
- libgtk-3-dev
- libharfbuzz-dev
- libicu-dev
- libpcre2-dev
@@ -338,6 +338,7 @@ parts:
- libfreetype6
- libgl1
- libglib2.0-0
- libgtk-3-0
- libharfbuzz0b
- libicu66
- libpcre2-16-0
@@ -398,7 +399,6 @@ parts:
-release \
-opensource \
-confirm-license \
-no-gtk \
-no-feature-xcb-sm \
-no-feature-xcomposite-egl \
-no-feature-xcomposite-glx \
@@ -420,6 +420,8 @@ parts:
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/bin
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/mkspecs
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/examples
# Allow tdesktop's custom try-portal-and-fallback logic to work
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins/platformthemes/libqxdgdesktopportal.so
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/*.a
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/*.la
- -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/*.prl