diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 7586a3241..349b8b481 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -71,7 +71,7 @@ jobs:
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
- libvorbis-dev libenchant-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
+ libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libatspi2.0-dev \
libxcb-render-util0-dev libxcb-util0-dev libxcb-xkb-dev libxrender-dev \
libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libegl1-mesa-dev \
@@ -323,7 +323,7 @@ jobs:
run: |
cd $LibrariesPath
- git clone -b openal-soft-1.19.1 --depth=1 $GIT/kcat/openal-soft.git
+ git clone -b openal-soft-1.20.1 --depth=1 $GIT/kcat/openal-soft.git
cd openal-soft/build
cmake -D LIBTYPE:STRING=STATIC ..
make -j$(nproc)
@@ -342,14 +342,15 @@ jobs:
run: |
cd $LibrariesPath
+ opensslDir=openssl_${OPENSSL_VER}
git clone -b OpenSSL_${OPENSSL_VER}-stable --depth=1 \
- $GIT/openssl/openssl openssl_${OPENSSL_VER}
- cd openssl_${OPENSSL_VER}
+ $GIT/openssl/openssl $opensslDir
+ cd $opensslDir
./config --prefix=$LibrariesPath/openssl-cache
make -j$(nproc)
- sudo make install
+ sudo make install_sw
cd ..
- rm -rf openssl_${OPENSSL_VER}
+ rm -rf $opensslDir
- name: OpenSSL install.
run: |
cd $LibrariesPath
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index 53beba810..896e1061c 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -26,11 +26,11 @@ jobs:
GIT: "https://github.com"
PREFIX: "/usr/local/macos"
MACOSX_DEPLOYMENT_TARGET: "10.12"
- XZ: "xz-5.0.5"
+ XZ: "xz-5.2.4"
QT: "5_12_5"
OPENSSL_VER: "1_1_1"
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
- LIBICONV_VER: "libiconv-1.15"
+ LIBICONV_VER: "libiconv-1.16"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "2"
diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml
index ec73a14d8..1dbe5b461 100644
--- a/.github/workflows/snap.yml
+++ b/.github/workflows/snap.yml
@@ -47,6 +47,10 @@ jobs:
md5cache=$(md5sum CMAKE_CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CMAKE_CACHE_KEY::$md5cache
+ awk -v RS="" -v ORS="\n\n" '/^ ffmpeg:/' snap/snapcraft.yaml > FFMPEG_CACHE_KEY.txt
+ md5cache=$(md5sum FFMPEG_CACHE_KEY.txt | cut -c -32)
+ echo ::set-env name=FFMPEG_CACHE_KEY::$md5cache
+
- name: CMake cache.
id: cache-cmake
uses: actions/cache@v1
@@ -58,6 +62,17 @@ jobs:
if: steps.cache-cmake.outputs.cache-hit != 'true'
run: snapcraft build --destructive-mode cmake
+ - name: FFmpeg cache.
+ id: cache-ffmpeg
+ uses: actions/cache@v1
+ with:
+ path: parts/ffmpeg
+ key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-${{ env.FFMPEG_CACHE_KEY }}
+
+ - name: FFmpeg build.
+ if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
+ run: snapcraft build --destructive-mode ffmpeg
+
- name: Kotatogram Desktop snap build.
if: env.ONLY_CACHE == 'false'
run: snapcraft --destructive-mode
@@ -80,5 +95,5 @@ jobs:
- name: Remove unneeded directories for cache.
run: |
- rm -rf parts/cmake/{build,src,ubuntu}
- rm -rf parts/cmake/state/{stage,prime}
+ rm -rf parts/{cmake,ffmpeg}/{build,src,ubuntu}
+ rm -rf parts/{cmake,ffmpeg}/state/{stage,prime}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d427ad059..645d886a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,9 @@ project(Telegram
)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Telegram)
+get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
+get_filename_component(submodules_loc "Telegram" REALPATH)
+
include(cmake/variables.cmake)
include(cmake/nice_target_sources.cmake)
include(cmake/target_link_static_libraries.cmake)
diff --git a/README.md b/README.md
index 4e2f58edc..67b281f1a 100644
--- a/README.md
+++ b/README.md
@@ -39,4 +39,4 @@ Full list of features will rewritten later, for now you can use one of `control-
[flatpak]: https://flathub.org/apps/details/io.github.kotatogram
[changelog]: https://github.com/kotatogram/kotatogram-desktop/blob/dev/kotatogram_changes.txt
[preview_image]: https://github.com/kotatogram/kotatogram-desktop/blob/dev/docs/assets/ktg_preview.png "Preview of Kotatogram Desktop"
-[preview_image_url]: https://github.com/kotatogram/kotatogram-desktop/blob/dev/docs/assets/ktg_preview.png
\ No newline at end of file
+[preview_image_url]: https://github.com/kotatogram/kotatogram-desktop/blob/dev/docs/assets/ktg_preview.png
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 4ae453987..2a6da282f 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -94,6 +94,7 @@ PRIVATE
desktop-app::external_lz4
desktop-app::external_rlottie
desktop-app::external_zlib
+ desktop-app::external_minizip
desktop-app::external_qt
desktop-app::external_qr_code_generator
desktop-app::external_crash_reports
@@ -1219,14 +1220,15 @@ endif()
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
include(GNUInstallDirs)
+ configure_file("../lib/xdg/kotatogramdesktop.appdata.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/kotatogramdesktop.appdata.xml" @ONLY)
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
- install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon48.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "kotatogram.png")
- install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "kotatogram.png")
+ install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon48.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
+ install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
install(FILES "../lib/xdg/kotatogramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop")
- install(FILES "../lib/xdg/kotatogramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml")
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kotatogramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml")
endif()
diff --git a/Telegram/Patches/ffmpeg.diff b/Telegram/Patches/ffmpeg.diff
new file mode 100644
index 000000000..b5ab619fe
--- /dev/null
+++ b/Telegram/Patches/ffmpeg.diff
@@ -0,0 +1,225 @@
+diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile
+index 00f93bf59f..52da7036f3 100644
+--- a/libavcodec/aarch64/Makefile
++++ b/libavcodec/aarch64/Makefile
+@@ -6,6 +6,7 @@ OBJS-$(CONFIG_H264DSP) += aarch64/h264dsp_init_aarch64.o
+ OBJS-$(CONFIG_H264PRED) += aarch64/h264pred_init.o
+ OBJS-$(CONFIG_H264QPEL) += aarch64/h264qpel_init_aarch64.o
+ OBJS-$(CONFIG_HPELDSP) += aarch64/hpeldsp_init_aarch64.o
++OBJS-$(CONFIG_IDCTDSP) += aarch64/idctdsp_init_aarch64.o
+ OBJS-$(CONFIG_MPEGAUDIODSP) += aarch64/mpegaudiodsp_init.o
+ OBJS-$(CONFIG_NEON_CLOBBER_TEST) += aarch64/neontest.o
+ OBJS-$(CONFIG_VIDEODSP) += aarch64/videodsp_init.o
+@@ -21,6 +22,7 @@ OBJS-$(CONFIG_VC1DSP) += aarch64/vc1dsp_init_aarch64.o
+ OBJS-$(CONFIG_VORBIS_DECODER) += aarch64/vorbisdsp_init.o
+ OBJS-$(CONFIG_VP9_DECODER) += aarch64/vp9dsp_init_10bpp_aarch64.o \
+ aarch64/vp9dsp_init_12bpp_aarch64.o \
++ aarch64/vp9mc_aarch64.o \
+ aarch64/vp9dsp_init_aarch64.o
+
+ # ARMv8 optimizations
+@@ -41,8 +43,7 @@ NEON-OBJS-$(CONFIG_H264PRED) += aarch64/h264pred_neon.o
+ NEON-OBJS-$(CONFIG_H264QPEL) += aarch64/h264qpel_neon.o \
+ aarch64/hpeldsp_neon.o
+ NEON-OBJS-$(CONFIG_HPELDSP) += aarch64/hpeldsp_neon.o
+-NEON-OBJS-$(CONFIG_IDCTDSP) += aarch64/idctdsp_init_aarch64.o \
+- aarch64/simple_idct_neon.o
++NEON-OBJS-$(CONFIG_IDCTDSP) += aarch64/simple_idct_neon.o
+ NEON-OBJS-$(CONFIG_MDCT) += aarch64/mdct_neon.o
+ NEON-OBJS-$(CONFIG_MPEGAUDIODSP) += aarch64/mpegaudiodsp_neon.o
+ NEON-OBJS-$(CONFIG_VP8DSP) += aarch64/vp8dsp_neon.o
+diff --git a/libavcodec/aarch64/idctdsp_init_aarch64.c b/libavcodec/aarch64/idctdsp_init_aarch64.c
+index 0406e60830..742a3372e3 100644
+--- a/libavcodec/aarch64/idctdsp_init_aarch64.c
++++ b/libavcodec/aarch64/idctdsp_init_aarch64.c
+@@ -21,6 +21,8 @@
+ */
+
+ #include "libavutil/attributes.h"
++#include "libavutil/cpu.h"
++#include "libavutil/arm/cpu.h"
+ #include "libavcodec/avcodec.h"
+ #include "libavcodec/idctdsp.h"
+ #include "idct.h"
+@@ -28,7 +30,9 @@
+ av_cold void ff_idctdsp_init_aarch64(IDCTDSPContext *c, AVCodecContext *avctx,
+ unsigned high_bit_depth)
+ {
+- if (!avctx->lowres && !high_bit_depth) {
++ int cpu_flags = av_get_cpu_flags();
++
++ if (have_neon(cpu_flags) && !avctx->lowres && !high_bit_depth) {
+ if (avctx->idct_algo == FF_IDCT_AUTO ||
+ avctx->idct_algo == FF_IDCT_SIMPLEAUTO ||
+ avctx->idct_algo == FF_IDCT_SIMPLENEON) {
+diff --git a/libavcodec/aarch64/vp9mc_16bpp_neon.S b/libavcodec/aarch64/vp9mc_16bpp_neon.S
+index cac6428709..53b372c262 100644
+--- a/libavcodec/aarch64/vp9mc_16bpp_neon.S
++++ b/libavcodec/aarch64/vp9mc_16bpp_neon.S
+@@ -25,31 +25,6 @@
+ // const uint8_t *ref, ptrdiff_t ref_stride,
+ // int h, int mx, int my);
+
+-function ff_vp9_copy128_aarch64, export=1
+-1:
+- ldp x5, x6, [x2]
+- ldp x7, x8, [x2, #16]
+- stp x5, x6, [x0]
+- ldp x9, x10, [x2, #32]
+- stp x7, x8, [x0, #16]
+- subs w4, w4, #1
+- ldp x11, x12, [x2, #48]
+- stp x9, x10, [x0, #32]
+- stp x11, x12, [x0, #48]
+- ldp x5, x6, [x2, #64]
+- ldp x7, x8, [x2, #80]
+- stp x5, x6, [x0, #64]
+- ldp x9, x10, [x2, #96]
+- stp x7, x8, [x0, #80]
+- ldp x11, x12, [x2, #112]
+- stp x9, x10, [x0, #96]
+- stp x11, x12, [x0, #112]
+- add x2, x2, x3
+- add x0, x0, x1
+- b.ne 1b
+- ret
+-endfunc
+-
+ function ff_vp9_avg64_16_neon, export=1
+ mov x5, x0
+ sub x1, x1, #64
+diff --git a/libavcodec/aarch64/vp9mc_aarch64.S b/libavcodec/aarch64/vp9mc_aarch64.S
+new file mode 100644
+index 0000000000..f17a8cf04a
+--- /dev/null
++++ b/libavcodec/aarch64/vp9mc_aarch64.S
+@@ -0,0 +1,81 @@
++/*
++ * Copyright (c) 2016 Google Inc.
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * FFmpeg is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include "libavutil/aarch64/asm.S"
++
++// All public functions in this file have the following signature:
++// typedef void (*vp9_mc_func)(uint8_t *dst, ptrdiff_t dst_stride,
++// const uint8_t *ref, ptrdiff_t ref_stride,
++// int h, int mx, int my);
++
++function ff_vp9_copy128_aarch64, export=1
++1:
++ ldp x5, x6, [x2]
++ ldp x7, x8, [x2, #16]
++ stp x5, x6, [x0]
++ ldp x9, x10, [x2, #32]
++ stp x7, x8, [x0, #16]
++ subs w4, w4, #1
++ ldp x11, x12, [x2, #48]
++ stp x9, x10, [x0, #32]
++ stp x11, x12, [x0, #48]
++ ldp x5, x6, [x2, #64]
++ ldp x7, x8, [x2, #80]
++ stp x5, x6, [x0, #64]
++ ldp x9, x10, [x2, #96]
++ stp x7, x8, [x0, #80]
++ ldp x11, x12, [x2, #112]
++ stp x9, x10, [x0, #96]
++ stp x11, x12, [x0, #112]
++ add x2, x2, x3
++ add x0, x0, x1
++ b.ne 1b
++ ret
++endfunc
++
++function ff_vp9_copy64_aarch64, export=1
++1:
++ ldp x5, x6, [x2]
++ ldp x7, x8, [x2, #16]
++ stp x5, x6, [x0]
++ ldp x9, x10, [x2, #32]
++ stp x7, x8, [x0, #16]
++ subs w4, w4, #1
++ ldp x11, x12, [x2, #48]
++ stp x9, x10, [x0, #32]
++ stp x11, x12, [x0, #48]
++ add x2, x2, x3
++ add x0, x0, x1
++ b.ne 1b
++ ret
++endfunc
++
++function ff_vp9_copy32_aarch64, export=1
++1:
++ ldp x5, x6, [x2]
++ ldp x7, x8, [x2, #16]
++ stp x5, x6, [x0]
++ subs w4, w4, #1
++ stp x7, x8, [x0, #16]
++ add x2, x2, x3
++ add x0, x0, x1
++ b.ne 1b
++ ret
++endfunc
+diff --git a/libavcodec/aarch64/vp9mc_neon.S b/libavcodec/aarch64/vp9mc_neon.S
+index f67624ca04..abf2bae9db 100644
+--- a/libavcodec/aarch64/vp9mc_neon.S
++++ b/libavcodec/aarch64/vp9mc_neon.S
+@@ -25,23 +25,6 @@
+ // const uint8_t *ref, ptrdiff_t ref_stride,
+ // int h, int mx, int my);
+
+-function ff_vp9_copy64_aarch64, export=1
+-1:
+- ldp x5, x6, [x2]
+- ldp x7, x8, [x2, #16]
+- stp x5, x6, [x0]
+- ldp x9, x10, [x2, #32]
+- stp x7, x8, [x0, #16]
+- subs w4, w4, #1
+- ldp x11, x12, [x2, #48]
+- stp x9, x10, [x0, #32]
+- stp x11, x12, [x0, #48]
+- add x2, x2, x3
+- add x0, x0, x1
+- b.ne 1b
+- ret
+-endfunc
+-
+ function ff_vp9_avg64_neon, export=1
+ mov x5, x0
+ 1:
+@@ -64,19 +47,6 @@ function ff_vp9_avg64_neon, export=1
+ ret
+ endfunc
+
+-function ff_vp9_copy32_aarch64, export=1
+-1:
+- ldp x5, x6, [x2]
+- ldp x7, x8, [x2, #16]
+- stp x5, x6, [x0]
+- subs w4, w4, #1
+- stp x7, x8, [x0, #16]
+- add x2, x2, x3
+- add x0, x0, x1
+- b.ne 1b
+- ret
+-endfunc
+-
+ function ff_vp9_avg32_neon, export=1
+ 1:
+ ld1 {v2.16b, v3.16b}, [x2], x3
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index afc2e7528..67046818f 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2239,6 +2239,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_linux_menu_undo" = "Undo";
"lng_linux_menu_redo" = "Redo";
+"lng_linux_menu_tools" = "Tools";
+"lng_linux_menu_help" = "Help";
"lng_linux_no_audio_prefs" = "You don't have any audio configuration applications installed.";
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 474eae12b..00a57955b 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -9,7 +9,7 @@
+ Version="1.9.20.0" />
Telegram Desktop
Telegram FZ-LLC
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index a0feaa15b..5dbea9760 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -1503,7 +1503,8 @@ void ApiWrap::applyLastParticipantsList(
channel->mgInfo->lastAdmins.clear();
channel->mgInfo->lastRestricted.clear();
channel->mgInfo->lastParticipants.clear();
- channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate;
+ channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate
+ | MegagroupInfo::LastParticipantsOnceReceived;
auto botStatus = channel->mgInfo->botStatus;
const auto emptyAdminRights = MTP_chatAdminRights(MTP_flags(0));
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index 2a5d03d52..9e9e4603d 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -206,8 +206,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (_chat) {
maxListSize += (_chat->participants.empty() ? _chat->lastAuthors.size() : _chat->participants.size());
} else if (_channel && _channel->isMegagroup()) {
- if (_channel->mgInfo->lastParticipants.empty() || _channel->lastParticipantsCountOutdated()) {
- } else {
+ if (!_channel->lastParticipantsRequestNeeded()) {
maxListSize += _channel->mgInfo->lastParticipants.size();
}
}
@@ -274,7 +273,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
}
} else if (_channel && _channel->isMegagroup()) {
QMultiMap ordered;
- if (_channel->mgInfo->lastParticipants.empty() || _channel->lastParticipantsCountOutdated()) {
+ if (_channel->lastParticipantsRequestNeeded()) {
Auth().api().requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
diff --git a/Telegram/SourceFiles/chat_helpers/spellchecker_common.cpp b/Telegram/SourceFiles/chat_helpers/spellchecker_common.cpp
index ee4b0d7c6..881a495dc 100644
--- a/Telegram/SourceFiles/chat_helpers/spellchecker_common.cpp
+++ b/Telegram/SourceFiles/chat_helpers/spellchecker_common.cpp
@@ -133,27 +133,22 @@ void DownloadDictionaryInBackground(
const auto id = langs[counter];
counter++;
const auto destroyer = [=] {
- // This is a temporary workaround.
- const auto copyId = id;
- const auto copyLangs = langs;
- const auto copySession = session;
- const auto copyCounter = counter;
BackgroundLoader = nullptr;
BackgroundLoaderChanged.fire(0);
- if (DictionaryExists(copyId)) {
- auto dicts = copySession->settings().dictionariesEnabled();
- if (!ranges::contains(dicts, copyId)) {
- dicts.push_back(copyId);
- copySession->settings().setDictionariesEnabled(std::move(dicts));
- copySession->saveSettingsDelayed();
+ if (DictionaryExists(id)) {
+ auto dicts = session->settings().dictionariesEnabled();
+ if (!ranges::contains(dicts, id)) {
+ dicts.push_back(id);
+ session->settings().setDictionariesEnabled(std::move(dicts));
+ session->saveSettingsDelayed();
}
}
- if (copyCounter >= copyLangs.size()) {
+ if (counter >= langs.size()) {
return;
}
- DownloadDictionaryInBackground(copySession, copyCounter, copyLangs);
+ DownloadDictionaryInBackground(session, counter, langs);
};
if (DictionaryExists(id)) {
destroyer();
@@ -194,20 +189,21 @@ DictLoader::DictLoader(
}
void DictLoader::unpack(const QString &path) {
- Expects(_destroyCallback);
crl::async([=] {
const auto success = Spellchecker::UnpackDictionary(path, id());
if (success) {
QFile(path).remove();
+ destroy();
+ return;
}
- crl::on_main(success ? _destroyCallback : [=] { fail(); });
+ crl::on_main([=] { fail(); });
});
}
void DictLoader::destroy() {
Expects(_destroyCallback);
- _destroyCallback();
+ crl::on_main(_destroyCallback);
}
void DictLoader::fail() {
diff --git a/Telegram/SourceFiles/chat_helpers/spellchecker_common.h b/Telegram/SourceFiles/chat_helpers/spellchecker_common.h
index 203c4e047..f806ceb08 100644
--- a/Telegram/SourceFiles/chat_helpers/spellchecker_common.h
+++ b/Telegram/SourceFiles/chat_helpers/spellchecker_common.h
@@ -60,6 +60,7 @@ private:
void unpack(const QString &path) override;
void fail() override;
+ // Be sure to always call it in the main thread.
Fn _destroyCallback;
rpl::lifetime _lifetime;
diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp
index 783dd333f..e0bfcf8b7 100644
--- a/Telegram/SourceFiles/core/changelogs.cpp
+++ b/Telegram/SourceFiles/core/changelogs.cpp
@@ -18,33 +18,6 @@ namespace {
std::map BetaLogs() {
return {
- {
- 1008005,
- "\xE2\x80\xA2 Create new themes based on your color "
- "and wallpaper choices.\n"
-
- "\xE2\x80\xA2 Share your themes with other users via links.\n"
-
- "\xE2\x80\xA2 Update your theme for all its users "
- "when you change something.\n"
- },
- {
- 1009000,
- "\xE2\x80\xA2 System spell checker on Windows 8+ and macOS 10.12+.\n"
- },
- {
- 1009002,
- "\xE2\x80\xA2 Videos in chats start playing automatically.\n"
-
- "\xE2\x80\xA2 Resume playback from where you left off "
- "when watching long videos.\n"
-
- "\xE2\x80\xA2 Control videos, GIFs and round video messages "
- "automatic playback in "
- "Settings > Advanced > Automatic media download.\n"
-
- "\xE2\x80\xA2 Spell checker on Linux using Enchant.\n"
- },
{
1009010,
"\xE2\x80\xA2 Switch to the Picture-in-Picture mode "
@@ -63,12 +36,19 @@ std::map BetaLogs() {
"\xE2\x80\xA2 Bug fixes and other minor improvements."
},
-
{
1009017,
"\xE2\x80\xA2 Spell checker on Windows 7.\n"
"\xE2\x80\xA2 Bug fixes and other minor improvements."
+ },
+ {
+ 1009020,
+ "\xE2\x80\xA2 Fix crash in shared links search.\n"
+
+ "\xE2\x80\xA2 Fix blurred thumbnails in albums with video files.\n"
+
+ "\xE2\x80\xA2 Fix a possible crash in animated stickers rendering."
}
};
};
diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp
index 551f190ad..3d20b5837 100644
--- a/Telegram/SourceFiles/core/launcher.cpp
+++ b/Telegram/SourceFiles/core/launcher.cpp
@@ -247,6 +247,7 @@ void Launcher::init() {
prepareSettings();
QApplication::setApplicationName(qsl("KotatogramDesktop"));
+ QApplication::setApplicationDisplayName(AppName.utf16());
#if defined(Q_OS_LINUX) && QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
QApplication::setDesktopFileName(Platform::GetLauncherFilename());
diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp
index bd43415c7..46c967b4f 100644
--- a/Telegram/SourceFiles/core/sandbox.cpp
+++ b/Telegram/SourceFiles/core/sandbox.cpp
@@ -274,7 +274,7 @@ void Sandbox::socketError(QLocalSocket::LocalSocketError e) {
psCheckLocalSocket(_localServerName);
if (!_localServer.listen(_localServerName)) {
- LOG(("Failed to start listening to %1 server, error %2").arg(_localServerName).arg(int(_localServer.serverError())));
+ LOG(("Failed to start listening to %1 server: %2").arg(_localServerName).arg(_localServer.errorString()));
return App::quit();
}
#endif // !Q_OS_WINRT
diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp
index 2eae51e78..08a7956f0 100644
--- a/Telegram/SourceFiles/data/data_channel.cpp
+++ b/Telegram/SourceFiles/data/data_channel.cpp
@@ -341,6 +341,20 @@ bool ChannelData::isGroupAdmin(not_null user) const {
return false;
}
+bool ChannelData::lastParticipantsRequestNeeded() const {
+ if (!mgInfo) {
+ return false;
+ } else if (mgInfo->lastParticipantsCount == membersCount()) {
+ mgInfo->lastParticipantsStatus
+ &= ~MegagroupInfo::LastParticipantsCountOutdated;
+ }
+ return mgInfo->lastParticipants.empty()
+ || !(mgInfo->lastParticipantsStatus
+ & MegagroupInfo::LastParticipantsOnceReceived)
+ || (mgInfo->lastParticipantsStatus
+ & MegagroupInfo::LastParticipantsCountOutdated);
+}
+
QString ChannelData::adminRank(not_null user) const {
if (!isGroupAdmin(user)) {
return QString();
diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h
index 6f71e6e7d..7e1995ed4 100644
--- a/Telegram/SourceFiles/data/data_channel.h
+++ b/Telegram/SourceFiles/data/data_channel.h
@@ -73,6 +73,7 @@ public:
enum LastParticipantsStatus {
LastParticipantsUpToDate = 0x00,
+ LastParticipantsOnceReceived = 0x01,
LastParticipantsCountOutdated = 0x02,
};
mutable int lastParticipantsStatus = LastParticipantsUpToDate;
@@ -219,21 +220,8 @@ public:
void markForbidden();
[[nodiscard]] bool isGroupAdmin(not_null user) const;
+ [[nodiscard]] bool lastParticipantsRequestNeeded() const;
[[nodiscard]] QString adminRank(not_null user) const;
-
- [[nodiscard]] bool lastParticipantsCountOutdated() const {
- if (!mgInfo
- || !(mgInfo->lastParticipantsStatus
- & MegagroupInfo::LastParticipantsCountOutdated)) {
- return false;
- }
- if (mgInfo->lastParticipantsCount == membersCount()) {
- mgInfo->lastParticipantsStatus
- &= ~MegagroupInfo::LastParticipantsCountOutdated;
- return false;
- }
- return true;
- }
[[nodiscard]] bool isMegagroup() const {
return flags() & MTPDchannel::Flag::f_megagroup;
}
diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp
index 56b6d5ff6..9603ba960 100644
--- a/Telegram/SourceFiles/data/data_document.cpp
+++ b/Telegram/SourceFiles/data/data_document.cpp
@@ -42,7 +42,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
-constexpr auto kMemoryForCache = 32 * 1024 * 1024;
+// Updated Mar 3, 2020: Increase the size of the memory cache for media, to prevent items still being displayed from being unloaded.
+constexpr auto kMemoryForCache = 128 * 1024 * 1024; // was 32, updated to 128
const auto kAnimatedStickerDimensions = QSize(512, 512);
using FilePathResolve = DocumentData::FilePathResolve;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index ea0921999..6396fd98b 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -5171,6 +5171,8 @@ int HistoryWidget::countInitialScrollTop() {
}
void HistoryWidget::createUnreadBarIfBelowVisibleArea(int withScrollTop) {
+ Expects(_history != nullptr);
+
if (_history->unreadBar()) {
return;
}
@@ -5292,7 +5294,7 @@ void HistoryWidget::updateHistoryGeometry(
newScrollTop = countInitialScrollTop();
_historyInited = true;
_scrollToAnimation.stop();
- } else if (wasAtBottom && !loadedDown) {
+ } else if (wasAtBottom && !loadedDown && !_history->unreadBar()) {
newScrollTop = countAutomaticScrollTop();
} else {
newScrollTop = std::min(
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 0b0f7458d..f07fe091d 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -280,6 +280,16 @@ bool Element::isUnderCursor() const {
return _delegate->elementUnderCursor(this);
}
+bool Element::isLastAndSelfMessage() const {
+ if (!hasOutLayout()) {
+ return false;
+ }
+ if (const auto last = data()->_history->lastMessage()) {
+ return last == data();
+ }
+ return false;
+}
+
void Element::setPendingResize() {
_flags |= Flag::NeedsResize;
if (_context == Context::History) {
diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h
index ced627482..b1226419f 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.h
+++ b/Telegram/SourceFiles/history/view/history_view_element.h
@@ -158,6 +158,8 @@ public:
bool pendingResize() const;
bool isUnderCursor() const;
+ bool isLastAndSelfMessage() const;
+
bool isAttachedToPrevious() const;
bool isAttachedToNext() const;
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index b9678e3cf..f0741d4a0 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -896,7 +896,7 @@ void TopBarWidget::updateOnlineDisplay() {
}
} else if (const auto channel = _activeChat.peer()->asChannel()) {
if (channel->isMegagroup() && channel->membersCount() > 0 && channel->membersCount() <= Global::ChatSizeMax()) {
- if (channel->mgInfo->lastParticipants.empty() || channel->lastParticipantsCountOutdated()) {
+ if (channel->lastParticipantsRequestNeeded()) {
session().api().requestLastParticipants(channel);
}
const auto self = session().user();
diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
index f063d9f92..2159b0700 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
@@ -1180,7 +1180,7 @@ void Gif::validateGroupedCache(
const auto height = geometry.height();
const auto options = Option::Smooth
| Option::RoundedLarge
- | (blur ? Option(0) : Option::Blurred)
+ | (blur ? Option::Blurred : Option(0))
| ((corners & RectPart::TopLeft) ? Option::RoundedTopLeft : Option::None)
| ((corners & RectPart::TopRight) ? Option::RoundedTopRight : Option::None)
| ((corners & RectPart::BottomLeft) ? Option::RoundedBottomLeft : Option::None)
@@ -1494,7 +1494,8 @@ bool Gif::dataLoaded() const {
bool Gif::needInfoDisplay() const {
return _parent->data()->isSending()
|| _data->uploading()
- || _parent->isUnderCursor();
+ || _parent->isUnderCursor()
+ || _parent->isLastAndSelfMessage();
}
bool Gif::needCornerStatusDisplay() const {
diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp
index 166ab327e..a754e985d 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp
@@ -464,7 +464,9 @@ bool GroupedMedia::computeNeedBubble() const {
}
bool GroupedMedia::needInfoDisplay() const {
- return (_parent->data()->id < 0 || _parent->isUnderCursor());
+ return (_parent->data()->id < 0
+ || _parent->isUnderCursor()
+ || _parent->isLastAndSelfMessage());
}
} // namespace HistoryView
diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp
index dab25d4b1..5e246181a 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp
@@ -435,6 +435,7 @@ bool UnwrappedMedia::needInfoDisplay() const {
return (_parent->data()->id < 0)
|| (_parent->isUnderCursor())
|| (_parent->displayRightAction())
+ || (_parent->isLastAndSelfMessage())
|| (_parent->hasOutLayout()
&& !Adaptive::ChatWide()
&& _content->alwaysShowOutTimestamp());
diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
index 6ff29e3ab..938b2d74c 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
@@ -523,7 +523,9 @@ bool Photo::dataLoaded() const {
}
bool Photo::needInfoDisplay() const {
- return (_parent->data()->id < 0 || _parent->isUnderCursor());
+ return (_parent->data()->id < 0
+ || _parent->isUnderCursor()
+ || _parent->isLastAndSelfMessage());
}
void Photo::validateGroupedCache(
diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp
index c1234c4da..2ff60a1be 100644
--- a/Telegram/SourceFiles/media/audio/media_audio.cpp
+++ b/Telegram/SourceFiles/media/audio/media_audio.cpp
@@ -82,7 +82,7 @@ bool PlaybackErrorHappened() {
void EnumeratePlaybackDevices() {
auto deviceNames = QStringList();
- auto devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
+ auto devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
Assert(devices != nullptr);
while (*devices != 0) {
auto deviceName8Bit = QByteArray(devices);
@@ -92,7 +92,7 @@ void EnumeratePlaybackDevices() {
}
LOG(("Audio Playback Devices: %1").arg(deviceNames.join(';')));
- if (auto device = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER)) {
+ if (auto device = alcGetString(nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER)) {
LOG(("Audio Playback Default Device: %1").arg(QString::fromLocal8Bit(device)));
} else {
LOG(("Audio Playback Default Device: (null)"));
diff --git a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp
index c3b0dbd68..06b4ae259 100644
--- a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp
+++ b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp
@@ -62,16 +62,12 @@ Instance::Instance() : _inner(new Inner(&_thread)) {
void Instance::check() {
_available = false;
- if (auto defaultDevice = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)) {
- if (auto device = alcCaptureOpenDevice(defaultDevice, kCaptureFrequency, AL_FORMAT_MONO16, kCaptureFrequency / 5)) {
- auto error = ErrorHappened(device);
- alcCaptureCloseDevice(device);
- _available = !error;
- } else {
- LOG(("Audio Error: Could not open capture device!"));
- }
+ if (auto device = alcCaptureOpenDevice(nullptr, kCaptureFrequency, AL_FORMAT_MONO16, kCaptureFrequency / 5)) {
+ auto error = ErrorHappened(device);
+ alcCaptureCloseDevice(device);
+ _available = !error;
} else {
- LOG(("Audio Error: No capture device found!"));
+ LOG(("Audio Error: Could not open capture device!"));
}
}
@@ -177,9 +173,7 @@ void Instance::Inner::onInit() {
void Instance::Inner::onStart() {
// Start OpenAL Capture
- const ALCchar *dName = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
- DEBUG_LOG(("Audio Info: Capture device name '%1'").arg(dName));
- d->device = alcCaptureOpenDevice(dName, kCaptureFrequency, AL_FORMAT_MONO16, kCaptureFrequency / 5);
+ d->device = alcCaptureOpenDevice(nullptr, kCaptureFrequency, AL_FORMAT_MONO16, kCaptureFrequency / 5);
if (!d->device) {
LOG(("Audio Error: capture device not present!"));
emit error();
diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dump_to_text.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_dump_to_text.cpp
index 2ec424f6c..088fc015a 100644
--- a/Telegram/SourceFiles/mtproto/details/mtproto_dump_to_text.cpp
+++ b/Telegram/SourceFiles/mtproto/details/mtproto_dump_to_text.cpp
@@ -7,10 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/details/mtproto_dump_to_text.h"
-#include "base/zlib_help.h"
#include "scheme-dump_to_text.h"
#include "scheme.h"
+#include "zlib.h"
+
namespace MTP::details {
bool DumpToTextCore(DumpToTextBuffer &to, const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons) {
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index 513b22df3..ea1c081e0 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -13,41 +13,60 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_desktop_environment.h"
#include "platform/platform_notifications_manager.h"
#include "history/history.h"
+#include "history/history_widget.h"
+#include "history/history_inner_widget.h"
+#include "main/main_account.h" // Account::sessionChanges.
#include "mainwindow.h"
#include "core/application.h"
#include "core/sandbox.h"
+#include "boxes/peer_list_controllers.h"
+#include "boxes/about_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
+#include "window/window_session_controller.h"
+#include "ui/widgets/input_fields.h"
#include "facades.h"
#include "app.h"
#include
+#include
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
namespace Platform {
namespace {
constexpr auto kDisableTrayCounter = "TDESKTOP_DISABLE_TRAY_COUNTER"_cs;
-constexpr auto kTrayIconName = "kotatogram"_cs;
+constexpr auto kForcePanelIcon = "TDESKTOP_FORCE_PANEL_ICON"_cs;
constexpr auto kPanelTrayIconName = "kotatogram-panel"_cs;
constexpr auto kMutePanelTrayIconName = "kotatogram-mute-panel"_cs;
constexpr auto kAttentionPanelTrayIconName = "kotatogram-attention-panel"_cs;
constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
+constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
constexpr auto kTrayIconFilename = "kdesktop-trayicon-XXXXXX.png"_cs;
+constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs;
+constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs;
+constexpr auto kAppMenuInterface = kAppMenuService;
+
bool TrayIconMuted = true;
int32 TrayIconCount = 0;
base::flat_map TrayIconImageBack;
QIcon TrayIcon;
QString TrayIconThemeName, TrayIconName;
-QString GetPanelIconName() {
- const auto counter = Core::App().unreadBadge();
- const auto muted = Core::App().unreadBadgeMuted();
+bool SNIAvailable = false;
+QString GetPanelIconName(int counter, bool muted) {
return (counter > 0)
? (muted
? kMutePanelTrayIconName.utf16()
@@ -55,220 +74,232 @@ QString GetPanelIconName() {
: kPanelTrayIconName.utf16();
}
-QString GetTrayIconName() {
- const auto panelIconName = GetPanelIconName();
+QString GetTrayIconName(int counter, bool muted) {
+ const auto iconName = GetIconName();
+ const auto panelIconName = GetPanelIconName(counter, muted);
if (QIcon::hasThemeIcon(panelIconName)) {
return panelIconName;
- } else if (InSandbox()) {
- const auto launcherBasename = GetLauncherBasename();
-
- if (QIcon::hasThemeIcon(launcherBasename)) {
- return launcherBasename;
- }
- } else if (QIcon::hasThemeIcon(kTrayIconName.utf16())) {
- return kTrayIconName.utf16();
+ } else if (QIcon::hasThemeIcon(iconName)) {
+ return iconName;
}
return QString();
}
-QIcon TrayIconGen() {
- const auto iconThemeName = QIcon::themeName();
- const auto iconName = GetTrayIconName();
-
- if (qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
- && !iconName.isEmpty()) {
- if (TrayIcon.isNull()
- || iconThemeName != TrayIconThemeName
- || iconName != TrayIconName) {
- TrayIcon = QIcon::fromTheme(iconName);
- TrayIconThemeName = iconThemeName;
- TrayIconName = iconName;
- }
-
- return TrayIcon;
- }
-
- const auto counter = Core::App().unreadBadge();
- const auto muted = Core::App().unreadBadgeMuted();
- const auto counterSlice = (counter >= 1000)
+int GetCounterSlice(int counter) {
+ return (counter >= 1000)
? (1000 + (counter % 100))
: counter;
+}
- if (TrayIcon.isNull()
+bool IsIconRegenerationNeeded(
+ int counter,
+ bool muted,
+ const QString &iconThemeName = QIcon::themeName()) {
+ const auto iconName = GetTrayIconName(counter, muted);
+ const auto counterSlice = GetCounterSlice(counter);
+
+ return TrayIcon.isNull()
|| iconThemeName != TrayIconThemeName
|| iconName != TrayIconName
|| muted != TrayIconMuted
- || counterSlice != TrayIconCount) {
- QIcon result;
- QIcon systemIcon;
-
- const auto iconSizes = {
- 16,
- 22,
- 24,
- 32,
- 48,
- 64
- };
-
- for (const auto iconSize : iconSizes) {
- auto ¤tImageBack = TrayIconImageBack[iconSize];
- const auto desiredSize = QSize(iconSize, iconSize);
-
- if (currentImageBack.isNull()
- || iconThemeName != TrayIconThemeName
- || iconName != TrayIconName) {
- if (!iconName.isEmpty()) {
- if(systemIcon.isNull()) {
- systemIcon = QIcon::fromTheme(iconName);
- }
-
- if (systemIcon.actualSize(desiredSize) == desiredSize) {
- currentImageBack = systemIcon
- .pixmap(desiredSize)
- .toImage();
- } else {
- const auto availableSizes = systemIcon
- .availableSizes();
-
- const auto biggestSize = ranges::max_element(
- availableSizes,
- std::less<>(),
- &QSize::width);
-
- currentImageBack = systemIcon
- .pixmap(*biggestSize)
- .toImage();
- }
- } else {
- QString iconFilename(cWorkingDir() + "tdata/icon.png");
- currentImageBack = App::readImage(iconFilename, nullptr, false);
- if (currentImageBack.isNull()) {
- currentImageBack = Core::App().logo();
- }
- }
-
- if (currentImageBack.size() != desiredSize) {
- currentImageBack = currentImageBack.scaled(
- desiredSize,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- }
- }
-
- auto iconImage = currentImageBack;
- TrayIconMuted = muted;
- TrayIconCount = counterSlice;
- TrayIconThemeName = iconThemeName;
- TrayIconName = iconName;
-
- if (!qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
- && counter > 0) {
- QPainter p(&iconImage);
- int32 layerSize = -16;
-
- if (iconSize >= 48) {
- layerSize = -32;
- } else if (iconSize >= 36) {
- layerSize = -24;
- } else if (iconSize >= 32) {
- layerSize = -20;
- }
-
- auto &bg = muted
- ? st::trayCounterBgMute
- : st::trayCounterBg;
-
- auto &fg = st::trayCounterFg;
-
- auto layer = App::wnd()->iconWithCounter(
- layerSize,
- counter,
- bg,
- fg,
- false);
-
- p.drawImage(
- iconImage.width() - layer.width() - 1,
- iconImage.height() - layer.height() - 1,
- layer);
- }
-
- result.addPixmap(App::pixmapFromImageInPlace(
- std::move(iconImage)));
- }
-
- TrayIcon = result;
- }
-
- return TrayIcon;
+ || counterSlice != TrayIconCount;
}
-bool IsAppIndicator() {
-#ifdef TDESKTOP_DISABLE_DBUS_INTEGRATION
- static const auto AppIndicator = false;
-#else // TDESKTOP_DISABLE_DBUS_INTEGRATION
- static const auto AppIndicator = QDBusInterface(
- qsl("com.canonical.indicator.application"),
- qsl("/com/canonical/indicator/application/service"),
- qsl("com.canonical.indicator.application.service")).isValid()
- || QDBusInterface(
- qsl("org.ayatana.indicator.application"),
- qsl("/org/ayatana/indicator/application/service"),
- qsl("org.ayatana.indicator.application.service")).isValid();
-#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+void UpdateIconRegenerationNeeded(
+ const QIcon &icon,
+ int counter,
+ bool muted,
+ const QString &iconThemeName) {
+ const auto iconName = GetTrayIconName(counter, muted);
+ const auto counterSlice = GetCounterSlice(counter);
- return AppIndicator;
+ TrayIcon = icon;
+ TrayIconMuted = muted;
+ TrayIconCount = counterSlice;
+ TrayIconThemeName = iconThemeName;
+ TrayIconName = iconName;
+}
+
+QIcon TrayIconGen(int counter, bool muted) {
+ const auto iconThemeName = QIcon::themeName();
+
+ if (!IsIconRegenerationNeeded(counter, muted, iconThemeName)) {
+ return TrayIcon;
+ }
+
+ const auto iconName = GetTrayIconName(counter, muted);
+
+ if (qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
+ && !iconName.isEmpty()) {
+ const auto result = QIcon::fromTheme(iconName);
+ UpdateIconRegenerationNeeded(result, counter, muted, iconThemeName);
+ return result;
+ }
+
+ QIcon result;
+ QIcon systemIcon;
+
+ const auto iconSizes = {
+ 16,
+ 22,
+ 24,
+ 32,
+ 48,
+ 64
+ };
+
+ for (const auto iconSize : iconSizes) {
+ auto ¤tImageBack = TrayIconImageBack[iconSize];
+ const auto desiredSize = QSize(iconSize, iconSize);
+
+ if (currentImageBack.isNull()
+ || iconThemeName != TrayIconThemeName
+ || iconName != TrayIconName) {
+ if (!iconName.isEmpty()) {
+ if(systemIcon.isNull()) {
+ systemIcon = QIcon::fromTheme(iconName);
+ }
+
+ if (systemIcon.actualSize(desiredSize) == desiredSize) {
+ currentImageBack = systemIcon
+ .pixmap(desiredSize)
+ .toImage();
+ } else {
+ const auto availableSizes = systemIcon
+ .availableSizes();
+
+ const auto biggestSize = ranges::max_element(
+ availableSizes,
+ std::less<>(),
+ &QSize::width);
+
+ currentImageBack = systemIcon
+ .pixmap(*biggestSize)
+ .toImage();
+ }
+ } else {
+ QString iconFilename(cWorkingDir() + "tdata/icon.png");
+ currentImageBack = App::readImage(iconFilename, nullptr, false);
+ if (currentImageBack.isNull()) {
+ currentImageBack = Core::App().logo();
+ }
+ }
+
+ if (currentImageBack.size() != desiredSize) {
+ currentImageBack = currentImageBack.scaled(
+ desiredSize,
+ Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation);
+ }
+ }
+
+ auto iconImage = currentImageBack;
+
+ if (!qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
+ && counter > 0) {
+ QPainter p(&iconImage);
+ int32 layerSize = -16;
+
+ if (iconSize >= 48) {
+ layerSize = -32;
+ } else if (iconSize >= 36) {
+ layerSize = -24;
+ } else if (iconSize >= 32) {
+ layerSize = -20;
+ }
+
+ auto &bg = muted
+ ? st::trayCounterBgMute
+ : st::trayCounterBg;
+
+ auto &fg = st::trayCounterFg;
+
+ auto layer = App::wnd()->iconWithCounter(
+ layerSize,
+ counter,
+ bg,
+ fg,
+ false);
+
+ p.drawImage(
+ iconImage.width() - layer.width() - 1,
+ iconImage.height() - layer.height() - 1,
+ layer);
+ }
+
+ result.addPixmap(App::pixmapFromImageInPlace(
+ std::move(iconImage)));
+ }
+
+ UpdateIconRegenerationNeeded(result, counter, muted, iconThemeName);
+
+ return result;
}
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-static bool NeedTrayIconFile() {
+bool IsIndicatorApplication() {
// Hack for indicator-application, which doesn't handle icons sent across D-Bus:
// save the icon to a temp file and set the icon name to that filename.
- static const auto TrayIconFileNeeded = IsAppIndicator();
- return TrayIconFileNeeded;
-}
+ static const auto IndicatorApplication = [&] {
+ const auto interface = QDBusConnection::sessionBus().interface();
-static inline QString TrayIconFileTemplate() {
- static const auto TempFileTemplate = AppRuntimeDirectory()
- + kTrayIconFilename.utf16();
- return TempFileTemplate;
+ const auto ubuntuIndicator = interface->isServiceRegistered(
+ qsl("com.canonical.indicator.application"));
+
+ const auto ayatanaIndicator = interface->isServiceRegistered(
+ qsl("org.ayatana.indicator.application"));
+
+ return ubuntuIndicator || ayatanaIndicator;
+ }();
+
+ return IndicatorApplication;
}
std::unique_ptr TrayIconFile(
const QIcon &icon,
int size,
QObject *parent) {
+ static const auto templateName = AppRuntimeDirectory()
+ + kTrayIconFilename.utf16();
+
auto ret = std::make_unique(
- TrayIconFileTemplate(),
+ templateName,
parent);
+
ret->open();
icon.pixmap(size).save(ret.get());
ret->close();
+
return ret;
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
bool IsSNIAvailable() {
- static const auto SNIAvailable = [&] {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- QDBusInterface systrayHost(
- kSNIWatcherService.utf16(),
- qsl("/StatusNotifierWatcher"),
- kSNIWatcherService.utf16());
+ auto message = QDBusMessage::createMethodCall(
+ kSNIWatcherService.utf16(),
+ qsl("/StatusNotifierWatcher"),
+ kPropertiesInterface.utf16(),
+ qsl("Get"));
- return systrayHost.isValid()
- && systrayHost
- .property("IsStatusNotifierHostRegistered")
- .toBool();
+ message.setArguments({
+ kSNIWatcherService.utf16(),
+ qsl("IsStatusNotifierHostRegistered")
+ });
+
+ const QDBusReply reply = QDBusConnection::sessionBus().call(
+ message);
+
+ if (reply.isValid()) {
+ return reply.value().toBool();
+ } else if (reply.error().type() != QDBusError::ServiceUnknown) {
+ LOG(("SNI Error: %1").arg(reply.error().message()));
+ }
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
- return false;
- }();
-
- return SNIAvailable;
+ return false;
}
bool UseUnityCounter() {
@@ -292,12 +323,113 @@ quint32 djbStringHash(QString string) {
return hash;
}
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+bool AppMenuSupported() {
+ static const auto Available = QDBusInterface(
+ kAppMenuService.utf16(),
+ kAppMenuObjectPath.utf16(),
+ kAppMenuInterface.utf16()).isValid();
+
+ return Available;
+}
+
+void RegisterAppMenu(uint winId, const QDBusObjectPath &menuPath) {
+ auto message = QDBusMessage::createMethodCall(
+ kAppMenuService.utf16(),
+ kAppMenuObjectPath.utf16(),
+ kAppMenuInterface.utf16(),
+ qsl("RegisterWindow"));
+
+ message.setArguments({
+ winId,
+ QVariant::fromValue(menuPath)
+ });
+
+ QDBusConnection::sessionBus().send(message);
+}
+
+void UnregisterAppMenu(uint winId) {
+ auto message = QDBusMessage::createMethodCall(
+ kAppMenuService.utf16(),
+ kAppMenuObjectPath.utf16(),
+ kAppMenuInterface.utf16(),
+ qsl("UnregisterWindow"));
+
+ message.setArguments({
+ winId
+ });
+
+ QDBusConnection::sessionBus().send(message);
+}
+
+void SendKeySequence(
+ Qt::Key key,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier) {
+ const auto focused = QApplication::focusWidget();
+ if (qobject_cast(focused)
+ || qobject_cast(focused)
+ || qobject_cast(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 // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
} // namespace
MainWindow::MainWindow(not_null controller)
: Window::MainWindow(controller) {
}
+void MainWindow::initHook() {
+ SNIAvailable = IsSNIAvailable();
+
+ const auto trayAvailable = SNIAvailable
+ || QSystemTrayIcon::isSystemTrayAvailable();
+
+ LOG(("System tray available: %1").arg(Logs::b(trayAvailable)));
+ cSetSupportTray(trayAvailable);
+
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+ auto sniWatcher = new QDBusServiceWatcher(
+ kSNIWatcherService.utf16(),
+ QDBusConnection::sessionBus(),
+ QDBusServiceWatcher::WatchForOwnerChange,
+ this);
+
+ connect(
+ sniWatcher,
+ &QDBusServiceWatcher::serviceOwnerChanged,
+ this,
+ &MainWindow::onSNIOwnerChanged);
+
+ connect(
+ windowHandle(),
+ &QWindow::visibleChanged,
+ this,
+ &MainWindow::onVisibleChanged);
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+ if (UseUnityCounter()) {
+ LOG(("Using Unity launcher counter."));
+ } else {
+ LOG(("Not using Unity launcher counter."));
+ }
+}
+
bool MainWindow::hasTrayIcon() const {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
return trayIcon || _sniTrayIcon;
@@ -307,39 +439,48 @@ bool MainWindow::hasTrayIcon() const {
}
void MainWindow::psShowTrayMenu() {
- if (!IsSNIAvailable()) {
- _trayIconMenuXEmbed->popup(QCursor::pos());
- }
+ _trayIconMenuXEmbed->popup(QCursor::pos());
}
void MainWindow::psTrayMenuUpdated() {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- if (IsSNIAvailable()) {
- if (_sniTrayIcon && trayIconMenu) {
- _sniTrayIcon->setContextMenu(trayIconMenu);
- }
+ if (_sniTrayIcon && trayIconMenu) {
+ _sniTrayIcon->setContextMenu(trayIconMenu);
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
}
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-void MainWindow::setSNITrayIcon(const QIcon &icon) {
- const auto iconName = GetTrayIconName();
+void MainWindow::setSNITrayIcon(int counter, bool muted, bool firstShow) {
+ const auto iconName = GetTrayIconName(counter, muted);
if (qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())
- && !iconName.isEmpty()) {
+ && (!iconName.isEmpty()
+ || qEnvironmentVariableIsSet(kForcePanelIcon.utf8()))) {
+ if (_sniTrayIcon->iconName() == iconName) {
+ return;
+ }
+
_sniTrayIcon->setIconByName(iconName);
_sniTrayIcon->setToolTipIconByName(iconName);
- } else if (NeedTrayIconFile()) {
+ } else if (IsIndicatorApplication()) {
+ if(!IsIconRegenerationNeeded(counter, muted) && !firstShow) {
+ return;
+ }
+
+ const auto icon = TrayIconGen(counter, muted);
_trayIconFile = TrayIconFile(icon, 22, this);
- _trayToolTipIconFile = TrayIconFile(icon, 48, this);
if (_trayIconFile) {
+ // indicator-application doesn't support tooltips
_sniTrayIcon->setIconByName(_trayIconFile->fileName());
- _sniTrayIcon->setToolTipIconByName(
- _trayToolTipIconFile->fileName());
}
} else {
+ if(!IsIconRegenerationNeeded(counter, muted) && !firstShow) {
+ return;
+ }
+
+ const auto icon = TrayIconGen(counter, muted);
_sniTrayIcon->setIconByPixmap(icon);
_sniTrayIcon->setToolTipIconByPixmap(icon);
}
@@ -365,10 +506,51 @@ void MainWindow::attachToSNITrayIcon() {
});
updateTrayMenu();
}
+
+void MainWindow::onSNIOwnerChanged(
+ const QString &service,
+ const QString &oldOwner,
+ const QString &newOwner) {
+ if (oldOwner.isEmpty() && !newOwner.isEmpty()) {
+ LOG(("Switching to SNI tray icon..."));
+ } else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
+ LOG(("Switching to Qt tray icon..."));
+ } else {
+ return;
+ }
+
+ if (_sniTrayIcon) {
+ _sniTrayIcon->setContextMenu(0);
+ _sniTrayIcon->deleteLater();
+ }
+ _sniTrayIcon = nullptr;
+
+ if (trayIcon) {
+ trayIcon->setContextMenu(0);
+ trayIcon->deleteLater();
+ }
+ trayIcon = nullptr;
+
+ SNIAvailable = IsSNIAvailable();
+
+ const auto trayAvailable = SNIAvailable
+ || QSystemTrayIcon::isSystemTrayAvailable();
+
+ cSetSupportTray(trayAvailable);
+
+ if(cSupportTray()) {
+ psSetupTrayIcon();
+ } else {
+ LOG(("System tray is not available."));
+ }
+}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
void MainWindow::psSetupTrayIcon() {
- if (IsSNIAvailable()) {
+ const auto counter = Core::App().unreadBadge();
+ const auto muted = Core::App().unreadBadgeMuted();
+
+ if (SNIAvailable) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
LOG(("Using SNI tray icon."));
if (!_sniTrayIcon) {
@@ -377,7 +559,7 @@ void MainWindow::psSetupTrayIcon() {
this);
_sniTrayIcon->setTitle(AppName.utf16());
- setSNITrayIcon(TrayIconGen());
+ setSNITrayIcon(counter, muted, true);
attachToSNITrayIcon();
}
@@ -385,15 +567,9 @@ void MainWindow::psSetupTrayIcon() {
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} else {
LOG(("Using Qt tray icon."));
-
- if (!_trayIconMenuXEmbed) {
- _trayIconMenuXEmbed = new Ui::PopupMenu(nullptr, trayIconMenu);
- _trayIconMenuXEmbed->deleteOnHide(false);
- }
-
if (!trayIcon) {
trayIcon = new QSystemTrayIcon(this);
- trayIcon->setIcon(TrayIconGen());
+ trayIcon->setIcon(TrayIconGen(counter, muted));
attachToTrayIcon(trayIcon);
}
@@ -407,21 +583,19 @@ void MainWindow::workmodeUpdated(DBIWorkMode mode) {
if (!cSupportTray()) return;
if (mode == dbiwmWindowOnly) {
- if (IsSNIAvailable()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- if (_sniTrayIcon) {
- _sniTrayIcon->setContextMenu(0);
- _sniTrayIcon->deleteLater();
- }
- _sniTrayIcon = 0;
-#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
- } else {
- if (trayIcon) {
- trayIcon->setContextMenu(0);
- trayIcon->deleteLater();
- }
- trayIcon = 0;
+ if (_sniTrayIcon) {
+ _sniTrayIcon->setContextMenu(0);
+ _sniTrayIcon->deleteLater();
}
+ _sniTrayIcon = nullptr;
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+ if (trayIcon) {
+ trayIcon->setContextMenu(0);
+ trayIcon->deleteLater();
+ }
+ trayIcon = nullptr;
} else {
psSetupTrayIcon();
}
@@ -433,11 +607,13 @@ void MainWindow::unreadCounterChangedHook() {
}
void MainWindow::updateIconCounters() {
+ const auto counter = Core::App().unreadBadge();
+ const auto muted = Core::App().unreadBadgeMuted();
+
updateWindowIcon();
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (UseUnityCounter()) {
- const auto counter = Core::App().unreadBadge();
const auto launcherUrl = "application://" + GetLauncherFilename();
QVariantMap dbusUnityProperties;
if (counter > 0) {
@@ -460,16 +636,14 @@ void MainWindow::updateIconCounters() {
signal << dbusUnityProperties;
QDBusConnection::sessionBus().send(signal);
}
+
+ if (_sniTrayIcon) {
+ setSNITrayIcon(counter, muted);
+ }
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
- if (IsSNIAvailable()) {
-#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- if (_sniTrayIcon) {
- setSNITrayIcon(TrayIconGen());
- }
-#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
- } else if (trayIcon) {
- trayIcon->setIcon(TrayIconGen());
+ if (trayIcon && IsIconRegenerationNeeded(counter, muted)) {
+ trayIcon->setIcon(TrayIconGen(counter, muted));
}
}
@@ -482,22 +656,341 @@ void MainWindow::LibsLoaded() {
}
void MainWindow::initTrayMenuHook() {
- const auto trayAvailable = IsSNIAvailable()
- || QSystemTrayIcon::isSystemTrayAvailable();
+ _trayIconMenuXEmbed = new Ui::PopupMenu(nullptr, trayIconMenu);
+ _trayIconMenuXEmbed->deleteOnHide(false);
+}
- LOG(("System tray available: %1").arg(Logs::b(trayAvailable)));
- cSetSupportTray(trayAvailable);
+#ifdef TDESKTOP_DISABLE_DBUS_INTEGRATION
- if (UseUnityCounter()) {
- LOG(("Using Unity launcher counter."));
- } else {
- LOG(("Not using Unity launcher counter."));
+void MainWindow::createGlobalMenu() {
+}
+
+void MainWindow::updateGlobalMenuHook() {
+}
+
+#else // TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+void MainWindow::createGlobalMenu() {
+ if (!AppMenuSupported()) return;
+
+ psMainMenu = new QMenu(this);
+
+ auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now));
+
+ psLogout = file->addAction(
+ tr::lng_mac_menu_logout(tr::now),
+ App::wnd(),
+ SLOT(onLogout()));
+
+ auto quit = file->addAction(
+ tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, qsl("Telegram")),
+ App::wnd(),
+ SLOT(quitFromTray()),
+ QKeySequence::Quit);
+
+ quit->setMenuRole(QAction::QuitRole);
+
+ auto edit = psMainMenu->addMenu(tr::lng_mac_menu_edit(tr::now));
+
+ psUndo = edit->addAction(
+ tr::lng_linux_menu_undo(tr::now),
+ this,
+ SLOT(psLinuxUndo()),
+ QKeySequence::Undo);
+
+ psRedo = edit->addAction(
+ tr::lng_linux_menu_redo(tr::now),
+ this,
+ SLOT(psLinuxRedo()),
+ QKeySequence::Redo);
+
+ edit->addSeparator();
+
+ psCut = edit->addAction(
+ tr::lng_mac_menu_cut(tr::now),
+ this,
+ SLOT(psLinuxCut()),
+ QKeySequence::Cut);
+ psCopy = edit->addAction(
+ tr::lng_mac_menu_copy(tr::now),
+ this,
+ SLOT(psLinuxCopy()),
+ QKeySequence::Copy);
+
+ psPaste = edit->addAction(
+ tr::lng_mac_menu_paste(tr::now),
+ this,
+ SLOT(psLinuxPaste()),
+ QKeySequence::Paste);
+
+ psDelete = edit->addAction(
+ tr::lng_mac_menu_delete(tr::now),
+ this,
+ SLOT(psLinuxDelete()),
+ QKeySequence(Qt::ControlModifier | Qt::Key_Backspace));
+
+ edit->addSeparator();
+
+ psBold = edit->addAction(
+ tr::lng_menu_formatting_bold(tr::now),
+ this,
+ SLOT(psLinuxBold()),
+ QKeySequence::Bold);
+
+ psItalic = edit->addAction(
+ tr::lng_menu_formatting_italic(tr::now),
+ this,
+ SLOT(psLinuxItalic()),
+ QKeySequence::Italic);
+
+ psUnderline = edit->addAction(
+ tr::lng_menu_formatting_underline(tr::now),
+ this,
+ SLOT(psLinuxUnderline()),
+ QKeySequence::Underline);
+
+ psStrikeOut = edit->addAction(
+ tr::lng_menu_formatting_strike_out(tr::now),
+ this,
+ SLOT(psLinuxStrikeOut()),
+ Ui::kStrikeOutSequence);
+
+ psMonospace = edit->addAction(
+ tr::lng_menu_formatting_monospace(tr::now),
+ this,
+ SLOT(psLinuxMonospace()),
+ Ui::kMonospaceSequence);
+
+ psClearFormat = edit->addAction(
+ tr::lng_menu_formatting_clear(tr::now),
+ this,
+ SLOT(psLinuxClearFormat()),
+ Ui::kClearFormatSequence);
+
+ edit->addSeparator();
+
+ psSelectAll = edit->addAction(
+ tr::lng_mac_menu_select_all(tr::now),
+ this, SLOT(psLinuxSelectAll()),
+ QKeySequence::SelectAll);
+
+ edit->addSeparator();
+
+ auto prefs = edit->addAction(
+ tr::lng_mac_menu_preferences(tr::now),
+ App::wnd(),
+ SLOT(showSettings()),
+ QKeySequence(Qt::ControlModifier | Qt::Key_Comma));
+
+ prefs->setMenuRole(QAction::PreferencesRole);
+
+ auto tools = psMainMenu->addMenu(tr::lng_linux_menu_tools(tr::now));
+
+ psContacts = tools->addAction(
+ tr::lng_mac_menu_contacts(tr::now),
+ crl::guard(this, [=] {
+ if (isHidden()) {
+ App::wnd()->showFromTray();
+ }
+
+ if (!account().sessionExists()) {
+ return;
+ }
+
+ Ui::show(
+ Box(std::make_unique(
+ sessionController()),
+ [](not_null box) {
+ box->addButton(tr::lng_close(), [box] {
+ box->closeBox();
+ });
+
+ box->addLeftButton(tr::lng_profile_add_contact(), [] {
+ App::wnd()->onShowAddContact();
+ });
+ }));
+ }));
+
+ psAddContact = tools->addAction(
+ tr::lng_mac_menu_add_contact(tr::now),
+ App::wnd(),
+ SLOT(onShowAddContact()));
+
+ tools->addSeparator();
+
+ psNewGroup = tools->addAction(
+ tr::lng_mac_menu_new_group(tr::now),
+ App::wnd(),
+ SLOT(onShowNewGroup()));
+
+ psNewChannel = tools->addAction(
+ tr::lng_mac_menu_new_channel(tr::now),
+ App::wnd(),
+ SLOT(onShowNewChannel()));
+
+ auto help = psMainMenu->addMenu(tr::lng_linux_menu_help(tr::now));
+
+ auto about = help->addAction(
+ tr::lng_mac_menu_about_telegram(
+ tr::now,
+ lt_telegram,
+ qsl("Telegram")),
+ [] {
+ if (App::wnd() && App::wnd()->isHidden()) {
+ App::wnd()->showFromTray();
+ }
+
+ Ui::show(Box());
+ });
+
+ about->setMenuRole(QAction::AboutQtRole);
+
+ _mainMenuPath.setPath(qsl("/MenuBar"));
+
+ _mainMenuExporter = new DBusMenuExporter(
+ _mainMenuPath.path(),
+ psMainMenu);
+
+ RegisterAppMenu(winId(), _mainMenuPath);
+
+ updateGlobalMenu();
+}
+
+void MainWindow::psLinuxUndo() {
+ SendKeySequence(Qt::Key_Z, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxRedo() {
+ SendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
+}
+
+void MainWindow::psLinuxCut() {
+ SendKeySequence(Qt::Key_X, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxCopy() {
+ SendKeySequence(Qt::Key_C, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxPaste() {
+ SendKeySequence(Qt::Key_V, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxDelete() {
+ SendKeySequence(Qt::Key_Delete);
+}
+
+void MainWindow::psLinuxSelectAll() {
+ SendKeySequence(Qt::Key_A, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxBold() {
+ SendKeySequence(Qt::Key_B, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxItalic() {
+ SendKeySequence(Qt::Key_I, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxUnderline() {
+ SendKeySequence(Qt::Key_U, Qt::ControlModifier);
+}
+
+void MainWindow::psLinuxStrikeOut() {
+ SendKeySequence(Qt::Key_X, Qt::ControlModifier | Qt::ShiftModifier);
+}
+
+void MainWindow::psLinuxMonospace() {
+ SendKeySequence(Qt::Key_M, Qt::ControlModifier | Qt::ShiftModifier);
+}
+
+void MainWindow::psLinuxClearFormat() {
+ SendKeySequence(Qt::Key_N, Qt::ControlModifier | Qt::ShiftModifier);
+}
+
+void MainWindow::updateGlobalMenuHook() {
+ if (!AppMenuSupported() || !App::wnd() || !positionInited()) return;
+
+ const auto focused = QApplication::focusWidget();
+ auto canUndo = false;
+ auto canRedo = false;
+ auto canCut = false;
+ auto canCopy = false;
+ auto canPaste = false;
+ auto canDelete = false;
+ auto canSelectAll = false;
+ const auto clipboardHasText = QGuiApplication::clipboard()
+ ->ownsClipboard();
+ auto markdownEnabled = false;
+ if (const auto edit = qobject_cast(focused)) {
+ canCut = canCopy = canDelete = edit->hasSelectedText();
+ canSelectAll = !edit->text().isEmpty();
+ canUndo = edit->isUndoAvailable();
+ canRedo = edit->isRedoAvailable();
+ canPaste = clipboardHasText;
+ } else if (const auto edit = qobject_cast(focused)) {
+ canCut = canCopy = canDelete = edit->textCursor().hasSelection();
+ canSelectAll = !edit->document()->isEmpty();
+ canUndo = edit->document()->isUndoAvailable();
+ canRedo = edit->document()->isRedoAvailable();
+ canPaste = clipboardHasText;
+ if (canCopy) {
+ if (const auto inputField = qobject_cast(
+ focused->parentWidget())) {
+ markdownEnabled = inputField->isMarkdownEnabled();
+ }
+ }
+ } else if (const auto list = qobject_cast(focused)) {
+ canCopy = list->canCopySelected();
+ canDelete = list->canDeleteSelected();
+ }
+ App::wnd()->updateIsActive(0);
+ const auto logged = account().sessionExists();
+ const auto locked = Core::App().locked();
+ const auto inactive = !logged || locked;
+ const auto support = logged && account().session().supportMode();
+ ForceDisabled(psLogout, !logged && !locked);
+ ForceDisabled(psUndo, !canUndo);
+ ForceDisabled(psRedo, !canRedo);
+ ForceDisabled(psCut, !canCut);
+ ForceDisabled(psCopy, !canCopy);
+ ForceDisabled(psPaste, !canPaste);
+ ForceDisabled(psDelete, !canDelete);
+ ForceDisabled(psSelectAll, !canSelectAll);
+ ForceDisabled(psContacts, inactive || support);
+ ForceDisabled(psAddContact, inactive);
+ ForceDisabled(psNewGroup, inactive || support);
+ ForceDisabled(psNewChannel, inactive || support);
+
+ ForceDisabled(psBold, !markdownEnabled);
+ ForceDisabled(psItalic, !markdownEnabled);
+ ForceDisabled(psUnderline, !markdownEnabled);
+ ForceDisabled(psStrikeOut, !markdownEnabled);
+ ForceDisabled(psMonospace, !markdownEnabled);
+ ForceDisabled(psClearFormat, !markdownEnabled);
+}
+
+void MainWindow::onVisibleChanged(bool visible) {
+ if (AppMenuSupported() && !_mainMenuPath.path().isEmpty()) {
+ if (visible) {
+ RegisterAppMenu(winId(), _mainMenuPath);
+ } else {
+ UnregisterAppMenu(winId());
+ }
}
}
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
MainWindow::~MainWindow() {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
delete _sniTrayIcon;
+
+ if (AppMenuSupported()) {
+ UnregisterAppMenu(winId());
+ delete _mainMenuExporter;
+ delete psMainMenu;
+ }
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
delete _trayIconMenuXEmbed;
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h
index f6eadc5d1..8602c5380 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.h
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h
@@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include "statusnotifieritem.h"
#include
+#include
+#include
#endif
namespace Platform {
@@ -38,13 +40,41 @@ public:
public slots:
void psShowTrayMenu();
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+ void onSNIOwnerChanged(
+ const QString &service,
+ const QString &oldOwner,
+ const QString &newOwner);
+
+ void psLinuxUndo();
+ void psLinuxRedo();
+ void psLinuxCut();
+ void psLinuxCopy();
+ void psLinuxPaste();
+ void psLinuxDelete();
+ void psLinuxSelectAll();
+
+ void psLinuxBold();
+ void psLinuxItalic();
+ void psLinuxUnderline();
+ void psLinuxStrikeOut();
+ void psLinuxMonospace();
+ void psLinuxClearFormat();
+
+ void onVisibleChanged(bool visible);
+
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
protected:
+ void initHook() override;
void unreadCounterChangedHook() override;
+ void updateGlobalMenuHook() override;
void initTrayMenuHook() override;
bool hasTrayIcon() const override;
void workmodeUpdated(DBIWorkMode mode) override;
+ void createGlobalMenu() override;
QSystemTrayIcon *trayIcon = nullptr;
QMenu *trayIconMenu = nullptr;
@@ -68,9 +98,32 @@ private:
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
StatusNotifierItem *_sniTrayIcon = nullptr;
std::unique_ptr _trayIconFile = nullptr;
- std::unique_ptr _trayToolTipIconFile = nullptr;
- void setSNITrayIcon(const QIcon &icon);
+ DBusMenuExporter *_mainMenuExporter = nullptr;
+ QDBusObjectPath _mainMenuPath;
+
+ QMenu *psMainMenu = nullptr;
+ QAction *psLogout = nullptr;
+ QAction *psUndo = nullptr;
+ QAction *psRedo = nullptr;
+ QAction *psCut = nullptr;
+ QAction *psCopy = nullptr;
+ QAction *psPaste = nullptr;
+ QAction *psDelete = nullptr;
+ QAction *psSelectAll = nullptr;
+ QAction *psContacts = nullptr;
+ QAction *psAddContact = nullptr;
+ QAction *psNewGroup = nullptr;
+ QAction *psNewChannel = nullptr;
+
+ QAction *psBold = nullptr;
+ QAction *psItalic = nullptr;
+ QAction *psUnderline = nullptr;
+ QAction *psStrikeOut = nullptr;
+ QAction *psMonospace = nullptr;
+ QAction *psClearFormat = nullptr;
+
+ void setSNITrayIcon(int counter, bool muted, bool firstShow = false);
void attachToSNITrayIcon();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
index b52191ae2..d6ef3efbc 100644
--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
@@ -7,18 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/notifications_manager_linux.h"
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include "platform/linux/specific_linux.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "facades.h"
-#include
-#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include
+#include
#include
+#include
#include
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+#include
namespace Platform {
namespace Notifications {
@@ -38,50 +40,101 @@ namespace {
constexpr auto kService = "org.freedesktop.Notifications"_cs;
constexpr auto kObjectPath = "/org/freedesktop/Notifications"_cs;
constexpr auto kInterface = kService;
+constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
-std::vector GetServerInformation(
- const std::shared_ptr ¬ificationInterface) {
+bool InhibitedNotSupported = false;
+
+std::vector ComputeServerInformation() {
std::vector serverInformation;
- const auto serverInformationReply = notificationInterface
- ->call(qsl("GetServerInformation"));
- if (serverInformationReply.type() == QDBusMessage::ReplyMessage) {
- for (const auto &arg : serverInformationReply.arguments()) {
- if (static_cast(arg.type())
- == QMetaType::QString) {
- serverInformation.push_back(arg.toString());
- } else {
- LOG(("Native notification error: "
- "all elements in GetServerInformation "
- "should be strings"));
- }
- }
- } else if (serverInformationReply.type() == QDBusMessage::ErrorMessage) {
- LOG(("Native notification error: %1")
- .arg(serverInformationReply.errorMessage()));
+ const auto message = QDBusMessage::createMethodCall(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kInterface.utf16(),
+ qsl("GetServerInformation"));
+
+ const auto reply = QDBusConnection::sessionBus().call(message);
+
+ if (reply.type() == QDBusMessage::ReplyMessage) {
+ ranges::transform(
+ reply.arguments(),
+ ranges::back_inserter(serverInformation),
+ &QVariant::toString
+ );
+ } else if (reply.type() == QDBusMessage::ErrorMessage) {
+ LOG(("Native notification error: %1").arg(reply.errorMessage()));
} else {
LOG(("Native notification error: "
- "error while getting information about notification daemon"));
+ "invalid reply from GetServerInformation"));
}
return serverInformation;
}
-QStringList GetCapabilities(
- const std::shared_ptr ¬ificationInterface) {
- const QDBusReply capabilitiesReply = notificationInterface
- ->call(qsl("GetCapabilities"));
+std::vector GetServerInformation() {
+ static const auto ServerInformation = ComputeServerInformation();
+ return ServerInformation;
+}
- if (capabilitiesReply.isValid()) {
- return capabilitiesReply.value();
+QStringList ComputeCapabilities() {
+ const auto message = QDBusMessage::createMethodCall(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kInterface.utf16(),
+ qsl("GetCapabilities"));
+
+ const QDBusReply reply = QDBusConnection::sessionBus().call(
+ message);
+
+ if (reply.isValid()) {
+ return reply.value();
} else {
- LOG(("Native notification error: %1")
- .arg(capabilitiesReply.error().message()));
+ LOG(("Native notification error: %1").arg(reply.error().message()));
}
return {};
}
+QStringList GetCapabilities() {
+ static const auto Capabilities = ComputeCapabilities();
+ return Capabilities;
+}
+
+bool Inhibited() {
+ auto message = QDBusMessage::createMethodCall(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kPropertiesInterface.utf16(),
+ qsl("Get"));
+
+ message.setArguments({
+ qsl("org.freedesktop.Notifications"),
+ qsl("Inhibited")
+ });
+
+ const QDBusReply reply = QDBusConnection::sessionBus().call(
+ message);
+
+ constexpr auto notSupportedErrors = {
+ QDBusError::ServiceUnknown,
+ QDBusError::InvalidArgs,
+ };
+
+ if (reply.isValid()) {
+ return reply.value().toBool();
+ } else if (ranges::contains(notSupportedErrors, reply.error().type())) {
+ InhibitedNotSupported = true;
+ } else {
+ if (reply.error().type() == QDBusError::AccessDenied) {
+ InhibitedNotSupported = true;
+ }
+
+ LOG(("Native notification error: %1").arg(reply.error().message()));
+ }
+
+ return false;
+}
+
QVersionNumber ParseSpecificationVersion(
const std::vector &serverInformation) {
if (serverInformation.size() >= 4) {
@@ -94,10 +147,31 @@ QVersionNumber ParseSpecificationVersion(
return QVersionNumber();
}
+QString GetImageKey(const QVersionNumber &specificationVersion) {
+ if (!specificationVersion.isNull()) {
+ const auto majorVersion = specificationVersion.majorVersion();
+ const auto minorVersion = specificationVersion.minorVersion();
+
+ if ((majorVersion == 1 && minorVersion >= 2) || majorVersion > 1) {
+ return qsl("image-data");
+ } else if (majorVersion == 1 && minorVersion == 1) {
+ return qsl("image_data");
+ } else if ((majorVersion == 1 && minorVersion < 1)
+ || majorVersion < 1) {
+ return qsl("icon_data");
+ } else {
+ LOG(("Native notification error: unknown specification version"));
+ }
+ } else {
+ LOG(("Native notification error: specification version is null"));
+ }
+
+ return QString();
+}
+
}
NotificationData::NotificationData(
- const std::shared_ptr ¬ificationInterface,
const base::weak_ptr &manager,
const QString &title,
const QString &subtitle,
@@ -105,12 +179,14 @@ NotificationData::NotificationData(
PeerId peerId,
MsgId msgId,
bool hideReplyButton)
-: _notificationInterface(notificationInterface)
+: _dbusConnection(QDBusConnection::sessionBus())
, _manager(manager)
, _title(title)
+, _imageKey(GetImageKey(ParseSpecificationVersion(
+ GetServerInformation())))
, _peerId(peerId)
, _msgId(msgId) {
- const auto capabilities = GetCapabilities(_notificationInterface);
+ const auto capabilities = GetCapabilities();
if (capabilities.contains(qsl("body-markup"))) {
_body = subtitle.isEmpty()
@@ -127,19 +203,16 @@ NotificationData::NotificationData(
if (capabilities.contains(qsl("actions"))) {
_actions << qsl("default") << QString();
- _notificationInterface->connection().connect(
- kService.utf16(),
- kObjectPath.utf16(),
- kInterface.utf16(),
- qsl("ActionInvoked"),
- this,
- SLOT(notificationClicked(uint,QString)));
+ _actions
+ << qsl("mail-mark-read")
+ << tr::lng_context_mark_read(tr::now);
if (capabilities.contains(qsl("inline-reply")) && !hideReplyButton) {
- _actions << qsl("inline-reply")
+ _actions
+ << qsl("inline-reply")
<< tr::lng_notification_reply(tr::now);
- _notificationInterface->connection().connect(
+ _dbusConnection.connect(
kService.utf16(),
kObjectPath.utf16(),
kInterface.utf16(),
@@ -148,9 +221,18 @@ NotificationData::NotificationData(
SLOT(notificationReplied(uint,QString)));
} else {
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
- _actions << qsl("mail-reply-sender")
+ _actions
+ << qsl("mail-reply-sender")
<< tr::lng_notification_reply(tr::now);
}
+
+ _dbusConnection.connect(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kInterface.utf16(),
+ qsl("ActionInvoked"),
+ this,
+ SLOT(actionInvoked(uint,QString)));
}
if (capabilities.contains(qsl("action-icons"))) {
@@ -172,10 +254,9 @@ NotificationData::NotificationData(
}
_hints["category"] = qsl("im.received");
-
_hints["desktop-entry"] = GetLauncherBasename();
- _notificationInterface->connection().connect(
+ _dbusConnection.connect(
kService.utf16(),
kObjectPath.utf16(),
kInterface.utf16(),
@@ -184,65 +265,56 @@ NotificationData::NotificationData(
SLOT(notificationClosed(uint)));
}
-bool NotificationData::show(bool hideNameAndPhoto) {
- const QDBusReply notifyReply = _notificationInterface->call(
- qsl("Notify"),
+bool NotificationData::show() {
+ const auto iconName = _imageKey.isEmpty() || !_hints.contains(_imageKey)
+ ? GetIconName()
+ : QString();
+
+ auto message = QDBusMessage::createMethodCall(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kInterface.utf16(),
+ qsl("Notify"));
+
+ message.setArguments({
AppName.utf16(),
uint(0),
- hideNameAndPhoto
- ? qsl("telegram")
- : QString(),
+ iconName,
_title,
_body,
_actions,
_hints,
- -1);
+ -1
+ });
- if (notifyReply.isValid()) {
- _notificationId = notifyReply.value();
+ const QDBusReply reply = _dbusConnection.call(
+ message);
+
+ if (reply.isValid()) {
+ _notificationId = reply.value();
} else {
- LOG(("Native notification error: %1")
- .arg(notifyReply.error().message()));
+ LOG(("Native notification error: %1").arg(reply.error().message()));
}
- return notifyReply.isValid();
+ return reply.isValid();
}
-bool NotificationData::close() {
- const QDBusReply closeReply = _notificationInterface
- ->call(qsl("CloseNotification"), _notificationId);
+void NotificationData::close() {
+ auto message = QDBusMessage::createMethodCall(
+ kService.utf16(),
+ kObjectPath.utf16(),
+ kInterface.utf16(),
+ qsl("CloseNotification"));
- if (!closeReply.isValid()) {
- LOG(("Native notification error: %1")
- .arg(closeReply.error().message()));
- }
+ message.setArguments({
+ _notificationId
+ });
- return closeReply.isValid();
+ _dbusConnection.send(message);
}
void NotificationData::setImage(const QString &imagePath) {
- const auto specificationVersion = ParseSpecificationVersion(
- GetServerInformation(_notificationInterface));
-
- QString imageKey;
-
- if (!specificationVersion.isNull()) {
- const auto majorVersion = specificationVersion.majorVersion();
- const auto minorVersion = specificationVersion.minorVersion();
-
- if ((majorVersion == 1 && minorVersion >= 2) || majorVersion > 1) {
- imageKey = qsl("image-data");
- } else if (majorVersion == 1 && minorVersion == 1) {
- imageKey = qsl("image_data");
- } else if ((majorVersion == 1 && minorVersion < 1)
- || majorVersion < 1) {
- imageKey = qsl("icon_data");
- } else {
- LOG(("Native notification error: unknown specification version"));
- return;
- }
- } else {
- LOG(("Native notification error: specification version is null"));
+ if (_imageKey.isEmpty()) {
return;
}
@@ -253,20 +325,21 @@ void NotificationData::setImage(const QString &imagePath) {
(const char*)image.constBits(),
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
image.byteCount());
-#else
+#else // Qt < 5.10.0
image.sizeInBytes());
-#endif
+#endif // Qt >= 5.10.0
- ImageData imageData;
- imageData.width = image.width();
- imageData.height = image.height();
- imageData.rowStride = image.bytesPerLine();
- imageData.hasAlpha = true;
- imageData.bitsPerSample = 8;
- imageData.channels = 4;
- imageData.data = imageBytes;
+ const auto imageData = ImageData{
+ image.width(),
+ image.height(),
+ image.bytesPerLine(),
+ true,
+ 8,
+ 4,
+ imageBytes
+ };
- _hints[imageKey] = QVariant::fromValue(imageData);
+ _hints[_imageKey] = QVariant::fromValue(imageData);
}
void NotificationData::notificationClosed(uint id) {
@@ -278,19 +351,23 @@ void NotificationData::notificationClosed(uint id) {
}
}
-void NotificationData::notificationClicked(uint id, const QString &actionId) {
+void NotificationData::actionInvoked(uint id, const QString &actionName) {
if (id != _notificationId) {
return;
}
- if (actionId != qsl("default") && actionId != qsl("mail-reply-sender")) {
- return;
+ if (actionName == qsl("default")
+ || actionName == qsl("mail-reply-sender")) {
+ const auto manager = _manager;
+ crl::on_main(manager, [=] {
+ manager->notificationActivated(_peerId, _msgId);
+ });
+ } else if (actionName == qsl("mail-mark-read")) {
+ const auto manager = _manager;
+ crl::on_main(manager, [=] {
+ manager->notificationReplied(_peerId, _msgId, {});
+ });
}
-
- const auto manager = _manager;
- crl::on_main(manager, [=] {
- manager->notificationActivated(_peerId, _msgId);
- });
}
void NotificationData::notificationReplied(uint id, const QString &text) {
@@ -306,7 +383,8 @@ QDBusArgument &operator<<(
QDBusArgument &argument,
const NotificationData::ImageData &imageData) {
argument.beginStructure();
- argument << imageData.width
+ argument
+ << imageData.width
<< imageData.height
<< imageData.rowStride
<< imageData.hasAlpha
@@ -321,7 +399,8 @@ const QDBusArgument &operator>>(
const QDBusArgument &argument,
NotificationData::ImageData &imageData) {
argument.beginStructure();
- argument >> imageData.width
+ argument
+ >> imageData.width
>> imageData.height
>> imageData.rowStride
>> imageData.hasAlpha
@@ -331,20 +410,39 @@ const QDBusArgument &operator>>(
argument.endStructure();
return argument;
}
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+bool SkipAudio() {
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+ if (Supported()
+ && GetCapabilities().contains(qsl("inhibitions"))
+ && !InhibitedNotSupported) {
+ return Inhibited();
+ }
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+ return false;
+}
+
+bool SkipToast() {
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+ if (Supported()
+ && GetCapabilities().contains(qsl("inhibitions"))
+ && !InhibitedNotSupported) {
+ return Inhibited();
+ }
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+ return false;
+}
bool Supported() {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- static const auto Available = QDBusInterface(
- kService.utf16(),
- kObjectPath.utf16(),
- kInterface.utf16()
- ).isValid();
-
+ static const auto Available = !GetServerInformation().empty();
return Available;
-#else
+#else // !TDESKTOP_DISABLE_DBUS_INTEGRATION
return false;
-#endif
+#endif // TDESKTOP_DISABLE_DBUS_INTEGRATION
}
std::unique_ptr Create(
@@ -353,34 +451,36 @@ std::unique_ptr Create(
if (Global::NativeNotifications() && Supported()) {
return std::make_unique(system);
}
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
return nullptr;
}
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-Manager::Private::Private(Manager *manager, Type type)
+Manager::Private::Private(not_null manager, Type type)
: _cachedUserpics(type)
-, _manager(manager)
-, _notificationInterface(
- std::make_shared(
- kService.utf16(),
- kObjectPath.utf16(),
- kInterface.utf16())) {
+, _manager(manager) {
qDBusRegisterMetaType();
- const auto specificationVersion = ParseSpecificationVersion(
- GetServerInformation(_notificationInterface));
+ const auto serverInformation = GetServerInformation();
+ const auto capabilities = GetCapabilities();
- const auto capabilities = GetCapabilities(_notificationInterface);
+ if (!serverInformation.empty()) {
+ LOG(("Notification daemon product name: %1")
+ .arg(serverInformation[0]));
+
+ LOG(("Notification daemon vendor name: %1")
+ .arg(serverInformation[1]));
+
+ LOG(("Notification daemon version: %1")
+ .arg(serverInformation[2]));
- if (!specificationVersion.isNull()) {
LOG(("Notification daemon specification version: %1")
- .arg(specificationVersion.toString()));
+ .arg(serverInformation[3]));
}
- if (!capabilities.empty()) {
- const auto capabilitiesString = capabilities.join(", ");
- LOG(("Notification daemon capabilities: %1").arg(capabilitiesString));
+ if (!capabilities.isEmpty()) {
+ LOG(("Notification daemon capabilities: %1")
+ .arg(capabilities.join(", ")));
}
}
@@ -393,7 +493,6 @@ void Manager::Private::showNotification(
bool hideNameAndPhoto,
bool hideReplyButton) {
auto notification = std::make_shared(
- _notificationInterface,
_manager,
title,
subtitle,
@@ -421,7 +520,7 @@ void Manager::Private::showNotification(
i = _notifications.insert(peer->id, QMap());
}
_notifications[peer->id].insert(msgId, notification);
- if (!notification->show(hideNameAndPhoto)) {
+ if (!notification->show()) {
i = _notifications.find(peer->id);
if (i != _notifications.cend()) {
i->remove(msgId);
@@ -465,7 +564,7 @@ Manager::Private::~Private() {
clearAll();
}
-Manager::Manager(Window::Notifications::System *system)
+Manager::Manager(not_null system)
: NativeManager(system)
, _private(std::make_unique(this, Private::Type::Rounded)) {
}
@@ -501,7 +600,7 @@ void Manager::doClearAllFast() {
void Manager::doClearFromHistory(not_null history) {
_private->clearFromHistory(history);
}
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} // namespace Notifications
} // namespace Platform
diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
index dcda340bf..1005c0403 100644
--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
+++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
@@ -12,28 +12,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/weak_ptr.h"
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-#include
+#include
#include
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
namespace Platform {
namespace Notifications {
-inline bool SkipAudio() {
- return false;
-}
-
-inline bool SkipToast() {
- return false;
-}
-
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
class NotificationData : public QObject {
Q_OBJECT
public:
NotificationData(
- const std::shared_ptr ¬ificationInterface,
const base::weak_ptr &manager,
const QString &title,
const QString &subtitle,
@@ -47,8 +38,8 @@ public:
NotificationData(NotificationData &&other) = delete;
NotificationData &operator=(NotificationData &&other) = delete;
- bool show(bool hideNameAndPhoto);
- bool close();
+ bool show();
+ void close();
void setImage(const QString &imagePath);
struct ImageData {
@@ -59,13 +50,14 @@ public:
};
private:
- std::shared_ptr _notificationInterface;
+ QDBusConnection _dbusConnection;
base::weak_ptr _manager;
QString _title;
QString _body;
QStringList _actions;
QVariantMap _hints;
+ QString _imageKey;
uint _notificationId;
PeerId _peerId;
@@ -73,7 +65,7 @@ private:
private slots:
void notificationClosed(uint id);
- void notificationClicked(uint id, const QString &actionId);
+ void actionInvoked(uint id, const QString &actionName);
void notificationReplied(uint id, const QString &text);
};
@@ -91,7 +83,7 @@ class Manager
: public Window::Notifications::NativeManager
, public base::has_weak_ptr {
public:
- Manager(Window::Notifications::System *system);
+ Manager(not_null system);
void clearNotification(PeerId peerId, MsgId msgId);
~Manager();
@@ -116,7 +108,7 @@ private:
class Manager::Private {
public:
using Type = Window::Notifications::CachedUserpics::Type;
- explicit Private(Manager *manager, Type type);
+ explicit Private(not_null manager, Type type);
void showNotification(
not_null peer,
@@ -138,13 +130,12 @@ private:
Window::Notifications::CachedUserpics _cachedUserpics;
base::weak_ptr _manager;
- std::shared_ptr _notificationInterface;
};
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} // namespace Notifications
} // namespace Platform
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
Q_DECLARE_METATYPE(Platform::Notifications::NotificationData::ImageData)
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index c1b80e28a..ed3dde713 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -26,7 +26,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include
-#endif
+#include
+#include
+#include
+#include
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
#include
#include
@@ -44,9 +48,10 @@ namespace {
constexpr auto kDesktopFile = ":/misc/kotatogramdesktop.desktop"_cs;
constexpr auto kSnapLauncherDir = "/var/lib/snapd/desktop/applications/"_cs;
+constexpr auto kIconName = "telegram"_cs;
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
-void SandboxAutostart(bool autostart) {
+void SandboxAutostart(bool autostart, bool silent = false) {
QVariantMap options;
options["reason"] = tr::lng_settings_auto_start(tr::now);
options["autostart"] = autostart;
@@ -56,20 +61,29 @@ void SandboxAutostart(bool autostart) {
});
options["dbus-activatable"] = false;
- const auto requestBackgroundReply = QDBusInterface(
+ auto message = QDBusMessage::createMethodCall(
qsl("org.freedesktop.portal.Desktop"),
qsl("/org/freedesktop/portal/desktop"),
- qsl("org.freedesktop.portal.Background")
- ).call(qsl("RequestBackground"), QString(), options);
+ qsl("org.freedesktop.portal.Background"),
+ qsl("RequestBackground"));
- if (requestBackgroundReply.type() == QDBusMessage::ErrorMessage) {
- LOG(("Flatpak autostart error: %1")
- .arg(requestBackgroundReply.errorMessage()));
- } else if (requestBackgroundReply.type() != QDBusMessage::ReplyMessage) {
- LOG(("Flatpak autostart error: invalid reply"));
+ message.setArguments({
+ QString(),
+ options
+ });
+
+ if (silent) {
+ QDBusConnection::sessionBus().send(message);
+ } else {
+ const QDBusReply reply = QDBusConnection::sessionBus().call(
+ message);
+
+ if (!reply.isValid()) {
+ LOG(("Flatpak autostart error: %1").arg(reply.error().message()));
+ }
}
}
-#endif
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
bool RunShellCommand(const QByteArray &command) {
auto result = system(command.constData());
@@ -120,7 +134,10 @@ void FallbackFontConfig() {
#endif // TDESKTOP_USE_FONT_CONFIG_FALLBACK
}
-bool GenerateDesktopFile(const QString &targetPath, const QString &args) {
+bool GenerateDesktopFile(
+ const QString &targetPath,
+ const QString &args,
+ bool silent = false) {
DEBUG_LOG(("App Info: placing .desktop file to %1").arg(targetPath));
if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
@@ -142,7 +159,9 @@ bool GenerateDesktopFile(const QString &targetPath, const QString &args) {
fileText = s.readAll();
source.close();
} else {
- LOG(("App Error: Could not open '%1' for read").arg(sourceFile));
+ if (!silent) {
+ LOG(("App Error: Could not open '%1' for read").arg(sourceFile));
+ }
return false;
}
@@ -150,23 +169,28 @@ bool GenerateDesktopFile(const QString &targetPath, const QString &args) {
if (target.open(QIODevice::WriteOnly)) {
#ifdef DESKTOP_APP_USE_PACKAGED
fileText = fileText.replace(
- QRegularExpression(qsl("^Exec=(.*) -- %u$"),
+ QRegularExpression(
+ qsl("^Exec=(.*) -- %u$"),
QRegularExpression::MultilineOption),
qsl("Exec=\\1")
+ (args.isEmpty() ? QString() : ' ' + args));
-#else
+#else // DESKTOP_APP_USE_PACKAGED
fileText = fileText.replace(
- QRegularExpression(qsl("^TryExec=.*$"),
+ QRegularExpression(
+ qsl("^TryExec=.*$"),
QRegularExpression::MultilineOption),
qsl("TryExec=")
- + EscapeShell(QFile::encodeName(cExeDir() + cExeName())));
+ + QFile::encodeName(cExeDir() + cExeName())
+ .replace('\\', qsl("\\\\")));
fileText = fileText.replace(
- QRegularExpression(qsl("^Exec=.*$"),
+ QRegularExpression(
+ qsl("^Exec=.*$"),
QRegularExpression::MultilineOption),
qsl("Exec=")
+ EscapeShell(QFile::encodeName(cExeDir() + cExeName()))
+ .replace('\\', qsl("\\\\"))
+ (args.isEmpty() ? QString() : ' ' + args));
-#endif
+#endif // !DESKTOP_APP_USE_PACKAGED
target.write(fileText.toUtf8());
target.close();
@@ -175,7 +199,9 @@ bool GenerateDesktopFile(const QString &targetPath, const QString &args) {
return true;
} else {
- LOG(("App Error: Could not open '%1' for write").arg(targetFile));
+ if (!silent) {
+ LOG(("App Error: Could not open '%1' for write").arg(targetFile));
+ }
return false;
}
}
@@ -189,9 +215,7 @@ void SetApplicationIcon(const QIcon &icon) {
}
bool InSandbox() {
- static const auto Sandbox = QFileInfo::exists(
- QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)
- + qsl("/flatpak-info"));
+ static const auto Sandbox = QFileInfo::exists(qsl("/.flatpak-info"));
return Sandbox;
}
@@ -208,12 +232,18 @@ bool IsXDGDesktopPortalPresent() {
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop").isValid();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
return XDGDesktopPortalPresent;
}
bool UseXDGDesktopPortal() {
- static const auto UsePortal = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL")
- && IsXDGDesktopPortalPresent();
+ static const auto UsePortal = [&] {
+ const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL");
+ const auto portalPresent = IsXDGDesktopPortalPresent();
+
+ return envVar && portalPresent;
+ }();
+
return UsePortal;
}
@@ -309,7 +339,6 @@ QString GetLauncherBasename() {
return possibleBasenames[0];
}();
- LOG(("Launcher filename is %1.desktop").arg(LauncherBasename));
return LauncherBasename;
}
@@ -319,6 +348,55 @@ QString GetLauncherFilename() {
return LauncherFilename;
}
+QString GetIconName() {
+ static const auto IconName = InSandbox()
+ ? GetLauncherBasename()
+ : kIconName.utf16();
+ return IconName;
+}
+
+std::optional LastUserInputTime() {
+ // TODO: a fallback pure-X11 implementation, this one covers only major DEs on X11 and Wayland
+ // an example: https://stackoverflow.com/q/9049087
+#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
+ static auto NotSupported = false;
+
+ if (NotSupported) {
+ return std::nullopt;
+ }
+
+ static const auto message = QDBusMessage::createMethodCall(
+ qsl("org.freedesktop.ScreenSaver"),
+ qsl("/org/freedesktop/ScreenSaver"),
+ qsl("org.freedesktop.ScreenSaver"),
+ qsl("GetSessionIdleTime"));
+
+ const QDBusReply reply = QDBusConnection::sessionBus().call(
+ message);
+
+ constexpr auto notSupportedErrors = {
+ QDBusError::ServiceUnknown,
+ QDBusError::NotSupported,
+ };
+
+ if (reply.isValid()) {
+ return (crl::now() - static_cast(reply.value()));
+ } else if (ranges::contains(notSupportedErrors, reply.error().type())) {
+ NotSupported = true;
+ } else {
+ if (reply.error().type() == QDBusError::AccessDenied) {
+ NotSupported = true;
+ }
+
+ LOG(("Unable to get last user input time: %1: %2")
+ .arg(reply.error().name())
+ .arg(reply.error().message()));
+ }
+#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
+
+ return std::nullopt;
+}
+
} // namespace Platform
namespace {
@@ -395,15 +473,18 @@ QString getHomeDir() {
QString psAppDataPath() {
// We should not use ~/.TelegramDesktop, since it's a fork.
-
- // auto home = getHomeDir();
- // if (!home.isEmpty()) {
- // auto oldPath = home + qsl(".TelegramDesktop/");
- // auto oldSettingsBase = oldPath + qsl("tdata/settings");
- // if (QFile(oldSettingsBase + '0').exists() || QFile(oldSettingsBase + '1').exists()) {
- // return oldPath;
- // }
- // }
+ /*
+ auto home = getHomeDir();
+ if (!home.isEmpty()) {
+ auto oldPath = home + qsl(".TelegramDesktop/");
+ auto oldSettingsBase = oldPath + qsl("tdata/settings");
+ if (QFile(oldSettingsBase + '0').exists()
+ || QFile(oldSettingsBase + '1').exists()
+ || QFile(oldSettingsBase + 's').exists()) {
+ return oldPath;
+ }
+ }
+ */
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + '/';
}
@@ -432,8 +513,12 @@ int psFixPrevious() {
namespace Platform {
void start() {
+ LOG(("Launcher filename: %1").arg(GetLauncherFilename()));
FallbackFontConfig();
+ qputenv("PULSE_PROP_application.name", AppName.utf8());
+ qputenv("PULSE_PROP_application.icon_name", GetIconName().toLatin1());
+
#ifdef TDESKTOP_FORCE_GTK_FILE_DIALOG
LOG(("Checking for XDG Desktop Portal..."));
// this can give us a chance to use a proper file dialog for current session
@@ -454,24 +539,29 @@ void start() {
void finish() {
}
-void RegisterCustomScheme() {
+void RegisterCustomScheme(bool force) {
#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
- auto home = getHomeDir();
- if (home.isEmpty() || cAlphaVersion() || cExeName().isEmpty())
- return; // don't update desktop file for alpha version
- if (Core::UpdaterDisabled())
+ const auto home = getHomeDir();
+ if (home.isEmpty() || cExeName().isEmpty())
+ return;
+
+ static const auto disabledByEnv = qEnvironmentVariableIsSet(
+ "TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION");
+
+ // don't update desktop file for alpha version or if updater is disabled
+ if ((cAlphaVersion() || Core::UpdaterDisabled() || disabledByEnv)
+ && !force)
return;
const auto applicationsPath = QStandardPaths::writableLocation(
QStandardPaths::ApplicationsLocation) + '/';
-#ifndef TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
GenerateDesktopFile(applicationsPath, qsl("-- %u"));
const auto icons =
QStandardPaths::writableLocation(
QStandardPaths::GenericDataLocation)
- + qsl("/icons/");
+ + qsl("/icons/");
if (!QDir(icons).exists()) QDir().mkpath(icons);
@@ -485,10 +575,9 @@ void RegisterCustomScheme() {
}
if (!iconExists) {
if (QFile(qsl(":/gui/art/logo_256.png")).copy(icon)) {
- DEBUG_LOG(("App Info: Icon copied to 'tdata'"));
+ DEBUG_LOG(("App Info: Icon copied to '%1'").arg(icon));
}
}
-#endif // !TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
RunShellCommand("update-desktop-database "
+ EscapeShell(QFile::encodeName(applicationsPath)));
@@ -559,14 +648,14 @@ bool psShowOpenWithMenu(int x, int y, const QString &file) {
}
void psAutoStart(bool start, bool silent) {
- auto home = getHomeDir();
- if (home.isEmpty() || cAlphaVersion() || cExeName().isEmpty())
+ const auto home = getHomeDir();
+ if (home.isEmpty() || cExeName().isEmpty())
return;
if (InSandbox()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
- SandboxAutostart(start);
-#endif
+ SandboxAutostart(start, silent);
+#endif // !DESKTOP_APP_USE_PACKAGED
} else {
const auto autostart = [&] {
if (InSnap()) {
@@ -578,12 +667,12 @@ void psAutoStart(bool start, bool silent) {
} else {
return QStandardPaths::writableLocation(
QStandardPaths::GenericConfigLocation)
- + qsl("/autostart/");
+ + qsl("/autostart/");
}
}();
if (start) {
- GenerateDesktopFile(autostart, qsl("-autostart"));
+ GenerateDesktopFile(autostart, qsl("-autostart"), silent);
} else {
QFile::remove(autostart + GetLauncherFilename());
}
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h
index f878dac71..7f18032cb 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.h
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.h
@@ -35,9 +35,7 @@ QString SingleInstanceLocalServerName(const QString &hash);
QString GetLauncherBasename();
QString GetLauncherFilename();
-inline std::optional LastUserInputTime() {
- return std::nullopt;
-}
+QString GetIconName();
inline void IgnoreApplicationActivationRightNow() {
}
diff --git a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm
index cf10125b1..279a4e5df 100644
--- a/Telegram/SourceFiles/platform/mac/mac_touchbar.mm
+++ b/Telegram/SourceFiles/platform/mac/mac_touchbar.mm
@@ -481,6 +481,8 @@ void AppendEmojiPacks(std::vector &to) {
@implementation PinnedDialogButton {
rpl::lifetime _lifetime;
rpl::lifetime _peerChangedLifetime;
+ base::has_weak_ptr _guard;
+
bool isWaitingUserpicLoad;
}
@@ -518,7 +520,7 @@ void AppendEmojiPacks(std::vector &to) {
) | rpl::filter([](const Window::Theme::BackgroundUpdate &update) {
return update.paletteChanged();
}) | rpl::start_with_next([=] {
- crl::on_main([=] {
+ crl::on_main(&_guard, [=] {
if (_number <= kSavedMessagesId || UseEmptyUserpic(_peer)) {
[self updateUserpic];
} else if (_peer
diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm
index c8490da50..8b31672f2 100644
--- a/Telegram/SourceFiles/platform/mac/specific_mac.mm
+++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm
@@ -136,7 +136,7 @@ void RemoveQuarantine(const QString &path) {
removexattr(local.data(), kQuarantineAttribute, 0);
}
-void RegisterCustomScheme() {
+void RegisterCustomScheme(bool force) {
#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
OSStatus result = LSSetDefaultHandlerForURLScheme(CFSTR("tg"), (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]);
DEBUG_LOG(("App Info: set default handler for 'tg' scheme result: %1").arg(result));
diff --git a/Telegram/SourceFiles/platform/platform_specific.h b/Telegram/SourceFiles/platform/platform_specific.h
index f5650f969..18ebd6c0c 100644
--- a/Telegram/SourceFiles/platform/platform_specific.h
+++ b/Telegram/SourceFiles/platform/platform_specific.h
@@ -28,7 +28,7 @@ enum class SystemSettingsType {
void SetWatchingMediaKeys(bool watching);
void SetApplicationIcon(const QIcon &icon);
-void RegisterCustomScheme();
+void RegisterCustomScheme(bool force = false);
PermissionStatus GetPermissionStatus(PermissionType type);
void RequestPermission(PermissionType type, Fn resultCallback);
void OpenSystemSettingsForPermission(PermissionType type);
diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp
index 485ecae2a..1998b4478 100644
--- a/Telegram/SourceFiles/platform/win/specific_win.cpp
+++ b/Telegram/SourceFiles/platform/win/specific_win.cpp
@@ -404,7 +404,7 @@ namespace {
namespace Platform {
-void RegisterCustomScheme() {
+void RegisterCustomScheme(bool force) {
if (cExeName().isEmpty()) {
return;
}
diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp
index 7b0149eb1..fe728c61f 100644
--- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp
+++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp
@@ -192,7 +192,7 @@ void GroupMembersWidget::refreshMembers() {
fillChatMembers(chat);
} else if (const auto megagroup = peer()->asMegagroup()) {
auto &megagroupInfo = megagroup->mgInfo;
- if (megagroupInfo->lastParticipants.empty() || megagroup->lastParticipantsCountOutdated()) {
+ if (megagroup->lastParticipantsRequestNeeded()) {
Auth().api().requestLastParticipants(megagroup);
}
fillMegagroupMembers(megagroup);
diff --git a/Telegram/SourceFiles/settings/settings_codes.cpp b/Telegram/SourceFiles/settings/settings_codes.cpp
index 505146b57..e50471fa8 100644
--- a/Telegram/SourceFiles/settings/settings_codes.cpp
+++ b/Telegram/SourceFiles/settings/settings_codes.cpp
@@ -112,10 +112,12 @@ auto GenerateCodes() {
}
});
});
+#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
codes.emplace(qsl("registertg"), [](::Main::Session *session) {
- Platform::RegisterCustomScheme();
+ Platform::RegisterCustomScheme(true);
Ui::Toast::Show("Forced custom scheme register.");
});
+#endif // !TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
codes.emplace(qsl("export"), [](::Main::Session *session) {
session->data().startExport();
});
diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp
index 2a3039f90..573cc9bef 100644
--- a/Telegram/SourceFiles/storage/localstorage.cpp
+++ b/Telegram/SourceFiles/storage/localstorage.cpp
@@ -3167,7 +3167,7 @@ void writeFileLocation(MediaKey location, const FileLocation &local) {
return;
}
if (i.value().first != location) {
- for (FileLocations::iterator j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first);) {
+ for (FileLocations::iterator j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first); ++j) {
if (j.value() == i.value().second) {
_fileLocations.erase(j);
break;
@@ -3682,7 +3682,21 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
// Read orders of installed and featured stickers.
if (outOrder) {
- stickers.stream >> *outOrder;
+ auto outOrderCount = quint32();
+ stickers.stream >> outOrderCount;
+ if (!_checkStreamStatus(stickers.stream) || outOrderCount > 1000) {
+ return failed();
+ }
+ outOrder->reserve(outOrderCount);
+ for (auto i = 0; i != outOrderCount; ++i) {
+ auto value = uint64();
+ stickers.stream >> value;
+ if (!_checkStreamStatus(stickers.stream)) {
+ outOrder->clear();
+ return failed();
+ }
+ outOrder->push_back(value);
+ }
}
if (!_checkStreamStatus(stickers.stream)) {
return failed();
diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp
index 3064af274..ff6aad0c8 100644
--- a/Telegram/SourceFiles/window/main_window.cpp
+++ b/Telegram/SourceFiles/window/main_window.cpp
@@ -117,9 +117,9 @@ QIcon CreateOfficialIcon(Main::Account *account) {
QIcon CreateIcon(Main::Account *account) {
auto result = CreateOfficialIcon(account);
- if (Platform::IsLinux()) {
- return QIcon::fromTheme("kotatogram", result);
- }
+#ifdef Q_OS_LINUX
+ return QIcon::fromTheme(Platform::GetIconName(), result);
+#endif
return result;
}
diff --git a/Telegram/build/version b/Telegram/build/version
index 37dc9a595..02fc021df 100644
--- a/Telegram/build/version
+++ b/Telegram/build/version
@@ -1,7 +1,7 @@
-AppVersion 1009019
+AppVersion 1009020
AppVersionStrMajor 1.9
-AppVersionStrSmall 1.9.19
-AppVersionStr 1.9.19
+AppVersionStrSmall 1.9.20
+AppVersionStr 1.9.20
BetaChannel 1
AlphaVersion 0
-AppVersionOriginal 1.9.19.beta
+AppVersionOriginal 1.9.20.beta
diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake
index 4c18955f4..c2d34c18a 100644
--- a/Telegram/cmake/telegram_options.cmake
+++ b/Telegram/cmake/telegram_options.cmake
@@ -7,8 +7,6 @@
option(TDESKTOP_FORCE_GTK_FILE_DIALOG "Force using GTK file dialog (Linux only)." OFF)
option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL scheme handler registration." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_DISABLE_NETWORK_PROXY "Disable all code for working through Socks5 or MTProxy." OFF)
-option(TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION "Disable automatic '.desktop' file generation (Linux only)." ${DESKTOP_APP_USE_PACKAGED})
-option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." ON)
option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundled one." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_API_TEST "Use test API credentials." OFF)
set(TDESKTOP_API_ID "0" CACHE STRING "Provide 'api_id' for the Telegram API access.")
@@ -43,12 +41,12 @@ if (TDESKTOP_API_ID STREQUAL "0" OR TDESKTOP_API_HASH STREQUAL "")
" ")
endif()
-if (NOT DESKTOP_APP_SPECIAL_TARGET STREQUAL "")
+if (NOT DESKTOP_APP_USE_PACKAGED)
set(TDESKTOP_FORCE_GTK_FILE_DIALOG ON)
endif()
-if (TDESKTOP_FORCE_GTK_FILE_DIALOG)
- set(TDESKTOP_DISABLE_GTK_INTEGRATION OFF)
+if (NOT TDESKTOP_FORCE_GTK_FILE_DIALOG)
+ set(TDESKTOP_DISABLE_GTK_INTEGRATION ON)
endif()
if (DESKTOP_APP_DISABLE_SPELLCHECK)
@@ -77,10 +75,6 @@ if (TDESKTOP_DISABLE_NETWORK_PROXY)
target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_NETWORK_PROXY)
endif()
-if (TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION)
- target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION)
-endif()
-
if (TDESKTOP_DISABLE_GTK_INTEGRATION)
target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_GTK_INTEGRATION)
endif()
diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie
index 17b6a6d53..0f6c9f4ac 160000
--- a/Telegram/lib_lottie
+++ b/Telegram/lib_lottie
@@ -1 +1 @@
-Subproject commit 17b6a6d53252b3e3ff02b113e352c152bd697896
+Subproject commit 0f6c9f4acbdc2412a1a941b4ee96b150589b8369
diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck
index dbb92ddbe..25f44d999 160000
--- a/Telegram/lib_spellcheck
+++ b/Telegram/lib_spellcheck
@@ -1 +1 @@
-Subproject commit dbb92ddbef82988d426ef7a0ffa40a698cdc3fd3
+Subproject commit 25f44d99915101e7a832cb38bd9b2cee47508c94
diff --git a/changelog.txt b/changelog.txt
index 990ae6a71..4ac9906fc 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,9 @@
+1.9.20 beta (14.03.20)
+
+- Fix crash in shared links search.
+- Fix blurred thumbnails in albums with video files.
+- Fix a possible crash in animated stickers rendering.
+
1.9.19 beta (25.02.20)
- Bug fixes and other minor improvements.
diff --git a/cmake b/cmake
index 81e27ccc0..9280064cc 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit 81e27ccc0e7bf27405569ce98582860dfc9ea9bb
+Subproject commit 9280064cc8a5e8f8acc4cdda3a6a905379d3df0d
diff --git a/docs/building-cmake.md b/docs/building-cmake.md
index af59c2d30..0583ae4f3 100644
--- a/docs/building-cmake.md
+++ b/docs/building-cmake.md
@@ -1,4 +1,4 @@
-## Build instructions for GYP/CMake under Ubuntu 14.04
+## Build instructions for CMake under Ubuntu 14.04
### Prepare folder
@@ -17,7 +17,7 @@ You will need GCC 8 installed. To install them and all the required dependencies
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
- libvorbis-dev libenchant-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
+ libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libatspi2.0-dev \
libxcb-render-util0-dev libxcb-util0-dev libxcb-xkb-dev libxrender-dev \
libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libegl1-mesa-dev \
@@ -198,7 +198,7 @@ Go to ***BuildPath*** and run
git clone git://repo.or.cz/openal-soft.git
cd openal-soft
- git checkout openal-soft-1.19.1
+ git checkout openal-soft-1.20.1
cd build
if [ `uname -p` == "i686" ]; then
cmake -D LIBTYPE:STRING=STATIC -D ALSOFT_UTILS:BOOL=OFF ..
@@ -214,7 +214,7 @@ Go to ***BuildPath*** and run
git checkout OpenSSL_1_1_1-stable
./config --prefix=/usr/local/desktop-app/openssl-1.1.1
make $MAKE_THREADS_CNT
- sudo make install
+ sudo make install_sw
cd ..
git clone https://github.com/xkbcommon/libxkbcommon.git
diff --git a/docs/building-msvc.md b/docs/building-msvc.md
index e23677577..60b47713f 100644
--- a/docs/building-msvc.md
+++ b/docs/building-msvc.md
@@ -18,7 +18,6 @@ You will require **api_id** and **api_hash** to access the Telegram API servers.
## Install third party software
-Strawberry
* Download **Strawberry Perl** installer from [http://strawberryperl.com/](http://strawberryperl.com/) and install to ***BuildPath*\\ThirdParty\\Strawberry**
* Download **NASM** installer from [http://www.nasm.us](http://www.nasm.us) and install to ***BuildPath*\\ThirdParty\\NASM**
* Download **Yasm** executable from [http://yasm.tortall.net/Download.html](http://yasm.tortall.net/Download.html), rename to *yasm.exe* and put to ***BuildPath*\\ThirdParty\\yasm**
diff --git a/docs/building-qtcreator.md b/docs/building-qtcreator.md
index aa592148f..71fe151e1 100644
--- a/docs/building-qtcreator.md
+++ b/docs/building-qtcreator.md
@@ -30,7 +30,7 @@ By git – in Terminal go to **/home/user/TBuild** and run
Install dev libraries
- sudo apt-get install libexif-dev liblzma-dev libz-dev libssl-dev libappindicator-dev libunity-dev libenchant-dev
+ sudo apt-get install libexif-dev liblzma-dev libz-dev libssl-dev libappindicator-dev libunity-dev
#### zlib 1.2.8
diff --git a/docs/building-xcode.md b/docs/building-xcode.md
index d4f374265..fba6b3064 100644
--- a/docs/building-xcode.md
+++ b/docs/building-xcode.md
@@ -1,4 +1,4 @@
-## Build instructions for Xcode 10.1
+## Build instructions for Xcode 11
### Prepare folder
@@ -8,18 +8,14 @@ Choose a folder for the future build, for example **/Users/user/TBuild**. It wil
You will require **api_id** and **api_hash** to access the Telegram API servers. To learn how to obtain them [click here][api_credentials].
-### Download libraries
-
-Download [**xz-5.0.5**](http://tukaani.org/xz/xz-5.0.5.tar.gz) and unpack to ***BuildPath*/Libraries/macos/xz-5.0.5**
-
-Download [**libiconv-1.15**](http://www.gnu.org/software/libiconv/#downloading) and unpack to ***BuildPath*/Libraries/macos/libiconv-1.15**
-
### Clone source code and prepare libraries
Go to ***BuildPath*** and run
MAKE_THREADS_CNT=-j8
MACOSX_DEPLOYMENT_TARGET=10.12
+ UNGUARDED="-Werror=unguarded-availability-new"
+ MIN_VER="-mmacosx-version-min=10.12"
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install automake cmake fdk-aac git lame libass libtool libvorbis libvpx ninja opus sdl shtool texi2html theora wget x264 xvid yasm pkg-config gnu-tar
@@ -45,24 +41,29 @@ Go to ***BuildPath*** and run
sudo ./setup.py install
cd ../..
+ mkdir -p Libraries/macos
cd Libraries/macos
LibrariesPath=`pwd`
git clone https://github.com/desktop-app/patches.git
cd patches
git checkout 4f1cffb
- cd ../
+ cd ..
git clone --branch 0.10.0 https://github.com/ericniebler/range-v3
- cd xz-5.0.5
- CFLAGS="-mmacosx-version-min=10.12" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos
+ xz_ver=5.2.4
+ wget https://tukaani.org/xz/xz-$xz_ver.tar.gz
+ tar -xvzf xz-$xz_ver.tar.gz
+ rm xz-$xz_ver.tar.gz
+ cd xz-$xz_ver
+ CFLAGS="$MIN_VER" LDFLAGS="$MIN_VER" ./configure --prefix=/usr/local/macos
make $MAKE_THREADS_CNT
sudo make install
cd ..
git clone https://github.com/desktop-app/zlib.git
cd zlib
- CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos
+ CFLAGS="$MIN_VER $UNGUARDED" LDFLAGS="$MIN_VER" ./configure --prefix=/usr/local/macos
make $MAKE_THREADS_CNT
sudo make install
cd ..
@@ -70,7 +71,7 @@ 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_1-stable
- ./Configure --prefix=/usr/local/macos darwin64-x86_64-cc -static -mmacosx-version-min=10.12
+ ./Configure --prefix=/usr/local/macos darwin64-x86_64-cc -static $MIN_VER
make build_libs $MAKE_THREADS_CNT
cd ..
@@ -78,13 +79,17 @@ Go to ***BuildPath*** and run
cd opus
git checkout v1.3
./autogen.sh
- CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --prefix=/usr/local/macos
+ CFLAGS="$MIN_VER $UNGUARDED" CPPFLAGS="$MIN_VER $UNGUARDED" LDFLAGS="$MIN_VER" ./configure --prefix=/usr/local/macos
make $MAKE_THREADS_CNT
sudo make install
cd ..
- cd libiconv-1.15
- CFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" CPPFLAGS="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" LDFLAGS="-mmacosx-version-min=10.12" ./configure --enable-static --prefix=/usr/local/macos
+ libiconv_ver=1.16
+ wget https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$libiconv_ver.tar.gz
+ tar -xvzf libiconv-$libiconv_ver.tar.gz
+ rm libiconv-$libiconv_ver.tar.gz
+ cd libiconv-$libiconv_ver
+ CFLAGS="$MIN_VER $UNGUARDED" CPPFLAGS="$MIN_VER $UNGUARDED" LDFLAGS="$MIN_VER" ./configure --enable-static --prefix=/usr/local/macos
make $MAKE_THREADS_CNT
sudo make install
cd ..
@@ -97,9 +102,9 @@ Go to ***BuildPath*** and run
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig
./configure --prefix=/usr/local/macos \
- --extra-cflags="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" \
- --extra-cxxflags="-mmacosx-version-min=10.12 -Werror=unguarded-availability-new" \
- --extra-ldflags="-mmacosx-version-min=10.12" \
+ --extra-cflags="$MIN_VER $UNGUARDED" \
+ --extra-cxxflags="$MIN_VER $UNGUARDED" \
+ --extra-ldflags="$MIN_VER" \
--enable-protocol=file --enable-libopus \
--disable-programs \
--disable-doc \
@@ -205,7 +210,7 @@ Go to ***BuildPath*** and run
cd openal-soft
git checkout v1.19
cd build
- CFLAGS='-Werror=unguarded-availability-new' CPPFLAGS='-Werror=unguarded-availability-new' cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr/local/macos -D ALSOFT_EXAMPLES=OFF -D LIBTYPE:STRING=STATIC -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.12 ..
+ CFLAGS=$UNGUARDED CPPFLAGS=$UNGUARDED cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr/local/macos -D ALSOFT_EXAMPLES=OFF -D LIBTYPE:STRING=STATIC -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.12 ..
make $MAKE_THREADS_CNT
sudo make install
cd ../..
@@ -241,7 +246,19 @@ Go to ***BuildPath*** and run
git apply ../../patches/qtbase_5_12_5.diff
cd ..
- ./configure -prefix "/usr/local/desktop-app/Qt-5.12.5" -debug-and-release -force-debug-info -opensource -confirm-license -static -opengl desktop -no-openssl -securetransport -nomake examples -nomake tests -platform macx-clang
+ ./configure -prefix "/usr/local/desktop-app/Qt-5.12.5" \
+ -debug-and-release \
+ -force-debug-info \
+ -opensource \
+ -confirm-license \
+ -static \
+ -opengl desktop \
+ -no-openssl \
+ -securetransport \
+ -nomake examples \
+ -nomake tests \
+ -platform macx-clang
+
make $MAKE_THREADS_CNT
sudo make install
cd ..
diff --git a/lib/xdg/kotatogramdesktop.appdata.xml b/lib/xdg/kotatogramdesktop.appdata.xml.in
similarity index 96%
rename from lib/xdg/kotatogramdesktop.appdata.xml
rename to lib/xdg/kotatogramdesktop.appdata.xml.in
index 25c3d4127..53f502004 100644
--- a/lib/xdg/kotatogramdesktop.appdata.xml
+++ b/lib/xdg/kotatogramdesktop.appdata.xml.in
@@ -20,7 +20,7 @@
https://github.com/kotatogram/kotatogram-desktop/issues
- https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/docs/assets/preview.png
+ https://github.com/kotatogram/kotatogram-desktop/blob/dev/docs/assets/ktg_preview.png
@@ -98,6 +98,7 @@
none
none
+ @TDESKTOP_LAUNCHER_BASENAME@.desktop
kotatogram-desktop
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 2629e4f36..8d9dc3b28 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -6,6 +6,13 @@ base: core18
grade: stable
confinement: strict
+architectures:
+ - build-on: amd64
+ - build-on: i386
+ - build-on: arm64
+ - build-on: armhf
+ - build-on: ppc64el
+
apps:
kotatogram-desktop:
command: bin/desktop-launch kotatogram-desktop
@@ -14,9 +21,12 @@ apps:
environment:
# Use GTK3 cursor theme, icon theme and open/save file dialogs.
QT_QPA_PLATFORMTHEME: gtk3
- # Wayland support is still too bad.
+ # The blocker is https://github.com/ubuntu/snapcraft-desktop-helpers/issues/172
DISABLE_WAYLAND: 1
plugs:
+ - alsa
+ - audio-playback
+ - audio-record
- desktop
- desktop-legacy
- home
@@ -44,6 +54,10 @@ plugs:
target: $SNAP/data-dir/sounds
default-provider: gtk-common-themes
+layout:
+ /usr/share/alsa:
+ bind: $SNAP/usr/share/alsa
+
parts:
kotatogram:
plugin: cmake
@@ -56,17 +70,11 @@ parts:
- qtbase5-private-dev
- libmapbox-variant-dev
- libasound2-dev
- - libavcodec-dev
- - libavformat-dev
- - libavutil-dev
- - libswscale-dev
- - libswresample-dev
- libdbusmenu-qt5-dev
- libhunspell-dev
- liblz4-dev
- liblzma-dev
- libminizip-dev
- - libopenal-dev
- libopus-dev
- libpulse-dev
- libssl-dev
@@ -74,17 +82,11 @@ parts:
stage-packages:
- qt5-image-formats-plugins
- libasound2
- - libavcodec57
- - libavformat57
- - libavutil55
- - libswscale4
- - libswresample2
- libdbusmenu-qt5-2
- libhunspell-1.6-0
- liblz4-1
- liblzma5
- libminizip1
- - libopenal1
- libopus0
- libpulse0
- libssl1.1
@@ -119,9 +121,16 @@ parts:
after:
- cmake
- desktop-qt5
+ - ffmpeg
+ - openal
- range-v3
- xxhash
+ patches:
+ plugin: dump
+ source: Telegram/Patches
+ prime: [-./*]
+
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
source-subdir: qt
@@ -176,6 +185,138 @@ parts:
- libtinfo5
prime: [-./*]
+ ffmpeg:
+ source: https://github.com/FFmpeg/FFmpeg.git
+ source-depth: 1
+ source-branch: release/4.2
+ plugin: autotools
+ build-packages:
+ - libtool
+ - pkg-config
+ - texi2html
+ - yasm
+ - libass-dev
+ - libfreetype6-dev
+ - libgpac-dev
+ - liblzma-dev
+ - libopus-dev
+ - libsdl1.2-dev
+ - libtheora-dev
+ - libva-dev
+ - libvdpau-dev
+ - libvorbis-dev
+ - libxcb1-dev
+ - libxcb-shm0-dev
+ - libxcb-xfixes0-dev
+ - zlib1g-dev
+ stage-packages:
+ - freeglut3
+ - libass9
+ - libfreetype6
+ - libgpac4
+ - liblzma5
+ - libopus0
+ - libslang2
+ - libsdl1.2debian
+ - libtheora0
+ - libva2
+ - libva-drm2
+ - libvdpau1
+ - libvorbis0a
+ - libxcb1
+ - libxcb-shm0
+ - libxcb-xfixes0
+ - zlib1g
+ configflags:
+ - --prefix=/usr
+ - --disable-debug
+ - --disable-programs
+ - --disable-doc
+ - --disable-everything
+ - --disable-neon
+ - --enable-gpl
+ - --enable-version3
+ - --enable-libopus
+ - --enable-decoder=aac
+ - --enable-decoder=aac_latm
+ - --enable-decoder=aasc
+ - --enable-decoder=flac
+ - --enable-decoder=gif
+ - --enable-decoder=h264
+ - --enable-decoder=h264_vdpau
+ - --enable-decoder=mp1
+ - --enable-decoder=mp1float
+ - --enable-decoder=mp2
+ - --enable-decoder=mp2float
+ - --enable-decoder=mp3
+ - --enable-decoder=mp3adu
+ - --enable-decoder=mp3adufloat
+ - --enable-decoder=mp3float
+ - --enable-decoder=mp3on4
+ - --enable-decoder=mp3on4float
+ - --enable-decoder=mpeg4
+ - --enable-decoder=mpeg4_vdpau
+ - --enable-decoder=msmpeg4v2
+ - --enable-decoder=msmpeg4v3
+ - --enable-decoder=opus
+ - --enable-decoder=vorbis
+ - --enable-decoder=wavpack
+ - --enable-decoder=wmalossless
+ - --enable-decoder=wmapro
+ - --enable-decoder=wmav1
+ - --enable-decoder=wmav2
+ - --enable-decoder=wmavoice
+ - --enable-encoder=libopus
+ - --enable-hwaccel=h264_vaapi
+ - --enable-hwaccel=h264_vdpau
+ - --enable-hwaccel=mpeg4_vaapi
+ - --enable-hwaccel=mpeg4_vdpau
+ - --enable-parser=aac
+ - --enable-parser=aac_latm
+ - --enable-parser=flac
+ - --enable-parser=h264
+ - --enable-parser=mpeg4video
+ - --enable-parser=mpegaudio
+ - --enable-parser=opus
+ - --enable-parser=vorbis
+ - --enable-demuxer=aac
+ - --enable-demuxer=flac
+ - --enable-demuxer=gif
+ - --enable-demuxer=h264
+ - --enable-demuxer=mov
+ - --enable-demuxer=mp3
+ - --enable-demuxer=ogg
+ - --enable-demuxer=wav
+ - --enable-muxer=ogg
+ - --enable-muxer=opus
+ override-pull: |
+ snapcraftctl pull
+ patch -p1 < "$SNAPCRAFT_STAGE/ffmpeg.diff"
+ after:
+ - patches
+
+ openal:
+ source: https://github.com/kcat/openal-soft.git
+ source-depth: 1
+ source-tag: openal-soft-1.20.1
+ plugin: cmake
+ build-packages:
+ - libasound2-dev
+ - libpulse-dev
+ - libsndio-dev
+ - portaudio19-dev
+ stage-packages:
+ - libasound2
+ - libpulse0
+ - libportaudio2
+ - libsndio6.1
+ configflags:
+ - -DCMAKE_BUILD_TYPE=Release
+ - -DALSOFT_EXAMPLES=OFF
+ - -DALSOFT_TESTS=OFF
+ - -DALSOFT_UTILS=OFF
+ - -DALSOFT_CONFIG=OFF
+
range-v3:
source: https://github.com/ericniebler/range-v3.git
source-depth: 1