init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
This commit is contained in:
322
Telegram/lib_base/CMakeLists.txt
Normal file
322
Telegram/lib_base/CMakeLists.txt
Normal file
@@ -0,0 +1,322 @@
|
||||
# This file is part of Desktop App Toolkit,
|
||||
# a set of libraries for developing nice desktop applications.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
|
||||
add_library(lib_base STATIC)
|
||||
add_library(desktop-app::lib_base ALIAS lib_base)
|
||||
init_target(lib_base)
|
||||
|
||||
add_library(lib_base_crash_report_writer STATIC)
|
||||
add_library(desktop-app::lib_base_crash_report_writer ALIAS lib_base_crash_report_writer)
|
||||
init_target(lib_base_crash_report_writer)
|
||||
|
||||
get_filename_component(src_loc . REALPATH)
|
||||
|
||||
target_precompile_headers(lib_base PRIVATE $<$<COMPILE_LANGUAGE:CXX,OBJCXX>:${src_loc}/base/base_pch.h>)
|
||||
nice_target_sources(lib_base ${src_loc}
|
||||
PRIVATE
|
||||
base/platform/linux/base_battery_saving_linux.cpp
|
||||
base/platform/linux/base_battery_saving_linux.h
|
||||
base/platform/linux/base_file_utilities_linux.cpp
|
||||
base/platform/linux/base_file_utilities_linux.h
|
||||
base/platform/linux/base_global_shortcuts_linux.cpp
|
||||
base/platform/linux/base_global_shortcuts_linux.h
|
||||
base/platform/linux/base_haptic_linux.cpp
|
||||
base/platform/linux/base_haptic_linux.h
|
||||
base/platform/linux/base_info_linux.cpp
|
||||
base/platform/linux/base_info_linux.h
|
||||
base/platform/linux/base_last_input_linux.cpp
|
||||
base/platform/linux/base_last_input_linux.h
|
||||
base/platform/linux/base_layout_switch_linux.cpp
|
||||
base/platform/linux/base_layout_switch_linux.h
|
||||
base/platform/linux/base_linux_allocation_tracer.cpp
|
||||
base/platform/linux/base_linux_allocation_tracer.h
|
||||
base/platform/linux/base_linux_app_launch_context.cpp
|
||||
base/platform/linux/base_linux_app_launch_context.h
|
||||
base/platform/linux/base_linux_dbus_utilities.cpp
|
||||
base/platform/linux/base_linux_dbus_utilities.h
|
||||
base/platform/linux/base_linux_library.cpp
|
||||
base/platform/linux/base_linux_library.h
|
||||
base/platform/linux/base_linux_xcb_utilities.cpp
|
||||
base/platform/linux/base_linux_xcb_utilities.h
|
||||
base/platform/linux/base_linux_xdg_activation_token.cpp
|
||||
base/platform/linux/base_linux_xdg_activation_token.h
|
||||
base/platform/linux/base_linux_xdp_utilities.cpp
|
||||
base/platform/linux/base_linux_xdp_utilities.h
|
||||
base/platform/linux/base_linux_xsettings.cpp
|
||||
base/platform/linux/base_linux_xsettings.h
|
||||
base/platform/linux/base_network_reachability_linux.cpp
|
||||
base/platform/linux/base_power_save_blocker_linux.cpp
|
||||
base/platform/linux/base_power_save_blocker_linux.h
|
||||
base/platform/linux/base_process_linux.cpp
|
||||
base/platform/linux/base_process_linux.h
|
||||
base/platform/linux/base_system_media_controls_linux.cpp
|
||||
base/platform/linux/base_system_unlock_linux.cpp
|
||||
base/platform/linux/base_system_unlock_linux.h
|
||||
base/platform/linux/base_url_scheme_linux.cpp
|
||||
base/platform/linux/base_url_scheme_linux.h
|
||||
base/platform/mac/base_battery_saving_mac.mm
|
||||
base/platform/mac/base_battery_saving_mac.h
|
||||
base/platform/mac/base_confirm_quit.h
|
||||
base/platform/mac/base_confirm_quit.mm
|
||||
base/platform/mac/base_custom_app_icon_mac.h
|
||||
base/platform/mac/base_custom_app_icon_mac.mm
|
||||
base/platform/mac/base_file_utilities_mac.h
|
||||
base/platform/mac/base_file_utilities_mac.mm
|
||||
base/platform/mac/base_global_shortcuts_mac.mm
|
||||
base/platform/mac/base_global_shortcuts_mac.h
|
||||
base/platform/mac/base_haptic_mac.h
|
||||
base/platform/mac/base_haptic_mac.mm
|
||||
base/platform/mac/base_info_mac.h
|
||||
base/platform/mac/base_info_mac.mm
|
||||
base/platform/mac/base_last_input_mac.h
|
||||
base/platform/mac/base_last_input_mac.mm
|
||||
base/platform/mac/base_layout_switch_mac.h
|
||||
base/platform/mac/base_layout_switch_mac.mm
|
||||
base/platform/mac/base_network_reachability_mac.mm
|
||||
base/platform/mac/base_power_save_blocker_mac.h
|
||||
base/platform/mac/base_power_save_blocker_mac.mm
|
||||
base/platform/mac/base_process_mac.h
|
||||
base/platform/mac/base_process_mac.mm
|
||||
base/platform/mac/base_system_media_controls_mac.mm
|
||||
base/platform/mac/base_system_unlock_mac.h
|
||||
base/platform/mac/base_system_unlock_mac.mm
|
||||
base/platform/mac/base_url_scheme_mac.h
|
||||
base/platform/mac/base_url_scheme_mac.mm
|
||||
base/platform/mac/base_utilities_mac.h
|
||||
base/platform/mac/base_utilities_mac.mm
|
||||
base/platform/win/base_battery_saving_win.cpp
|
||||
base/platform/win/base_battery_saving_win.h
|
||||
base/platform/win/base_file_utilities_win.cpp
|
||||
base/platform/win/base_file_utilities_win.h
|
||||
base/platform/win/base_global_shortcuts_win.cpp
|
||||
base/platform/win/base_global_shortcuts_win.h
|
||||
base/platform/win/base_haptic_win.cpp
|
||||
base/platform/win/base_haptic_win.h
|
||||
base/platform/win/base_info_win.cpp
|
||||
base/platform/win/base_info_win.h
|
||||
base/platform/win/base_last_input_win.cpp
|
||||
base/platform/win/base_last_input_win.h
|
||||
base/platform/win/base_layout_switch_win.cpp
|
||||
base/platform/win/base_layout_switch_win.h
|
||||
base/platform/win/base_network_reachability_win.cpp
|
||||
base/platform/win/base_power_save_blocker_win.cpp
|
||||
base/platform/win/base_power_save_blocker_win.h
|
||||
base/platform/win/base_process_win.cpp
|
||||
base/platform/win/base_process_win.h
|
||||
base/platform/win/base_system_media_controls_win.cpp
|
||||
base/platform/win/base_system_unlock_win.cpp
|
||||
base/platform/win/base_system_unlock_win.h
|
||||
base/platform/win/base_url_scheme_win.cpp
|
||||
base/platform/win/base_url_scheme_win.h
|
||||
base/platform/win/base_windows_co_task_mem.h
|
||||
base/platform/win/base_windows_gdiplus_h.h
|
||||
base/platform/win/base_windows_rpcndr_h.h
|
||||
base/platform/win/base_windows_safe_library.cpp
|
||||
base/platform/win/base_windows_safe_library.h
|
||||
base/platform/win/base_windows_shlobj_h.h
|
||||
base/platform/win/base_windows_winrt.cpp
|
||||
base/platform/win/base_windows_winrt.h
|
||||
base/platform/win/base_windows_wrl.cpp
|
||||
base/platform/win/base_windows_wrl.h
|
||||
base/platform/win/wrl/wrl_implements_h.h
|
||||
base/platform/win/wrl/wrl_module_h.h
|
||||
base/platform/base_platform_custom_app_icon.h
|
||||
base/platform/base_platform_global_shortcuts.h
|
||||
base/platform/base_platform_haptic.h
|
||||
base/platform/base_platform_info.cpp
|
||||
base/platform/base_platform_info.h
|
||||
base/platform/base_platform_last_input.h
|
||||
base/platform/base_platform_layout_switch.h
|
||||
base/platform/base_platform_network_reachability.h
|
||||
base/platform/base_platform_file_utilities.h
|
||||
base/platform/base_platform_power_save_blocker.h
|
||||
base/platform/base_platform_process.h
|
||||
base/platform/base_platform_url_scheme.h
|
||||
base/platform/base_platform_system_media_controls.h
|
||||
base/algorithm.cpp
|
||||
base/algorithm.h
|
||||
base/assertion.cpp
|
||||
base/assertion.h
|
||||
base/atomic.h
|
||||
base/base_file_utilities.cpp
|
||||
base/base_file_utilities.h
|
||||
base/basic_types.h
|
||||
base/battery_saving.cpp
|
||||
base/battery_saving.h
|
||||
base/binary_guard.h
|
||||
base/build_config.h
|
||||
base/bytes.cpp
|
||||
base/bytes.h
|
||||
base/call_delayed.cpp
|
||||
base/call_delayed.h
|
||||
base/crc32hash.cpp
|
||||
base/crc32hash.h
|
||||
base/concurrent_timer.cpp
|
||||
base/concurrent_timer.h
|
||||
base/const_string.h
|
||||
base/custom_app_icon.h
|
||||
base/custom_delete.h
|
||||
base/debug_destroy_informer.h
|
||||
base/debug_log.cpp
|
||||
base/debug_log.h
|
||||
base/enum_mask.h
|
||||
base/event_filter.cpp
|
||||
base/event_filter.h
|
||||
base/expected.h
|
||||
base/file_lock.h
|
||||
base/file_lock_win.cpp
|
||||
base/file_lock_posix.cpp
|
||||
base/flags.h
|
||||
base/flat_map.h
|
||||
base/flat_set.h
|
||||
base/functors.h
|
||||
base/global_shortcuts.h
|
||||
base/global_shortcuts_generic.cpp
|
||||
base/global_shortcuts_generic.h
|
||||
base/index_based_iterator.h
|
||||
base/integration.cpp
|
||||
base/integration.h
|
||||
base/invoke_queued.h
|
||||
base/last_used_cache.h
|
||||
base/last_user_input.cpp
|
||||
base/last_user_input.h
|
||||
base/match_method.h
|
||||
base/network_reachability.cpp
|
||||
base/network_reachability.h
|
||||
base/never_freed_pointer.h
|
||||
base/object_ptr.h
|
||||
base/ordered_set.h
|
||||
base/openssl_help.h
|
||||
base/optional.h
|
||||
base/options.cpp
|
||||
base/options.h
|
||||
base/overload.h
|
||||
base/parse_helper.cpp
|
||||
base/parse_helper.h
|
||||
base/power_save_blocker.cpp
|
||||
base/power_save_blocker.h
|
||||
base/qthelp_regex.h
|
||||
base/qthelp_url.cpp
|
||||
base/qthelp_url.h
|
||||
base/qt_connection.h
|
||||
base/qt_signal_producer.h
|
||||
base/random.cpp
|
||||
base/random.h
|
||||
base/required.h
|
||||
base/runtime_composer.cpp
|
||||
base/runtime_composer.h
|
||||
base/screen_reader_state.cpp
|
||||
base/screen_reader_state.h
|
||||
base/single_instance.cpp
|
||||
base/single_instance.h
|
||||
base/system_unlock.h
|
||||
base/thread_safe_wrap.h
|
||||
base/timer.cpp
|
||||
base/timer.h
|
||||
base/timer_rpl.h
|
||||
base/type_traits.h
|
||||
base/unique_any.h
|
||||
base/unique_function.h
|
||||
base/unique_qptr.h
|
||||
base/unixtime.cpp
|
||||
base/unixtime.h
|
||||
base/variant.h
|
||||
base/virtual_method.h
|
||||
base/weak_ptr.h
|
||||
base/weak_qptr.h
|
||||
base/zlib_help.h
|
||||
base/qt/qt_common_adapters.h
|
||||
base/qt/qt_compare.h
|
||||
base/qt/qt_key_modifiers.h
|
||||
base/qt/qt_string_view.h
|
||||
base/qt/qt_tab_key.cpp
|
||||
base/qt/qt_tab_key.h
|
||||
|
||||
base/base_pch.h
|
||||
)
|
||||
|
||||
if (DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
remove_target_sources(lib_base ${src_loc}
|
||||
base/platform/linux/base_linux_xcb_utilities.cpp
|
||||
base/platform/linux/base_linux_xcb_utilities.h
|
||||
base/platform/linux/base_linux_xsettings.cpp
|
||||
base/platform/linux/base_linux_xsettings.h
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(lib_base
|
||||
PUBLIC
|
||||
${src_loc}
|
||||
)
|
||||
|
||||
target_link_libraries(lib_base
|
||||
PUBLIC
|
||||
desktop-app::lib_rpl
|
||||
desktop-app::lib_crl
|
||||
desktop-app::external_qt
|
||||
desktop-app::external_openssl
|
||||
desktop-app::external_crash_reports
|
||||
desktop-app::external_ranges
|
||||
desktop-app::external_gsl
|
||||
desktop-app::external_expected
|
||||
PRIVATE
|
||||
desktop-app::external_xxhash
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
target_link_libraries(lib_base
|
||||
PUBLIC
|
||||
desktop-app::external_kcoreaddons
|
||||
)
|
||||
|
||||
include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
|
||||
generate_dbus(lib_base org.freedesktop. XdgDBus ${src_loc}/base/platform/linux/org.freedesktop.DBus.xml)
|
||||
generate_dbus(lib_base org.freedesktop.portal. XdpInhibit ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Inhibit.xml)
|
||||
generate_dbus(lib_base org.freedesktop.portal. XdpOpenURI ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.OpenURI.xml)
|
||||
generate_dbus(lib_base org.freedesktop.portal. XdpRequest ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Request.xml)
|
||||
generate_dbus(lib_base org.freedesktop.portal. XdpSettings ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Settings.xml)
|
||||
generate_dbus(lib_base org.freedesktop. XdgFileManager1 ${src_loc}/base/platform/linux/org.freedesktop.FileManager1.xml)
|
||||
generate_dbus(lib_base org.gnome.Mutter. MutterIdleMonitor ${src_loc}/base/platform/linux/org.gnome.Mutter.IdleMonitor.xml)
|
||||
generate_dbus(lib_base org.sigxcpu.Feedback. SigxcpuFeedback ${src_loc}/base/platform/linux/org.sigxcpu.Feedback.Haptic.xml)
|
||||
generate_dbus(lib_base org.mpris. Mpris ${src_loc}/base/platform/linux/mpris.xml)
|
||||
generate_dbus(lib_base io.snapcraft. Snapcraft ${src_loc}/base/platform/linux/io.snapcraft.Settings.xml)
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
target_link_libraries(lib_base
|
||||
PUBLIC
|
||||
desktop-app::external_xcb_keysyms
|
||||
desktop-app::external_xcb_record
|
||||
desktop-app::external_xcb_screensaver
|
||||
desktop-app::external_xcb
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_USE_ALLOCATION_TRACER)
|
||||
target_compile_definitions(lib_base
|
||||
PRIVATE
|
||||
DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
)
|
||||
endif()
|
||||
|
||||
#target_precompile_headers(lib_base_crash_report_writer REUSE_FROM lib_base)
|
||||
target_precompile_headers(lib_base_crash_report_writer PRIVATE ${src_loc}/base/base_pch.h)
|
||||
nice_target_sources(lib_base_crash_report_writer ${src_loc}
|
||||
PRIVATE
|
||||
base/crash_report_header.cpp
|
||||
base/crash_report_header.h
|
||||
base/crash_report_writer.cpp
|
||||
base/crash_report_writer.h
|
||||
)
|
||||
|
||||
if (DESKTOP_APP_DISABLE_CRASH_REPORTS)
|
||||
remove_target_sources(lib_base_crash_report_writer ${src_loc}
|
||||
base/crash_report_writer.cpp
|
||||
base/crash_report_writer.h
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(lib_base_crash_report_writer PUBLIC desktop-app::lib_base)
|
||||
44
Telegram/lib_base/base/algorithm.cpp
Normal file
44
Telegram/lib_base/base/algorithm.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#include <cfenv>
|
||||
|
||||
namespace base {
|
||||
|
||||
[[nodiscard]] double SafeRound(double value) {
|
||||
Expects(!std::isnan(value));
|
||||
|
||||
if (const auto result = std::round(value); !std::isnan(result)) {
|
||||
return result;
|
||||
}
|
||||
const auto errors = std::fetestexcept(FE_ALL_EXCEPT);
|
||||
if (const auto result = std::round(value); !std::isnan(result)) {
|
||||
return result;
|
||||
}
|
||||
LOG(("Streaming Error: Got second NAN in std::round(%1), fe: %2."
|
||||
).arg(value
|
||||
).arg(errors));
|
||||
std::feclearexcept(FE_ALL_EXCEPT);
|
||||
if (const auto result = std::round(value); !std::isnan(result)) {
|
||||
return result;
|
||||
}
|
||||
Unexpected("NAN after third std::round.");
|
||||
}
|
||||
|
||||
QString CleanAndSimplify(QString text) {
|
||||
for (auto &ch : text) {
|
||||
if (ch.unicode() < 32) {
|
||||
ch = QChar(' ');
|
||||
}
|
||||
}
|
||||
return text.simplified();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
153
Telegram/lib_base/base/algorithm.h
Normal file
153
Telegram/lib_base/base/algorithm.h
Normal file
@@ -0,0 +1,153 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QLatin1String>
|
||||
#include <QtCore/QString>
|
||||
#include <memory>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Type>
|
||||
inline constexpr Type take(Type &value) noexcept {
|
||||
return std::exchange(value, Type {});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline constexpr Type duplicate(const Type &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline constexpr size_t array_size(const Type(&)[Size]) noexcept {
|
||||
return Size;
|
||||
}
|
||||
|
||||
template <typename Container, typename T>
|
||||
inline bool contains(const Container &container, const T &value) {
|
||||
const auto end = std::end(container);
|
||||
return std::find(std::begin(container), end, value) != end;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline void reorder(Container &container, int oldPosition, int newPosition) {
|
||||
const auto b = container.begin();
|
||||
if (oldPosition < newPosition) {
|
||||
std::rotate(
|
||||
b + oldPosition,
|
||||
b + oldPosition + 1,
|
||||
b + newPosition + 1);
|
||||
} else if (newPosition < oldPosition) {
|
||||
std::rotate(
|
||||
b + newPosition,
|
||||
b + oldPosition,
|
||||
b + oldPosition + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int reorder_index(
|
||||
int index,
|
||||
int oldPosition,
|
||||
int newPosition) {
|
||||
return (index == oldPosition)
|
||||
? newPosition
|
||||
: (oldPosition < index && index <= newPosition)
|
||||
? (index - 1)
|
||||
: (oldPosition > index && index >= newPosition)
|
||||
? (index + 1)
|
||||
: index;
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
inline constexpr D up_cast(T object) {
|
||||
using DV = std::decay_t<decltype(*D())>;
|
||||
using TV = std::decay_t<decltype(*T())>;
|
||||
if constexpr (std::is_base_of_v<DV, TV>) {
|
||||
return object;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// We need a custom comparator for set<std::unique_ptr<T>>::find to work with pointers.
|
||||
// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs
|
||||
template <typename T>
|
||||
struct pointer_comparator {
|
||||
using is_transparent = std::true_type;
|
||||
|
||||
// helper does some magic in order to reduce the number of
|
||||
// pairs of types we need to know how to compare: it turns
|
||||
// everything into a pointer, and then uses `std::less<T*>`
|
||||
// to do the comparison:
|
||||
struct helper {
|
||||
const T *ptr = nullptr;
|
||||
helper() = default;
|
||||
helper(const helper &other) = default;
|
||||
helper(const T *p) : ptr(p) {
|
||||
}
|
||||
template <typename ...Ts>
|
||||
helper(const std::shared_ptr<Ts...> &other) : ptr(other.get()) {
|
||||
}
|
||||
template <typename ...Ts>
|
||||
helper(const std::unique_ptr<Ts...> &other) : ptr(other.get()) {
|
||||
}
|
||||
bool operator<(helper other) const {
|
||||
return std::less<const T*>()(ptr, other.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// without helper, we'd need 2^n different overloads, where
|
||||
// n is the number of types we want to support (so, 8 with
|
||||
// raw pointers, unique pointers, and shared pointers). That
|
||||
// seems silly.
|
||||
// && helps enforce rvalue use only
|
||||
bool operator()(const helper &&lhs, const helper &&rhs) const {
|
||||
return lhs < rhs;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline QString FromUtf8Safe(const char *string, int size = -1) {
|
||||
if (!string || !size) {
|
||||
return QString();
|
||||
} else if (size < 0) {
|
||||
size = strlen(string);
|
||||
}
|
||||
const auto result = QString::fromUtf8(string, size);
|
||||
const auto back = result.toUtf8();
|
||||
return (back.size() != size || memcmp(back.constData(), string, size))
|
||||
? QString::fromLocal8Bit(string, size)
|
||||
: result;
|
||||
}
|
||||
|
||||
inline QString FromUtf8Safe(const QByteArray &string) {
|
||||
return FromUtf8Safe(string.constData(), string.size());
|
||||
}
|
||||
|
||||
[[nodiscard]] double SafeRound(double value);
|
||||
|
||||
[[nodiscard]] QString CleanAndSimplify(QString text);
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] bool OppositeSigns(T a, T b) {
|
||||
// For zero returns false!
|
||||
return (a < 0 && b > 0) || (a > 0 && b < 0);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename T>
|
||||
inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; }
|
||||
|
||||
template <typename T>
|
||||
inline void accumulate_min(T &a, const T &b) { if (a > b) a = b; }
|
||||
|
||||
template <size_t Size>
|
||||
QLatin1String qstr(const char(&string)[Size]) {
|
||||
return QLatin1String(string, int(Size) - 1);
|
||||
}
|
||||
49
Telegram/lib_base/base/algorithm_tests.cpp
Normal file
49
Telegram/lib_base/base/algorithm_tests.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "base/index_based_iterator.h"
|
||||
|
||||
TEST_CASE("index_based_iterator tests", "[base::algorithm]") {
|
||||
auto v = std::vector<int>();
|
||||
|
||||
v.insert(v.end(), { 1, 2, 3, 4, 5, 4, 3, 2, 1 });
|
||||
auto push_back_safe_remove_if = [](auto &v, auto predicate) {
|
||||
auto begin = base::index_based_begin(v);
|
||||
auto end = base::index_based_end(v);
|
||||
auto from = std::remove_if(begin, end, predicate);
|
||||
if (from != end) {
|
||||
auto newEnd = base::index_based_end(v);
|
||||
if (newEnd != end) {
|
||||
REQUIRE(newEnd > end);
|
||||
while (end != newEnd) {
|
||||
*from++ = *end++;
|
||||
}
|
||||
}
|
||||
v.erase(from.base(), newEnd.base());
|
||||
}
|
||||
};
|
||||
SECTION("allows to push_back from predicate") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
v.push_back(value);
|
||||
return (value % 2) == 1;
|
||||
});
|
||||
auto expected = std::vector<int> { 2, 4, 4, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
|
||||
SECTION("allows to push_back while removing all") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
if (value == 5) {
|
||||
v.push_back(value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
auto expected = std::vector<int> { 5 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
}
|
||||
24
Telegram/lib_base/base/assertion.cpp
Normal file
24
Telegram/lib_base/base/assertion.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/assertion.h"
|
||||
|
||||
#include "base/integration.h"
|
||||
|
||||
namespace base::assertion {
|
||||
|
||||
void log(const char *message, const char *file, int line) {
|
||||
if (Integration::Exists()) {
|
||||
const auto info = message
|
||||
+ QString(' ')
|
||||
+ file
|
||||
+ ':'
|
||||
+ QString::number(line);
|
||||
Integration::Instance().logAssertionViolation(info);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base::assertion
|
||||
99
Telegram/lib_base/base/assertion.h
Normal file
99
Telegram/lib_base/base/assertion.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// Ensures/Expects.
|
||||
#include <gsl/assert>
|
||||
|
||||
namespace base {
|
||||
namespace assertion {
|
||||
|
||||
void log(const char *message, const char *file, int line);
|
||||
|
||||
// Release build assertions.
|
||||
inline constexpr void noop() {
|
||||
}
|
||||
|
||||
[[noreturn]] inline void fail(
|
||||
const char *message,
|
||||
const char *file,
|
||||
int line) {
|
||||
log(message, file, line);
|
||||
|
||||
// Crash with access violation and generate crash report.
|
||||
volatile auto nullptr_value = (int*)nullptr;
|
||||
*nullptr_value = 0;
|
||||
|
||||
// Silent the possible failure to comply noreturn warning.
|
||||
std::abort();
|
||||
}
|
||||
|
||||
constexpr const char* extract_basename(const char* path, size_t size) {
|
||||
while (size != 0 && path[size - 1] != '/' && path[size - 1] != '\\') {
|
||||
--size;
|
||||
}
|
||||
return path + size;
|
||||
}
|
||||
|
||||
} // namespace assertion
|
||||
} // namespace base
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define AssertUnlikelyHelper(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define AssertUnlikelyHelper(x) (!!(x))
|
||||
#endif
|
||||
|
||||
#define AssertValidationCondition(condition, message, file, line)\
|
||||
((AssertUnlikelyHelper(!(condition)))\
|
||||
? ::base::assertion::fail(message, file, line)\
|
||||
: ::base::assertion::noop())
|
||||
|
||||
#define SOURCE_FILE_BASENAME (::base::assertion::extract_basename(\
|
||||
__FILE__,\
|
||||
sizeof(__FILE__)))
|
||||
|
||||
#define AssertCustom(condition, message) (AssertValidationCondition(\
|
||||
condition,\
|
||||
message,\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
#define Assert(condition) AssertCustom(condition, "\"" #condition "\"")
|
||||
|
||||
// Define our own versions of Expects() and Ensures().
|
||||
// Let them crash with reports and logging.
|
||||
#ifdef Expects
|
||||
#undef Expects
|
||||
#endif // Expects
|
||||
#define Expects(condition) (AssertValidationCondition(\
|
||||
condition,\
|
||||
"\"" #condition "\"",\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef Ensures
|
||||
#undef Ensures
|
||||
#endif // Ensures
|
||||
#define Ensures(condition) (AssertValidationCondition(\
|
||||
condition,\
|
||||
"\"" #condition "\"",\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef Unexpected
|
||||
#undef Unexpected
|
||||
#endif // Unexpected
|
||||
#define Unexpected(message) (::base::assertion::fail(\
|
||||
"Unexpected: " message,\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define AssertIsDebug(...)
|
||||
#endif // _DEBUG
|
||||
65
Telegram/lib_base/base/atomic.h
Normal file
65
Telegram/lib_base/base/atomic.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Helper for macOS < 11.0 and GCC < 11 that don't have wait/notify.
|
||||
|
||||
#if defined __cpp_lib_atomic_wait && !defined Q_OS_MAC
|
||||
|
||||
using std::atomic;
|
||||
using std::atomic_notify_all;
|
||||
|
||||
#else // __cpp_lib_atomic_wait
|
||||
|
||||
template <typename Type>
|
||||
class atomic final {
|
||||
public:
|
||||
atomic(Type value) : _value(value) {
|
||||
}
|
||||
atomic &operator=(Type value) {
|
||||
_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] Type load() const {
|
||||
return _value.load();
|
||||
}
|
||||
|
||||
void wait(Type whileEquals) {
|
||||
if (_value == whileEquals) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
while (_value == whileEquals) {
|
||||
_condition.wait(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
friend inline bool operator==(const atomic &a, Type b) {
|
||||
return (a._value == b);
|
||||
}
|
||||
|
||||
friend void atomic_notify_all(atomic *value) {
|
||||
std::unique_lock<std::mutex> lock(value->_mutex);
|
||||
value->_condition.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Type> _value;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _condition;
|
||||
|
||||
};
|
||||
|
||||
#endif // __cpp_lib_atomic_wait
|
||||
|
||||
} // namespace base
|
||||
47
Telegram/lib_base/base/base_file_utilities.cpp
Normal file
47
Telegram/lib_base/base/base_file_utilities.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/base_file_utilities.h"
|
||||
|
||||
#include "base/platform/base_platform_file_utilities.h"
|
||||
|
||||
#include <QtCore/QResource>
|
||||
|
||||
namespace base {
|
||||
|
||||
QString FileNameFromUserString(QString name) {
|
||||
// We don't want LTR/RTL mark/embedding/override/isolate chars
|
||||
// in filenames, because they introduce a security issue, when
|
||||
// an executable "Fil[x]gepj.exe" may look like "Filexe.jpeg".
|
||||
const ushort kBad[] = {
|
||||
0x200E, // LTR Mark
|
||||
0x200F, // RTL Mark
|
||||
0x202A, // LTR Embedding
|
||||
0x202B, // RTL Embedding
|
||||
0x202D, // LTR Override
|
||||
0x202E, // RTL Override
|
||||
0x2066, // LTR Isolate
|
||||
0x2067, // RTL Isolate
|
||||
'/', '\\', '<', '>', ':', '"', '|', '?', '*' };
|
||||
for (auto &ch : name) {
|
||||
if (ch.unicode() < 32 || ranges::find(kBad, ch.unicode()) != end(kBad)) {
|
||||
ch = '_';
|
||||
}
|
||||
}
|
||||
if (name.isEmpty() || name.endsWith(' ') || name.endsWith('.')) {
|
||||
name.append('_');
|
||||
}
|
||||
return Platform::FileNameFromUserString(std::move(name));
|
||||
}
|
||||
|
||||
void RegisterBundledResources(const QString &name) {
|
||||
const auto location = Platform::BundledResourcesPath();
|
||||
if (!QResource::registerResource(location + '/' + name)) {
|
||||
Unexpected("Packed resources not found.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
15
Telegram/lib_base/base/base_file_utilities.h
Normal file
15
Telegram/lib_base/base/base_file_utilities.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
[[nodiscard]] QString FileNameFromUserString(QString name);
|
||||
|
||||
void RegisterBundledResources(const QString &name);
|
||||
|
||||
} // namespace base
|
||||
30
Telegram/lib_base/base/base_pch.h
Normal file
30
Telegram/lib_base/base/base_pch.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <crl/crl.h>
|
||||
#include <rpl/rpl.h>
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
#include <range/v3/all.hpp>
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/algorithm.h"
|
||||
#include "base/basic_types.h"
|
||||
#include "base/weak_qptr.h"
|
||||
176
Telegram/lib_base/base/basic_types.h
Normal file
176
Telegram/lib_base/base/basic_types.h
Normal file
@@ -0,0 +1,176 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/build_config.h"
|
||||
#include "base/ordered_set.h"
|
||||
#include "base/unique_function.h"
|
||||
#include "base/functors.h"
|
||||
#include "base/required.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <gsl/gsl>
|
||||
|
||||
namespace func = base::functors;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
using native_event_filter_result = qintptr;
|
||||
#else // Qt >= 6.0.0
|
||||
using native_event_filter_result = long;
|
||||
#endif // Qt >= 6.0.0
|
||||
|
||||
using gsl::not_null;
|
||||
using index_type = gsl::index;
|
||||
using size_type = gsl::index;
|
||||
using base::required;
|
||||
|
||||
template <typename Signature>
|
||||
using Fn = std::function<Signature>;
|
||||
|
||||
template <typename Signature>
|
||||
using FnMut = base::unique_function<Signature>;
|
||||
|
||||
//using uchar = unsigned char; // Qt has uchar
|
||||
using int8 = qint8;
|
||||
using uint8 = quint8;
|
||||
using int16 = qint16;
|
||||
using uint16 = quint16;
|
||||
using int32 = qint32;
|
||||
using uint32 = quint32;
|
||||
using int64 = qint64;
|
||||
using uint64 = quint64;
|
||||
using float32 = float;
|
||||
using float64 = double;
|
||||
|
||||
using TimeId = int32;
|
||||
|
||||
#ifndef _DEBUG
|
||||
|
||||
#if defined _MSC_VER && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#define DESKTOP_APP_USE_NO_ALLOCATION_LITERAL
|
||||
#endif // _MSC_VER
|
||||
|
||||
#endif // !_DEBUG
|
||||
|
||||
#ifdef DESKTOP_APP_USE_NO_ALLOCATION_LITERAL
|
||||
|
||||
namespace base::details {
|
||||
|
||||
template <size_t N>
|
||||
struct LiteralResolver {
|
||||
template <size_t ...I>
|
||||
constexpr LiteralResolver(
|
||||
const char16_t (&text)[N],
|
||||
std::index_sequence<I...>)
|
||||
: utf16text{ text[I]... } {
|
||||
}
|
||||
template <size_t ...I>
|
||||
constexpr LiteralResolver(
|
||||
const char (&text)[N],
|
||||
std::index_sequence<I...>)
|
||||
: utf8text{ text[I]... }
|
||||
, utf8(true) {
|
||||
}
|
||||
constexpr LiteralResolver(const char16_t (&text)[N])
|
||||
: LiteralResolver(text, std::make_index_sequence<N>{}) {
|
||||
}
|
||||
constexpr LiteralResolver(const char (&text)[N])
|
||||
: LiteralResolver(text, std::make_index_sequence<N>{}) {
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const LiteralResolver &) const = default;
|
||||
|
||||
const char16_t utf16text[N]{};
|
||||
const char utf8text[N]{};
|
||||
size_t length = N;
|
||||
bool utf8 = false;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct StaticStringData {
|
||||
template <std::size_t... I>
|
||||
constexpr StaticStringData(
|
||||
const char16_t (&text)[N],
|
||||
std::index_sequence<I...>)
|
||||
: data Q_STATIC_STRING_DATA_HEADER_INITIALIZER(N - 1)
|
||||
, text{ text[I]... } {
|
||||
}
|
||||
QArrayData data;
|
||||
char16_t text[N];
|
||||
|
||||
QStringData *pointer() {
|
||||
Q_ASSERT(data.ref.isStatic());
|
||||
return static_cast<QStringData*>(&data);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct StaticByteArrayData {
|
||||
template <std::size_t... I>
|
||||
constexpr StaticByteArrayData(
|
||||
const char (&text)[N],
|
||||
std::index_sequence<I...>)
|
||||
: data Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER(N - 1)
|
||||
, text{ text[I]... } {
|
||||
}
|
||||
QByteArrayData data;
|
||||
char text[N];
|
||||
|
||||
QByteArrayData *pointer() {
|
||||
Q_ASSERT(data.ref.isStatic());
|
||||
return &data;
|
||||
}
|
||||
};
|
||||
|
||||
template <LiteralResolver Resolve>
|
||||
using q_literal_type = std::conditional_t<Resolve.utf8, QByteArray, QString>;
|
||||
|
||||
} // namespace base::details
|
||||
|
||||
template <base::details::LiteralResolver Resolve>
|
||||
base::details::q_literal_type<Resolve> operator""_q() {
|
||||
static_assert(Resolve.length > 0);
|
||||
|
||||
using namespace base::details;
|
||||
if constexpr (Resolve.utf8) {
|
||||
static auto Literal = StaticByteArrayData<Resolve.length>(
|
||||
Resolve.utf8text,
|
||||
std::make_index_sequence<Resolve.length>{});
|
||||
return QByteArray{ QByteArrayDataPtr{ Literal.pointer() } };
|
||||
} else {
|
||||
static auto Literal = StaticStringData<Resolve.length>(
|
||||
Resolve.utf16text,
|
||||
std::make_index_sequence<Resolve.length>{});
|
||||
return QString{ QStringDataPtr{ Literal.pointer() } };
|
||||
}
|
||||
};
|
||||
|
||||
#else // DESKTOP_APP_USE_NO_ALLOCATION_LITERAL
|
||||
|
||||
[[nodiscard]] inline QByteArray operator""_q(
|
||||
const char *data,
|
||||
std::size_t size) {
|
||||
return QByteArray::fromRawData(data, size);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline QString operator""_q(
|
||||
const char16_t *data,
|
||||
std::size_t size) {
|
||||
return QString::fromRawData(
|
||||
reinterpret_cast<const QChar*>(data),
|
||||
size);
|
||||
}
|
||||
|
||||
#endif // DESKTOP_APP_USE_NO_ALLOCATION_LITERAL
|
||||
30
Telegram/lib_base/base/battery_saving.cpp
Normal file
30
Telegram/lib_base/base/battery_saving.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/battery_saving.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
BatterySaving::BatterySaving()
|
||||
: _helper(Platform::CreateBatterySaving([=] {
|
||||
_value = _helper->enabled();
|
||||
}))
|
||||
, _value(_helper->enabled()) {
|
||||
}
|
||||
|
||||
BatterySaving::~BatterySaving() = default;
|
||||
|
||||
std::optional<bool> BatterySaving::enabled() const {
|
||||
return _value.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> BatterySaving::value() const {
|
||||
return _value.value() | rpl::map([=](std::optional<bool> maybe) {
|
||||
return maybe.value_or(false);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
43
Telegram/lib_base/base/battery_saving.h
Normal file
43
Telegram/lib_base/base/battery_saving.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
class AbstractBatterySaving {
|
||||
public:
|
||||
virtual ~AbstractBatterySaving() = default;
|
||||
|
||||
virtual std::optional<bool> enabled() const = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<AbstractBatterySaving> CreateBatterySaving(
|
||||
Fn<void()> changeCallback);
|
||||
|
||||
} // namespace base::Platform
|
||||
|
||||
namespace base {
|
||||
|
||||
class BatterySaving final {
|
||||
public:
|
||||
BatterySaving();
|
||||
~BatterySaving();
|
||||
|
||||
[[nodiscard]] bool supported() const {
|
||||
return enabled().has_value();
|
||||
}
|
||||
[[nodiscard]] std::optional<bool> enabled() const;
|
||||
[[nodiscard]] rpl::producer<bool> value() const;
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Platform::AbstractBatterySaving> _helper;
|
||||
|
||||
rpl::variable<std::optional<bool>> _value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
100
Telegram/lib_base/base/binary_guard.h
Normal file
100
Telegram/lib_base/base/binary_guard.h
Normal file
@@ -0,0 +1,100 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace base {
|
||||
|
||||
class binary_guard {
|
||||
public:
|
||||
binary_guard() = default;
|
||||
binary_guard(binary_guard &&other);
|
||||
binary_guard &operator=(binary_guard &&other);
|
||||
~binary_guard();
|
||||
|
||||
binary_guard &operator=(std::nullptr_t);
|
||||
|
||||
bool alive() const;
|
||||
binary_guard make_guard();
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
private:
|
||||
void destroy();
|
||||
|
||||
std::atomic<bool> *_bothAlive = nullptr;
|
||||
|
||||
};
|
||||
|
||||
inline binary_guard::binary_guard(binary_guard &&other)
|
||||
: _bothAlive(base::take(other._bothAlive)) {
|
||||
}
|
||||
|
||||
inline binary_guard &binary_guard::operator=(binary_guard &&other) {
|
||||
if (this != &other) {
|
||||
destroy();
|
||||
_bothAlive = base::take(other._bothAlive);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline binary_guard::~binary_guard() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
|
||||
destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline binary_guard::operator bool() const {
|
||||
return alive();
|
||||
}
|
||||
|
||||
inline bool binary_guard::alive() const {
|
||||
return _bothAlive && _bothAlive->load();
|
||||
}
|
||||
|
||||
inline void binary_guard::destroy() {
|
||||
if (const auto both = base::take(_bothAlive)) {
|
||||
auto old = true;
|
||||
if (!both->compare_exchange_strong(old, false)) {
|
||||
delete both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline binary_guard binary_guard::make_guard() {
|
||||
destroy();
|
||||
|
||||
auto result = binary_guard();
|
||||
_bothAlive = result._bothAlive = new std::atomic<bool>(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <>
|
||||
struct guard_traits<base::binary_guard, void> {
|
||||
static base::binary_guard create(base::binary_guard value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const base::binary_guard &guard) {
|
||||
return guard.alive();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
46
Telegram/lib_base/base/build_config.h
Normal file
46
Telegram/lib_base/base/build_config.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// thanks Chromium
|
||||
|
||||
// Compiler detection.
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG 1
|
||||
#elif defined(__GNUC__) // __clang__
|
||||
#define COMPILER_GCC 1
|
||||
#elif defined(_MSC_VER) // __clang__ || __GNUC__
|
||||
#define COMPILER_MSVC 1
|
||||
#endif // _MSC_VER || __clang__ || __GNUC__
|
||||
|
||||
// Processor architecture detection.
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
#define ARCH_CPU_X86_FAMILY 1
|
||||
#define ARCH_CPU_X86_64 1
|
||||
#elif defined(_M_IX86) || defined(__i386__)
|
||||
#define ARCH_CPU_X86_FAMILY 1
|
||||
#define ARCH_CPU_X86 1
|
||||
#endif
|
||||
// _LP64 is defined by GCC, others by MSVC
|
||||
#if defined _LP64 || defined _M_X64 || defined _M_ARM64 || defined _M_ALPHA
|
||||
#define ARCH_CPU_64_BITS 1
|
||||
#else
|
||||
#define ARCH_CPU_32_BITS 1
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define TG_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define TG_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define TG_FORCE_INLINE inline
|
||||
#endif
|
||||
|
||||
#include <climits>
|
||||
static_assert(CHAR_BIT == 8, "Not supported char size.");
|
||||
19
Telegram/lib_base/base/bytes.cpp
Normal file
19
Telegram/lib_base/base/bytes.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/bytes.h"
|
||||
|
||||
#include "base/random.h"
|
||||
|
||||
namespace bytes {
|
||||
|
||||
void set_random(span destination) {
|
||||
if (!destination.empty()) {
|
||||
base::RandomFill(destination.data(), destination.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bytes
|
||||
170
Telegram/lib_base/base/bytes.h
Normal file
170
Telegram/lib_base/base/bytes.h
Normal file
@@ -0,0 +1,170 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
#include <gsl/gsl>
|
||||
#include <gsl/byte>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace bytes {
|
||||
|
||||
using type = gsl::byte;
|
||||
using span = gsl::span<type>;
|
||||
using const_span = gsl::span<const type>;
|
||||
using vector = std::vector<type>;
|
||||
|
||||
template <gsl::index Size>
|
||||
using array = std::array<type, Size>;
|
||||
|
||||
inline span make_detached_span(QByteArray &container) {
|
||||
return gsl::as_writable_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Container,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_const_v<Container>
|
||||
&& !std::is_same_v<Container, QByteArray>>>
|
||||
inline span make_span(Container &container) {
|
||||
return gsl::as_writable_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline span make_span<span, void>(span &container) {
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline const_span make_span(const Container &container) {
|
||||
return gsl::as_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
inline const_span make_span(const_span &container) {
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline span make_span(gsl::span<Type, Extent> container) {
|
||||
return gsl::as_writable_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline const_span make_span(gsl::span<const Type, Extent> container) {
|
||||
return gsl::as_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline span make_span(Type *value, std::size_t count) {
|
||||
return gsl::as_writable_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline const_span make_span(const Type *value, std::size_t count) {
|
||||
return gsl::as_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline span object_as_span(Type *value) {
|
||||
return bytes::make_span(value, 1);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline const_span object_as_span(const Type *value) {
|
||||
return bytes::make_span(value, 1);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline vector make_vector(const Container &container) {
|
||||
const auto buffer = bytes::make_span(container);
|
||||
return { buffer.begin(), buffer.end() };
|
||||
}
|
||||
|
||||
inline void copy(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memcpy(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void move(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memmove(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void set_with_const(span destination, type value) {
|
||||
memset(
|
||||
destination.data(),
|
||||
gsl::to_integer<unsigned char>(value),
|
||||
destination.size());
|
||||
}
|
||||
|
||||
inline int compare(const_span a, const_span b) {
|
||||
const auto aSize = a.size(), bSize = b.size();
|
||||
return (aSize > bSize)
|
||||
? 1
|
||||
: (aSize < bSize)
|
||||
? -1
|
||||
: memcmp(a.data(), b.data(), aSize);
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Arg>
|
||||
std::size_t spansLength(Arg &&arg) {
|
||||
return bytes::make_span(arg).size();
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
std::size_t spansLength(Arg &&arg, Args &&...args) {
|
||||
return bytes::make_span(arg).size() + spansLength(args...);
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
void spansAppend(span destination, Arg &&arg) {
|
||||
bytes::copy(destination, bytes::make_span(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
void spansAppend(span destination, Arg &&arg, Args &&...args) {
|
||||
const auto data = bytes::make_span(arg);
|
||||
bytes::copy(destination, data);
|
||||
spansAppend(destination.subspan(data.size()), args...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
vector concatenate(Args &&...args) {
|
||||
const auto size = details::spansLength(args...);
|
||||
auto result = vector(size);
|
||||
details::spansAppend(make_span(result), args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename SpanRange>
|
||||
vector concatenate(SpanRange args) {
|
||||
auto size = std::size_t(0);
|
||||
for (const auto &arg : args) {
|
||||
size += bytes::make_span(arg).size();
|
||||
}
|
||||
auto result = vector(size);
|
||||
auto buffer = make_span(result);
|
||||
for (const auto &arg : args) {
|
||||
const auto part = bytes::make_span(arg);
|
||||
bytes::copy(buffer, part);
|
||||
buffer = buffer.subspan(part.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_random(span destination);
|
||||
|
||||
} // namespace bytes
|
||||
49
Telegram/lib_base/base/call_delayed.cpp
Normal file
49
Telegram/lib_base/base/call_delayed.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/call_delayed.h"
|
||||
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
DelayedCallTimer *GlobalTimer = nullptr;
|
||||
bool Finished = false;
|
||||
|
||||
void CreateGlobalTimer() {
|
||||
Expects(QCoreApplication::instance() != nullptr);
|
||||
Expects(!GlobalTimer);
|
||||
|
||||
const auto instance = QCoreApplication::instance();
|
||||
Assert(instance != nullptr);
|
||||
|
||||
GlobalTimer = new DelayedCallTimer();
|
||||
instance->connect(instance, &QCoreApplication::aboutToQuit, [] {
|
||||
Finished = true;
|
||||
});
|
||||
instance->connect(instance, &QCoreApplication::destroyed, [] {
|
||||
Finished = true;
|
||||
delete GlobalTimer;
|
||||
GlobalTimer = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void call_delayed(crl::time delay, FnMut<void()> &&callable) {
|
||||
if (Finished) {
|
||||
return;
|
||||
}
|
||||
if (!GlobalTimer) {
|
||||
CreateGlobalTimer();
|
||||
}
|
||||
GlobalTimer->call(delay, std::move(callable));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
42
Telegram/lib_base/base/call_delayed.h
Normal file
42
Telegram/lib_base/base/call_delayed.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
void call_delayed(crl::time delay, FnMut<void()> &&callable);
|
||||
|
||||
template <
|
||||
typename Guard,
|
||||
typename Callable,
|
||||
typename GuardTraits = crl::guard_traits<std::decay_t<Guard>>,
|
||||
typename = std::enable_if_t<
|
||||
sizeof(GuardTraits) != crl::details::dependent_zero<GuardTraits>>>
|
||||
inline void call_delayed(
|
||||
crl::time delay,
|
||||
Guard &&object,
|
||||
Callable &&callable) {
|
||||
return call_delayed(delay, crl::guard(
|
||||
std::forward<Guard>(object),
|
||||
std::forward<Callable>(callable)));
|
||||
}
|
||||
|
||||
template <typename Guard, typename Callable>
|
||||
[[nodiscard]] inline auto fn_delayed(
|
||||
crl::time delay,
|
||||
Guard &&object,
|
||||
Callable &&callable) {
|
||||
auto guarded = crl::guard(
|
||||
std::forward<Guard>(object),
|
||||
std::forward<Callable>(callable));
|
||||
return [saved = std::move(guarded), delay] {
|
||||
auto copy = saved;
|
||||
base::call_delayed(delay, std::move(copy));
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
369
Telegram/lib_base/base/concurrent_timer.cpp
Normal file
369
Telegram/lib_base/base/concurrent_timer.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/concurrent_timer.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
using namespace base::details;
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
namespace {
|
||||
|
||||
auto CallDelayedEventType() {
|
||||
static const auto Result = QEvent::Type(QEvent::registerEventType());
|
||||
return Result;
|
||||
}
|
||||
|
||||
auto CancelTimerEventType() {
|
||||
static const auto Result = QEvent::Type(QEvent::registerEventType());
|
||||
return Result;
|
||||
}
|
||||
|
||||
ConcurrentTimerEnvironment *Environment/* = nullptr*/;
|
||||
QMutex EnvironmentMutex;
|
||||
|
||||
class CallDelayedEvent : public QEvent {
|
||||
public:
|
||||
CallDelayedEvent(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
|
||||
crl::time timeout() const;
|
||||
Qt::TimerType type() const;
|
||||
FnMut<void()> takeMethod();
|
||||
|
||||
private:
|
||||
crl::time _timeout = 0;
|
||||
Qt::TimerType _type = Qt::PreciseTimer;
|
||||
FnMut<void()> _method;
|
||||
|
||||
};
|
||||
|
||||
class CancelTimerEvent : public QEvent {
|
||||
public:
|
||||
CancelTimerEvent();
|
||||
|
||||
};
|
||||
|
||||
CallDelayedEvent::CallDelayedEvent(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method)
|
||||
: QEvent(CallDelayedEventType())
|
||||
, _timeout(timeout)
|
||||
, _type(type)
|
||||
, _method(std::move(method)) {
|
||||
Expects(_timeout >= 0 && _timeout < std::numeric_limits<int>::max());
|
||||
}
|
||||
|
||||
crl::time CallDelayedEvent::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
Qt::TimerType CallDelayedEvent::type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
FnMut<void()> CallDelayedEvent::takeMethod() {
|
||||
return base::take(_method);
|
||||
}
|
||||
|
||||
CancelTimerEvent::CancelTimerEvent() : QEvent(CancelTimerEventType()) {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TimerObject : public QObject {
|
||||
public:
|
||||
TimerObject(
|
||||
not_null<QThread*> thread,
|
||||
not_null<QObject*> adjuster,
|
||||
Fn<void()> adjust);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void callDelayed(not_null<CallDelayedEvent*> e);
|
||||
void callNow();
|
||||
void cancel();
|
||||
void adjust();
|
||||
|
||||
FnMut<void()> _next;
|
||||
Fn<void()> _adjust;
|
||||
int _timerId = 0;
|
||||
|
||||
};
|
||||
|
||||
TimerObject::TimerObject(
|
||||
not_null<QThread*> thread,
|
||||
not_null<QObject*> adjuster,
|
||||
Fn<void()> adjust)
|
||||
: _adjust(std::move(adjust)) {
|
||||
moveToThread(thread);
|
||||
connect(
|
||||
adjuster,
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
&TimerObject::adjust,
|
||||
Qt::DirectConnection);
|
||||
}
|
||||
|
||||
bool TimerObject::event(QEvent *e) {
|
||||
const auto type = e->type();
|
||||
if (type == CallDelayedEventType()) {
|
||||
callDelayed(static_cast<CallDelayedEvent*>(e));
|
||||
return true;
|
||||
} else if (type == CancelTimerEventType()) {
|
||||
cancel();
|
||||
return true;
|
||||
} else if (type == QEvent::Timer) {
|
||||
callNow();
|
||||
return true;
|
||||
}
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
void TimerObject::callDelayed(not_null<CallDelayedEvent*> e) {
|
||||
cancel();
|
||||
|
||||
const auto timeout = e->timeout();
|
||||
const auto type = e->type();
|
||||
_next = e->takeMethod();
|
||||
if (timeout > 0) {
|
||||
_timerId = startTimer(timeout, type);
|
||||
} else {
|
||||
base::take(_next)();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerObject::cancel() {
|
||||
if (const auto id = base::take(_timerId)) {
|
||||
killTimer(id);
|
||||
}
|
||||
_next = nullptr;
|
||||
}
|
||||
|
||||
void TimerObject::callNow() {
|
||||
auto next = base::take(_next);
|
||||
cancel();
|
||||
next();
|
||||
}
|
||||
|
||||
void TimerObject::adjust() {
|
||||
if (_adjust) {
|
||||
_adjust();
|
||||
}
|
||||
}
|
||||
|
||||
TimerObjectWrap::TimerObjectWrap(Fn<void()> adjust) {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
|
||||
if (Environment) {
|
||||
_value = Environment->createTimer(std::move(adjust));
|
||||
}
|
||||
}
|
||||
|
||||
TimerObjectWrap::~TimerObjectWrap() {
|
||||
if (_value) {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
|
||||
if (Environment) {
|
||||
_value.release()->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimerObjectWrap::call(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method) {
|
||||
sendEvent(std::make_unique<CallDelayedEvent>(
|
||||
timeout,
|
||||
type,
|
||||
std::move(method)));
|
||||
}
|
||||
|
||||
void TimerObjectWrap::cancel() {
|
||||
sendEvent(std::make_unique<CancelTimerEvent>());
|
||||
}
|
||||
|
||||
void TimerObjectWrap::sendEvent(std::unique_ptr<QEvent> event) {
|
||||
if (!_value) {
|
||||
return;
|
||||
}
|
||||
QCoreApplication::postEvent(
|
||||
_value.get(),
|
||||
event.release(),
|
||||
Qt::HighEventPriority);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
ConcurrentTimerEnvironment::ConcurrentTimerEnvironment() {
|
||||
_thread.setObjectName("Concurrent Timer Thread");
|
||||
|
||||
_thread.start();
|
||||
_adjuster.moveToThread(&_thread);
|
||||
|
||||
acquire();
|
||||
}
|
||||
|
||||
ConcurrentTimerEnvironment::~ConcurrentTimerEnvironment() {
|
||||
_thread.quit();
|
||||
release();
|
||||
_thread.wait();
|
||||
QObject::disconnect(&_adjuster, &QObject::destroyed, nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<TimerObject> ConcurrentTimerEnvironment::createTimer(
|
||||
Fn<void()> adjust) {
|
||||
return std::make_unique<TimerObject>(
|
||||
&_thread,
|
||||
&_adjuster,
|
||||
std::move(adjust));
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::Adjust() {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
if (Environment) {
|
||||
Environment->adjustTimers();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::adjustTimers() {
|
||||
QObject emitter;
|
||||
QObject::connect(
|
||||
&emitter,
|
||||
&QObject::destroyed,
|
||||
&_adjuster,
|
||||
&QObject::destroyed,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::acquire() {
|
||||
Expects(Environment == nullptr);
|
||||
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
Environment = this;
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::release() {
|
||||
Expects(Environment == this);
|
||||
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
Environment = nullptr;
|
||||
}
|
||||
|
||||
ConcurrentTimer::ConcurrentTimer(
|
||||
Fn<void(FnMut<void()>)> runner,
|
||||
Fn<void()> callback)
|
||||
: _runner(std::move(runner))
|
||||
, _object(createAdjuster())
|
||||
, _callback(std::move(callback))
|
||||
, _type(Qt::PreciseTimer) {
|
||||
setRepeat(Repeat::Interval);
|
||||
}
|
||||
|
||||
Fn<void()> ConcurrentTimer::createAdjuster() {
|
||||
_guard = std::make_shared<bool>(true);
|
||||
return [=, runner = _runner, guard = std::weak_ptr<bool>(_guard)] {
|
||||
runner([=] {
|
||||
if (!guard.lock()) {
|
||||
return;
|
||||
}
|
||||
adjust();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void ConcurrentTimer::start(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
Repeat repeat) {
|
||||
_type = type;
|
||||
setRepeat(repeat);
|
||||
_adjusted = false;
|
||||
setTimeout(timeout);
|
||||
|
||||
cancelAndSchedule(_timeout);
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
|
||||
void ConcurrentTimer::cancelAndSchedule(int timeout) {
|
||||
auto method = [
|
||||
=,
|
||||
runner = _runner,
|
||||
guard = _running.make_guard()
|
||||
]() mutable {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
runner([=, guard = std::move(guard)] {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
timerEvent();
|
||||
});
|
||||
};
|
||||
_object.call(timeout, _type, std::move(method));
|
||||
}
|
||||
|
||||
void ConcurrentTimer::timerEvent() {
|
||||
if (repeat() == Repeat::Interval) {
|
||||
if (_adjusted) {
|
||||
start(_timeout, _type, repeat());
|
||||
} else {
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimer::cancel() {
|
||||
_running = {};
|
||||
if (isActive()) {
|
||||
_running = base::binary_guard();
|
||||
_object.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
crl::time ConcurrentTimer::remainingTime() const {
|
||||
if (!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
const auto now = crl::now();
|
||||
return (_next > now) ? (_next - now) : crl::time(0);
|
||||
}
|
||||
|
||||
void ConcurrentTimer::adjust() {
|
||||
auto remaining = remainingTime();
|
||||
if (remaining >= 0) {
|
||||
cancelAndSchedule(remaining);
|
||||
_adjusted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimer::setTimeout(crl::time timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
}
|
||||
|
||||
int ConcurrentTimer::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
145
Telegram/lib_base/base/concurrent_timer.h
Normal file
145
Telegram/lib_base/base/concurrent_timer.h
Normal file
@@ -0,0 +1,145 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/binary_guard.h"
|
||||
#include <crl/crl_time.h>
|
||||
#include <crl/crl_object_on_queue.h>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
class TimerObject;
|
||||
|
||||
class TimerObjectWrap {
|
||||
public:
|
||||
explicit TimerObjectWrap(Fn<void()> adjust);
|
||||
~TimerObjectWrap();
|
||||
|
||||
void call(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void sendEvent(std::unique_ptr<QEvent> event);
|
||||
|
||||
std::unique_ptr<TimerObject> _value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
class ConcurrentTimerEnvironment {
|
||||
public:
|
||||
ConcurrentTimerEnvironment();
|
||||
~ConcurrentTimerEnvironment();
|
||||
|
||||
std::unique_ptr<details::TimerObject> createTimer(Fn<void()> adjust);
|
||||
|
||||
static void Adjust();
|
||||
|
||||
private:
|
||||
void acquire();
|
||||
void release();
|
||||
void adjustTimers();
|
||||
|
||||
QThread _thread;
|
||||
QObject _adjuster;
|
||||
|
||||
};
|
||||
|
||||
class ConcurrentTimer {
|
||||
public:
|
||||
explicit ConcurrentTimer(
|
||||
Fn<void(FnMut<void()>)> runner,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
template <typename Policy, typename Object>
|
||||
explicit ConcurrentTimer(
|
||||
crl::details::weak_async<Policy, Object> weak,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(crl::time timeout) {
|
||||
constexpr auto kThreshold = crl::time(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
void setCallback(Fn<void()> callback) {
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout) {
|
||||
callOnce(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout) {
|
||||
callEach(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::SingleShot);
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::Interval);
|
||||
}
|
||||
|
||||
bool isActive() const {
|
||||
return _running.alive();
|
||||
}
|
||||
|
||||
void cancel();
|
||||
crl::time remainingTime() const;
|
||||
|
||||
private:
|
||||
enum class Repeat : unsigned {
|
||||
Interval = 0,
|
||||
SingleShot = 1,
|
||||
};
|
||||
Fn<void()> createAdjuster();
|
||||
void start(crl::time timeout, Qt::TimerType type, Repeat repeat);
|
||||
void adjust();
|
||||
|
||||
void cancelAndSchedule(int timeout);
|
||||
|
||||
void setTimeout(crl::time timeout);
|
||||
int timeout() const;
|
||||
|
||||
void timerEvent();
|
||||
|
||||
void setRepeat(Repeat repeat) {
|
||||
_repeat = static_cast<unsigned>(repeat);
|
||||
}
|
||||
Repeat repeat() const {
|
||||
return static_cast<Repeat>(_repeat);
|
||||
}
|
||||
|
||||
Fn<void(FnMut<void()>)> _runner;
|
||||
std::shared_ptr<bool> _guard; // Must be before _object.
|
||||
details::TimerObjectWrap _object;
|
||||
Fn<void()> _callback;
|
||||
base::binary_guard _running;
|
||||
crl::time _next = 0;
|
||||
int _timeout = 0;
|
||||
|
||||
Qt::TimerType _type : 2;
|
||||
bool _adjusted : 1 = false;
|
||||
unsigned _repeat : 1 = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Policy, typename Object>
|
||||
ConcurrentTimer::ConcurrentTimer(
|
||||
crl::details::weak_async<Policy, Object> weak,
|
||||
Fn<void()> callback)
|
||||
: ConcurrentTimer(weak.runner(), std::move(callback)) {
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
35
Telegram/lib_base/base/const_string.h
Normal file
35
Telegram/lib_base/base/const_string.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <string_view>
|
||||
|
||||
namespace base {
|
||||
|
||||
class const_string final : public std::string_view {
|
||||
public:
|
||||
using std::string_view::string_view;
|
||||
|
||||
[[nodiscard]] QString utf16() const {
|
||||
return QString::fromUtf8(data(), size());
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray utf8() const {
|
||||
return QByteArray::fromRawData(data(), size());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
[[nodiscard]] inline constexpr base::const_string operator""_cs(
|
||||
const char *data,
|
||||
std::size_t size) {
|
||||
return { data, size };
|
||||
}
|
||||
112
Telegram/lib_base/base/crash_report_header.cpp
Normal file
112
Telegram/lib_base/base/crash_report_header.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/crash_report_header.h"
|
||||
|
||||
namespace base::details {
|
||||
namespace {
|
||||
|
||||
std::array<char, kReportHeaderSizeLimit> Bytes;
|
||||
int Length = 0;
|
||||
|
||||
void SafeWriteChar(char ch) {
|
||||
if (Length < kReportHeaderSizeLimit) {
|
||||
Bytes[Length++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void SafeWriteNumber(Type number) {
|
||||
if constexpr (Type(-1) < Type(0)) {
|
||||
if (number < 0) {
|
||||
SafeWriteChar('-');
|
||||
number = -number;
|
||||
}
|
||||
}
|
||||
Type upper = 1, prev = number / 10;
|
||||
while (prev >= upper) {
|
||||
upper *= 10;
|
||||
}
|
||||
while (upper > 0) {
|
||||
int digit = (number / upper);
|
||||
SafeWriteChar('0' + digit);
|
||||
number -= digit * upper;
|
||||
upper /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, const char *str) {
|
||||
if (str) {
|
||||
while (const auto ch = *str++) {
|
||||
SafeWriteChar(ch);
|
||||
}
|
||||
}
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, const wchar_t *str) {
|
||||
for (int i = 0, l = wcslen(str); i < l; ++i) {
|
||||
if (
|
||||
#if !defined(__WCHAR_UNSIGNED__)
|
||||
str[i] >= 0 &&
|
||||
#endif
|
||||
str[i] < 128) {
|
||||
SafeWriteChar(char(str[i]));
|
||||
} else {
|
||||
SafeWriteChar('?');
|
||||
}
|
||||
}
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, int num) {
|
||||
SafeWriteNumber(num);
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned int num) {
|
||||
SafeWriteNumber(num);
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned long num) {
|
||||
SafeWriteNumber(num);
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned long long num) {
|
||||
SafeWriteNumber(num);
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, double num) {
|
||||
if (num < 0) {
|
||||
SafeWriteChar('-');
|
||||
num = -num;
|
||||
}
|
||||
SafeWriteNumber(uint64(floor(num)));
|
||||
SafeWriteChar('.');
|
||||
num -= floor(num);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
num *= 10;
|
||||
int digit = int(floor(num));
|
||||
SafeWriteChar('0' + digit);
|
||||
num -= digit;
|
||||
}
|
||||
return ReportHeaderWriter();
|
||||
}
|
||||
|
||||
const char *ReportHeaderBytes() {
|
||||
return Bytes.data();
|
||||
}
|
||||
|
||||
int ReportHeaderLength() {
|
||||
return Length;
|
||||
}
|
||||
|
||||
} // namespace base::details
|
||||
27
Telegram/lib_base/base/crash_report_header.h
Normal file
27
Telegram/lib_base/base/crash_report_header.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::details {
|
||||
|
||||
inline constexpr auto kReportHeaderSizeLimit = 64 * 1024;
|
||||
|
||||
struct ReportHeaderWriter {
|
||||
};
|
||||
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, const char *str);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, const wchar_t *str);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, int num);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned int num);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned long num);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, unsigned long long num);
|
||||
ReportHeaderWriter operator<<(ReportHeaderWriter, double num);
|
||||
|
||||
[[nodiscard]] const char *ReportHeaderBytes();
|
||||
[[nodiscard]] int ReportHeaderLength();
|
||||
|
||||
} // namespace base::details
|
||||
462
Telegram/lib_base/base/crash_report_writer.cpp
Normal file
462
Telegram/lib_base/base/crash_report_writer.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/crash_report_writer.h"
|
||||
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/integration.h"
|
||||
#include "base/crash_report_header.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QMutexLocker>
|
||||
#include <signal.h>
|
||||
#include <new>
|
||||
#include <mutex>
|
||||
|
||||
#if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
|
||||
#define USE_BREAKPAD
|
||||
#endif // !Q_OS_MAC || MAC_USE_BREAKPAD
|
||||
|
||||
// see https://blog.inventic.eu/2012/08/qt-and-google-breakpad/
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091)
|
||||
#include <client/windows/handler/exception_handler.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_BREAKPAD
|
||||
#include <client/mac/handler/exception_handler.h>
|
||||
#else // USE_BREAKPAD
|
||||
#include <client/crashpad_client.h>
|
||||
#endif // USE_BREAKPAD
|
||||
|
||||
#else // Q_OS_MAC
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <client/linux/handler/exception_handler.h>
|
||||
|
||||
#endif // else for Q_OS_WIN || Q_OS_MAC
|
||||
|
||||
namespace base::Platform {
|
||||
using namespace ::Platform;
|
||||
} // namespace base::Platform
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
using namespace details;
|
||||
|
||||
CrashReportWriter *Instance = nullptr;
|
||||
|
||||
QMutex AnnotationsMutex;
|
||||
std::map<std::string, std::string> Annotations;
|
||||
|
||||
int ReportFileNo = -1;
|
||||
|
||||
std::atomic<Qt::HANDLE> ReportingThreadId = nullptr;
|
||||
bool SkipWriteReportHeader = false;
|
||||
bool ReportingHeaderWritten = false;
|
||||
QMutex ReportingMutex;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const wchar_t *BreakpadDumpId = nullptr;
|
||||
std::wstring FinalReportPath;
|
||||
#else // Q_OS_WIN
|
||||
const char *BreakpadDumpId = nullptr;
|
||||
std::string FinalReportPath;
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
using ReservedMemoryChunk = std::array<gsl::byte, 1024 * 1024>;
|
||||
std::unique_ptr<ReservedMemoryChunk> ReservedMemory;
|
||||
|
||||
const char *PlatformString() {
|
||||
if (Platform::IsWindowsStoreBuild()) {
|
||||
return Platform::IsWindows64Bit()
|
||||
? "WinStore64Bit"
|
||||
: "WinStore32Bit";
|
||||
} else if (Platform::IsWindows32Bit()) {
|
||||
return "Windows32Bit";
|
||||
} else if (Platform::IsWindows64Bit()) {
|
||||
return "Windows64Bit";
|
||||
} else if (Platform::IsMacStoreBuild()) {
|
||||
return "MacAppStore";
|
||||
} else if (Platform::IsMac()) {
|
||||
return "MacOS";
|
||||
} else if (Platform::IsLinux()) {
|
||||
return "Linux";
|
||||
}
|
||||
Unexpected("Platform in CrashReports::PlatformString.");
|
||||
}
|
||||
|
||||
void AddAnnotation(std::string key, std::string value) {
|
||||
QMutexLocker lock(&AnnotationsMutex);
|
||||
Annotations.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
void InstallOperatorNewHandler() {
|
||||
ReservedMemory = std::make_unique<ReservedMemoryChunk>();
|
||||
std::set_new_handler([] {
|
||||
std::set_new_handler(nullptr);
|
||||
ReservedMemory.reset();
|
||||
Unexpected("Could not allocate!");
|
||||
});
|
||||
}
|
||||
|
||||
void InstallQtMessageHandler() {
|
||||
static QtMessageHandler original = nullptr;
|
||||
original = qInstallMessageHandler([](
|
||||
QtMsgType type,
|
||||
const QMessageLogContext &context,
|
||||
const QString &message) {
|
||||
if (original) {
|
||||
original(type, context, message);
|
||||
}
|
||||
if (type == QtFatalMsg && Instance) {
|
||||
AddAnnotation("QtFatal", message.toStdString());
|
||||
Unexpected("Qt FATAL message was generated!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void SignalHandler(int signum) {
|
||||
#else // Q_OS_WIN
|
||||
struct sigaction SIG_def[32];
|
||||
|
||||
void SignalHandler(int signum, siginfo_t *info, void *ucontext) {
|
||||
if (signum > 0) {
|
||||
sigaction(signum, &SIG_def[signum], 0);
|
||||
}
|
||||
|
||||
#endif // else for Q_OS_WIN
|
||||
|
||||
const char* name = 0;
|
||||
switch (signum) {
|
||||
case SIGABRT: name = "SIGABRT"; break;
|
||||
case SIGSEGV: name = "SIGSEGV"; break;
|
||||
case SIGILL: name = "SIGILL"; break;
|
||||
case SIGFPE: name = "SIGFPE"; break;
|
||||
#ifndef Q_OS_WIN
|
||||
case SIGBUS: name = "SIGBUS"; break;
|
||||
case SIGSYS: name = "SIGSYS"; break;
|
||||
#endif // !Q_OS_WIN
|
||||
}
|
||||
|
||||
const auto thread = QThread::currentThreadId();
|
||||
if (thread == ReportingThreadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker lock(&ReportingMutex);
|
||||
ReportingThreadId = thread;
|
||||
|
||||
if (SkipWriteReportHeader || ReportFileNo < 0) {
|
||||
return;
|
||||
}
|
||||
if (!ReportingHeaderWritten) {
|
||||
ReportingHeaderWritten = true;
|
||||
|
||||
QMutexLocker lock(&AnnotationsMutex);
|
||||
for (const auto &i : Annotations) {
|
||||
ReportHeaderWriter() << i.first.c_str() << ": " << i.second.c_str() << "\n";
|
||||
}
|
||||
ReportHeaderWriter() << "\n";
|
||||
}
|
||||
if (name) {
|
||||
ReportHeaderWriter() << "Caught signal " << signum << " (" << name << ") in thread " << uint64(thread) << "\n";
|
||||
} else if (signum == -1) {
|
||||
ReportHeaderWriter() << "Google Breakpad caught a crash, minidump written in thread " << uint64(thread) << "\n";
|
||||
if (BreakpadDumpId) {
|
||||
ReportHeaderWriter() << "Minidump: " << BreakpadDumpId << "\n";
|
||||
}
|
||||
} else {
|
||||
ReportHeaderWriter() << "Caught signal " << signum << " in thread " << uint64(thread) << "\n";
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
_write(ReportFileNo, ReportHeaderBytes(), ReportHeaderLength());
|
||||
_close(ReportFileNo);
|
||||
#else // Q_OS_WIN
|
||||
[[maybe_unused]] auto result_ = write(ReportFileNo, ReportHeaderBytes(), ReportHeaderLength());
|
||||
close(ReportFileNo);
|
||||
#endif // Q_OS_WIN
|
||||
ReportFileNo = -1;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (BreakpadDumpId) {
|
||||
FinalReportPath.append(BreakpadDumpId);
|
||||
FinalReportPath.append(L".txt");
|
||||
auto handle = int();
|
||||
const auto errcode = _wsopen_s(
|
||||
&handle,
|
||||
FinalReportPath.c_str(),
|
||||
_O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY,
|
||||
_SH_DENYWR,
|
||||
_S_IWRITE);
|
||||
if (!errcode) {
|
||||
_write(handle, ReportHeaderBytes(), ReportHeaderLength());
|
||||
_close(handle);
|
||||
}
|
||||
}
|
||||
#else // Q_OS_WIN
|
||||
if (BreakpadDumpId) {
|
||||
FinalReportPath.append(BreakpadDumpId);
|
||||
const auto good = int(FinalReportPath.size()) - 4;
|
||||
if (good > 0 && !strcmp(FinalReportPath.c_str() + good, ".dmp")) {
|
||||
FinalReportPath.erase(FinalReportPath.begin() + good, FinalReportPath.end());
|
||||
}
|
||||
FinalReportPath.append(".txt");
|
||||
const auto handle = open(
|
||||
FinalReportPath.c_str(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (handle >= 0) {
|
||||
[[maybe_unused]] auto result_ = write(handle, ReportHeaderBytes(), ReportHeaderLength());
|
||||
close(handle);
|
||||
}
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
ReportingThreadId = nullptr;
|
||||
}
|
||||
|
||||
bool SetSignalHandlers = Platform::IsLinux() || Platform::IsMac();
|
||||
bool CrashLogged = false;
|
||||
|
||||
#ifdef USE_BREAKPAD
|
||||
google_breakpad::ExceptionHandler* BreakpadExceptionHandler = 0;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
bool DumpCallback(const wchar_t* _dump_dir, const wchar_t* _minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool success)
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
bool DumpCallback(const char* _dump_dir, const char* _minidump_id, void *context, bool success)
|
||||
#else // Q_OS_MAC
|
||||
bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)
|
||||
#endif // else for Q_OS_WIN || Q_OS_MAC
|
||||
{
|
||||
if (CrashLogged) return success;
|
||||
CrashLogged = true;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
BreakpadDumpId = _minidump_id;
|
||||
SignalHandler(-1);
|
||||
#else // Q_OS_WIN
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
BreakpadDumpId = _minidump_id;
|
||||
#else // Q_OS_MAC
|
||||
BreakpadDumpId = md.path();
|
||||
auto afterLastSlash = BreakpadDumpId;
|
||||
for (auto ch = afterLastSlash; *ch != 0; ++ch) {
|
||||
if (*ch == '/') {
|
||||
afterLastSlash = (ch + 1);
|
||||
}
|
||||
}
|
||||
if (*afterLastSlash) {
|
||||
BreakpadDumpId = afterLastSlash;
|
||||
}
|
||||
#endif // else for Q_OS_MAC
|
||||
SignalHandler(-1, 0, 0);
|
||||
|
||||
#endif // else for Q_OS_WIN
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif // USE_BREAKPAD
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashReportWriter::CrashReportWriter(const QString &path) : _path(path) {
|
||||
Expects(Instance == nullptr);
|
||||
Expects(_path.endsWith('/'));
|
||||
|
||||
Instance = this;
|
||||
_previousReport = readPreviousReport();
|
||||
}
|
||||
|
||||
CrashReportWriter::~CrashReportWriter() {
|
||||
Expects(Instance == this);
|
||||
|
||||
finishCatching();
|
||||
closeReport();
|
||||
|
||||
Instance = nullptr;
|
||||
}
|
||||
|
||||
void CrashReportWriter::start() {
|
||||
AddAnnotation(
|
||||
"Launched",
|
||||
QDateTime::currentDateTime().toString(
|
||||
"dd.MM.yyyy hh:mm:ss"
|
||||
).toStdString());
|
||||
AddAnnotation("Platform", PlatformString());
|
||||
|
||||
QDir().mkpath(_path);
|
||||
|
||||
openReport();
|
||||
startCatching();
|
||||
}
|
||||
|
||||
bool CrashReportWriter::openReport() {
|
||||
if (ReportFileNo >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to lock the report file to kill
|
||||
// all remaining processes that opened it.
|
||||
_reportFile.setFileName(reportPath());
|
||||
if (!_reportLock.lock(_reportFile, QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
ReportFileNo = _reportFile.handle();
|
||||
if (ReportFileNo < 0) {
|
||||
return false;
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
FinalReportPath = _path.toStdWString();
|
||||
#else // Q_OS_WIN
|
||||
FinalReportPath = QFile::encodeName(_path).toStdString();
|
||||
#endif // Q_OS_WIN
|
||||
FinalReportPath.reserve(FinalReportPath.size() + 1024);
|
||||
|
||||
if (SetSignalHandlers) {
|
||||
#ifndef Q_OS_WIN
|
||||
struct sigaction sigact;
|
||||
|
||||
sigact.sa_sigaction = SignalHandler;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
|
||||
|
||||
sigaction(SIGABRT, &sigact, &SIG_def[SIGABRT]);
|
||||
sigaction(SIGSEGV, &sigact, &SIG_def[SIGSEGV]);
|
||||
sigaction(SIGILL, &sigact, &SIG_def[SIGILL]);
|
||||
sigaction(SIGFPE, &sigact, &SIG_def[SIGFPE]);
|
||||
sigaction(SIGBUS, &sigact, &SIG_def[SIGBUS]);
|
||||
sigaction(SIGSYS, &sigact, &SIG_def[SIGSYS]);
|
||||
#else // !Q_OS_WIN
|
||||
signal(SIGABRT, SignalHandler);
|
||||
signal(SIGSEGV, SignalHandler);
|
||||
signal(SIGILL, SignalHandler);
|
||||
signal(SIGFPE, SignalHandler);
|
||||
#endif // else for !Q_OS_WIN
|
||||
}
|
||||
|
||||
InstallOperatorNewHandler();
|
||||
InstallQtMessageHandler();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CrashReportWriter::closeReport() {
|
||||
QMutexLocker lock(&ReportingMutex);
|
||||
if (SkipWriteReportHeader) {
|
||||
return;
|
||||
}
|
||||
SkipWriteReportHeader = true;
|
||||
lock.unlock();
|
||||
|
||||
_reportLock.unlock();
|
||||
_reportFile.close();
|
||||
_reportFile.remove();
|
||||
ReportFileNo = -1;
|
||||
}
|
||||
|
||||
void CrashReportWriter::startCatching() {
|
||||
#ifdef Q_OS_WIN
|
||||
BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
|
||||
_path.toStdWString(),
|
||||
google_breakpad::ExceptionHandler::FilterCallback(nullptr),
|
||||
DumpCallback,
|
||||
(void*)nullptr, // callback_context
|
||||
google_breakpad::ExceptionHandler::HANDLER_ALL,
|
||||
MINIDUMP_TYPE(MiniDumpNormal),
|
||||
// MINIDUMP_TYPE(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules | MiniDumpWithFullAuxiliaryState | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation),
|
||||
(const wchar_t*)nullptr, // pipe_name
|
||||
(const google_breakpad::CustomClientInfo*)nullptr
|
||||
);
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
|
||||
#ifdef USE_BREAKPAD
|
||||
#ifndef _DEBUG
|
||||
BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
|
||||
QFile::encodeName(_path).toStdString(),
|
||||
/*FilterCallback*/ 0,
|
||||
DumpCallback,
|
||||
/*context*/ 0,
|
||||
true,
|
||||
0
|
||||
);
|
||||
#endif // !_DEBUG
|
||||
#else // USE_BREAKPAD
|
||||
crashpad::CrashpadClient crashpad_client;
|
||||
const auto handler = (Integration::Instance().executablePath() + "/Contents/Helpers/crashpad_handler").toStdString();
|
||||
const auto database = QFile::encodeName(_path).constData();
|
||||
if (crashpad_client.StartHandler(
|
||||
base::FilePath(handler),
|
||||
base::FilePath(database),
|
||||
{}, // metrics_dir
|
||||
std::string(), // url
|
||||
Annotations,
|
||||
std::vector<std::string>(), // arguments
|
||||
false, // restartable
|
||||
false)) { // asynchronous_start
|
||||
}
|
||||
#endif // USE_BREAKPAD
|
||||
#else
|
||||
BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
|
||||
google_breakpad::MinidumpDescriptor(QFile::encodeName(_path).toStdString()),
|
||||
/*FilterCallback*/ 0,
|
||||
DumpCallback,
|
||||
/*context*/ 0,
|
||||
true,
|
||||
-1
|
||||
);
|
||||
#endif // else for Q_OS_WIN || Q_OS_MAC
|
||||
}
|
||||
|
||||
void CrashReportWriter::finishCatching() {
|
||||
#ifdef USE_BREAKPAD
|
||||
delete base::take(BreakpadExceptionHandler);
|
||||
#endif // USE_BREAKPAD
|
||||
}
|
||||
|
||||
void CrashReportWriter::addAnnotation(std::string key, std::string value) {
|
||||
AddAnnotation(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
QString CrashReportWriter::reportPath() const {
|
||||
return _path + "report";
|
||||
}
|
||||
|
||||
std::optional<QByteArray> CrashReportWriter::readPreviousReport() {
|
||||
auto file = QFile(reportPath());
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
return file.readAll();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace CrashReports
|
||||
38
Telegram/lib_base/base/crash_report_writer.h
Normal file
38
Telegram/lib_base/base/crash_report_writer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/file_lock.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class CrashReportWriter final {
|
||||
public:
|
||||
CrashReportWriter(const QString &path);
|
||||
~CrashReportWriter();
|
||||
|
||||
void start();
|
||||
|
||||
void addAnnotation(std::string key, std::string value);
|
||||
|
||||
private:
|
||||
[[nodiscard]] QString reportPath() const;
|
||||
[[nodiscard]] std::optional<QByteArray> readPreviousReport();
|
||||
bool openReport();
|
||||
void closeReport();
|
||||
void startCatching();
|
||||
void finishCatching();
|
||||
|
||||
const QString _path;
|
||||
FileLock _reportLock;
|
||||
QFile _reportFile;
|
||||
std::optional<QByteArray> _previousReport;
|
||||
std::map<std::string, std::string> _annotations;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
60
Telegram/lib_base/base/crc32hash.cpp
Normal file
60
Telegram/lib_base/base/crc32hash.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/crc32hash.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
class Crc32Table {
|
||||
public:
|
||||
Crc32Table() {
|
||||
auto poly = std::uint32_t(0x04c11db7);
|
||||
for (auto i = 0; i != 256; ++i) {
|
||||
_data[i] = reflect(i, 8) << 24;
|
||||
for (auto j = 0; j != 8; ++j) {
|
||||
_data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0);
|
||||
}
|
||||
_data[i] = reflect(_data[i], 32);
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t operator[](int index) const {
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint32_t reflect(std::uint32_t val, char ch) {
|
||||
auto result = std::uint32_t(0);
|
||||
for (int i = 1; i < (ch + 1); ++i) {
|
||||
if (val & 1) {
|
||||
result |= 1 << (ch - i);
|
||||
}
|
||||
val >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::uint32_t _data[256];
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::int32_t crc32(const void *data, int len) {
|
||||
static const auto kTable = Crc32Table();
|
||||
|
||||
const auto buffer = static_cast<const std::uint8_t*>(data);
|
||||
|
||||
auto crc = std::uint32_t(0xffffffff);
|
||||
for (auto i = 0; i != len; ++i) {
|
||||
crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]];
|
||||
}
|
||||
|
||||
return static_cast<std::int32_t>(crc ^ 0xffffffff);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
15
Telegram/lib_base/base/crc32hash.h
Normal file
15
Telegram/lib_base/base/crc32hash.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace base {
|
||||
|
||||
std::int32_t crc32(const void *data, int len);
|
||||
|
||||
} // namespace base
|
||||
29
Telegram/lib_base/base/custom_app_icon.h
Normal file
29
Telegram/lib_base/base/custom_app_icon.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/platform/base_platform_custom_app_icon.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
inline std::optional<uint64> SetCustomAppIcon(QImage image) {
|
||||
return Platform::SetCustomAppIcon(std::move(image));
|
||||
}
|
||||
|
||||
inline std::optional<uint64> SetCustomAppIcon(const QString &path) {
|
||||
return Platform::SetCustomAppIcon(path);
|
||||
}
|
||||
|
||||
inline std::optional<uint64> CurrentCustomAppIconDigest() {
|
||||
return Platform::CurrentCustomAppIconDigest();
|
||||
}
|
||||
|
||||
inline bool ClearCustomAppIcon() {
|
||||
return Platform::ClearCustomAppIcon();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
21
Telegram/lib_base/base/custom_delete.h
Normal file
21
Telegram/lib_base/base/custom_delete.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <auto fn>
|
||||
struct custom_delete {
|
||||
template <typename T>
|
||||
constexpr void operator()(T* value) const {
|
||||
if (value) {
|
||||
fn(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
35
Telegram/lib_base/base/debug_destroy_informer.h
Normal file
35
Telegram/lib_base/base/debug_destroy_informer.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template <typename T>
|
||||
class PrintDead : public T {
|
||||
public:
|
||||
template <typename... Args>
|
||||
PrintDead(Args&&... args) : T(std::forward<Args>(args)...) {}
|
||||
|
||||
~PrintDead() {
|
||||
std::cout
|
||||
<< "\033[32m" // Green.
|
||||
<< "Instance of class "
|
||||
<< typeid(T).name()
|
||||
<< " is dead!"
|
||||
<< "\033[0m"
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// #include "base/debug_destroy_informer.h"
|
||||
// Usage: std::make_shared<PrintDead<QSvgRenderer>>(...);
|
||||
// Usage: PrintDead<QSvgRenderer>(...);
|
||||
|
||||
#endif // _DEBUG
|
||||
37
Telegram/lib_base/base/debug_log.cpp
Normal file
37
Telegram/lib_base/base/debug_log.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#include "base/integration.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
void LogWriteMain(const QString &message) {
|
||||
if (Integration::Exists()) {
|
||||
Integration::Instance().logMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
void LogWriteDebug(const QString &message, const char *file, int line) {
|
||||
Expects(!LogSkipDebug());
|
||||
|
||||
Integration::Instance().logMessageDebug(QString("%1 (%2 : %3)").arg(
|
||||
message,
|
||||
QString::fromUtf8(file),
|
||||
QString::number(__LINE__)));
|
||||
}
|
||||
|
||||
bool LogSkipDebug() {
|
||||
return !Integration::Exists() || Integration::Instance().logSkipDebug();
|
||||
}
|
||||
|
||||
QString LogProfilePrefix() {
|
||||
const auto now = crl::profile();
|
||||
return '[' + QString::number(now / 1000., 'f', 3) + "] ";
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
38
Telegram/lib_base/base/debug_log.h
Normal file
38
Telegram/lib_base/base/debug_log.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/assertion.h" // SOURCE_FILE_BASENAME
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace base {
|
||||
|
||||
void LogWriteMain(const QString &message);
|
||||
void LogWriteDebug(const QString &message, const char *file, int line);
|
||||
[[nodiscard]] bool LogSkipDebug();
|
||||
|
||||
[[nodiscard]] QString LogProfilePrefix();
|
||||
|
||||
} // namespace base
|
||||
|
||||
#define LOG(message) (::base::LogWriteMain(QString message))
|
||||
//usage LOG(("log: %1 %2").arg(1).arg(2))
|
||||
|
||||
#define PROFILE_LOG(message) {\
|
||||
if (!::base::LogSkipDebug()) {\
|
||||
::base::LogWriteMain(::base::LogProfilePrefix() + QString message);\
|
||||
}\
|
||||
}
|
||||
//usage PROFILE_LOG(("step: %1 %2").arg(1).arg(2))
|
||||
|
||||
#define DEBUG_LOG(message) {\
|
||||
if (!::base::LogSkipDebug()) {\
|
||||
::base::LogWriteDebug(QString message, SOURCE_FILE_BASENAME, __LINE__);\
|
||||
}\
|
||||
}
|
||||
//usage DEBUG_LOG(("log: %1 %2").arg(1).arg(2))
|
||||
52
Telegram/lib_base/base/enum_mask.h
Normal file
52
Telegram/lib_base/base/enum_mask.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Enum>
|
||||
class enum_mask {
|
||||
using Type = std::uint32_t;
|
||||
|
||||
public:
|
||||
static_assert(static_cast<int>(Enum::kCount) <= 32, "We have only 32 bit.");
|
||||
|
||||
enum_mask() = default;
|
||||
enum_mask(Enum value) : _value(ToBit(value)) {
|
||||
}
|
||||
|
||||
static enum_mask All() {
|
||||
auto result = enum_mask();
|
||||
result._value = ~Type(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
enum_mask added(enum_mask other) const {
|
||||
auto result = *this;
|
||||
result.set(other);
|
||||
return result;
|
||||
}
|
||||
void set(enum_mask other) {
|
||||
_value |= other._value;
|
||||
}
|
||||
bool test(Enum value) const {
|
||||
return _value & ToBit(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static Type ToBit(Enum value) {
|
||||
return 1 << static_cast<Type>(value);
|
||||
}
|
||||
Type _value = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
61
Telegram/lib_base/base/event_filter.cpp
Normal file
61
Telegram/lib_base/base/event_filter.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/event_filter.h"
|
||||
|
||||
#include "base/weak_qptr.h"
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
EventFilter::EventFilter(
|
||||
not_null<QObject*> parent,
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter)
|
||||
: QObject(parent)
|
||||
, _filter(std::move(filter)) {
|
||||
object->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool EventFilter::eventFilter(QObject *watched, QEvent *event) {
|
||||
return (_filter(event) == EventFilterResult::Cancel);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
not_null<QObject*> install_event_filter(
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter) {
|
||||
return install_event_filter(object, object, std::move(filter));
|
||||
}
|
||||
|
||||
not_null<QObject*> install_event_filter(
|
||||
not_null<QObject*> context,
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter) {
|
||||
return new details::EventFilter(context, object, std::move(filter));
|
||||
}
|
||||
|
||||
void install_event_filter(
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter,
|
||||
rpl::lifetime &lifetime) {
|
||||
// Not safe in case object is deleted before lifetime.
|
||||
//
|
||||
//lifetime.make_state<details::EventFilter>(
|
||||
// object,
|
||||
// object,
|
||||
// std::move(filter));
|
||||
|
||||
const auto raw = install_event_filter(object, std::move(filter));
|
||||
lifetime.add([weak = make_weak(raw)] {
|
||||
if (const auto strong = weak.get()) {
|
||||
delete strong;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
52
Telegram/lib_base/base/event_filter.h
Normal file
52
Telegram/lib_base/base/event_filter.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
namespace base {
|
||||
|
||||
enum class EventFilterResult {
|
||||
Continue,
|
||||
Cancel,
|
||||
};
|
||||
|
||||
not_null<QObject*> install_event_filter(
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter);
|
||||
|
||||
not_null<QObject*> install_event_filter(
|
||||
not_null<QObject*> context,
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter);
|
||||
|
||||
void install_event_filter(
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter,
|
||||
rpl::lifetime &lifetime);
|
||||
|
||||
namespace details {
|
||||
|
||||
class EventFilter : public QObject {
|
||||
public:
|
||||
EventFilter(
|
||||
not_null<QObject*> parent,
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> filter);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event);
|
||||
|
||||
private:
|
||||
Fn<EventFilterResult(not_null<QEvent*>)> _filter;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace base
|
||||
17
Telegram/lib_base/base/expected.h
Normal file
17
Telegram/lib_base/base/expected.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
namespace base {
|
||||
|
||||
using ::tl::expected;
|
||||
using ::tl::unexpected;
|
||||
using ::tl::make_unexpected;
|
||||
|
||||
} // namespace base
|
||||
38
Telegram/lib_base/base/file_lock.h
Normal file
38
Telegram/lib_base/base/file_lock.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
#include <QtCore/QFile>
|
||||
|
||||
namespace base {
|
||||
|
||||
class FileLock {
|
||||
public:
|
||||
FileLock();
|
||||
|
||||
bool lock(QFile &file, QIODevice::OpenMode mode);
|
||||
[[nodiscard]] bool locked() const;
|
||||
void unlock();
|
||||
|
||||
static constexpr auto kSkipBytes = size_type(4);
|
||||
|
||||
~FileLock();
|
||||
|
||||
private:
|
||||
class Lock;
|
||||
struct Descriptor;
|
||||
struct LockingPid;
|
||||
|
||||
static constexpr auto kLockOffset = index_type(0);
|
||||
static constexpr auto kLockLimit = kSkipBytes;
|
||||
|
||||
std::unique_ptr<Lock> _lock;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
126
Telegram/lib_base/base/file_lock_posix.cpp
Normal file
126
Telegram/lib_base/base/file_lock_posix.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/file_lock.h"
|
||||
|
||||
#include <variant>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
bool KillProcess(pid_t pid) {
|
||||
auto signal = SIGTERM;
|
||||
auto attempts = 0;
|
||||
while (true) {
|
||||
const auto result = kill(pid, signal);
|
||||
if (result < 0) {
|
||||
return (errno == ESRCH);
|
||||
}
|
||||
usleep(10000);
|
||||
if (++attempts == 50) {
|
||||
signal = SIGKILL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct FileLock::Descriptor {
|
||||
int value;
|
||||
};
|
||||
|
||||
struct FileLock::LockingPid {
|
||||
pid_t value;
|
||||
};
|
||||
|
||||
class FileLock::Lock {
|
||||
public:
|
||||
using Result = std::variant<Descriptor, LockingPid>;
|
||||
static Result Acquire(const QFile &file);
|
||||
|
||||
explicit Lock(int descriptor);
|
||||
~Lock();
|
||||
|
||||
private:
|
||||
int _descriptor = 0;
|
||||
|
||||
};
|
||||
|
||||
FileLock::Lock::Result FileLock::Lock::Acquire(const QFile &file) {
|
||||
const auto descriptor = file.handle();
|
||||
if (!descriptor || !file.isOpen()) {
|
||||
return Descriptor{ 0 };
|
||||
}
|
||||
while (true) {
|
||||
struct flock lock;
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = kLockOffset;
|
||||
lock.l_len = kLockLimit;
|
||||
if (fcntl(descriptor, F_SETLK, &lock) == 0) {
|
||||
return Descriptor{ descriptor };
|
||||
} else if (fcntl(descriptor, F_GETLK, &lock) < 0) {
|
||||
return LockingPid{ 0 };
|
||||
} else if (lock.l_type != F_UNLCK) {
|
||||
return LockingPid{ lock.l_pid };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileLock::Lock::Lock(int descriptor) : _descriptor(descriptor) {
|
||||
}
|
||||
|
||||
FileLock::Lock::~Lock() {
|
||||
struct flock unlock;
|
||||
unlock.l_type = F_UNLCK;
|
||||
unlock.l_whence = SEEK_SET;
|
||||
unlock.l_start = kLockOffset;
|
||||
unlock.l_len = kLockLimit;
|
||||
fcntl(_descriptor, F_SETLK, &unlock);
|
||||
}
|
||||
|
||||
FileLock::FileLock() = default;
|
||||
|
||||
bool FileLock::lock(QFile &file, QIODevice::OpenMode mode) {
|
||||
Expects(_lock == nullptr || file.isOpen());
|
||||
|
||||
unlock();
|
||||
file.close();
|
||||
if (!file.open(mode)) {
|
||||
return false;
|
||||
}
|
||||
while (true) {
|
||||
const auto result = Lock::Acquire(file);
|
||||
if (const auto descriptor = std::get_if<Descriptor>(&result)) {
|
||||
if (descriptor->value > 0) {
|
||||
_lock = std::make_unique<Lock>(descriptor->value);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
} else if (const auto pid = std::get_if<LockingPid>(&result)) {
|
||||
if (pid->value <= 0 || !KillProcess(pid->value)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileLock::locked() const {
|
||||
return (_lock != nullptr);
|
||||
}
|
||||
|
||||
void FileLock::unlock() {
|
||||
_lock = nullptr;
|
||||
}
|
||||
|
||||
FileLock::~FileLock() = default;
|
||||
|
||||
} // namespace base
|
||||
89
Telegram/lib_base/base/file_lock_win.cpp
Normal file
89
Telegram/lib_base/base/file_lock_win.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/file_lock.h"
|
||||
|
||||
#include "base/platform/win/base_file_utilities_win.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fileapi.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
class FileLock::Lock {
|
||||
public:
|
||||
static int Acquire(const QFile &file);
|
||||
|
||||
explicit Lock(int descriptor);
|
||||
~Lock();
|
||||
|
||||
private:
|
||||
static constexpr auto offsetLow = DWORD(kLockOffset);
|
||||
static constexpr auto offsetHigh = DWORD(0);
|
||||
static constexpr auto limitLow = DWORD(kLockLimit);
|
||||
static constexpr auto limitHigh = DWORD(0);
|
||||
|
||||
int _descriptor = 0;
|
||||
|
||||
};
|
||||
|
||||
int FileLock::Lock::Acquire(const QFile &file) {
|
||||
const auto descriptor = file.handle();
|
||||
if (!descriptor || !file.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
const auto handle = HANDLE(_get_osfhandle(descriptor));
|
||||
if (!handle) {
|
||||
return false;
|
||||
}
|
||||
return LockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh)
|
||||
? descriptor
|
||||
: 0;
|
||||
}
|
||||
|
||||
FileLock::Lock::Lock(int descriptor) : _descriptor(descriptor) {
|
||||
}
|
||||
|
||||
FileLock::Lock::~Lock() {
|
||||
if (const auto handle = HANDLE(_get_osfhandle(_descriptor))) {
|
||||
UnlockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh);
|
||||
}
|
||||
}
|
||||
|
||||
FileLock::FileLock() = default;
|
||||
|
||||
bool FileLock::lock(QFile &file, QIODevice::OpenMode mode) {
|
||||
Expects(_lock == nullptr || file.isOpen());
|
||||
|
||||
unlock();
|
||||
file.close();
|
||||
do {
|
||||
if (!file.open(mode)) {
|
||||
return false;
|
||||
} else if (const auto descriptor = Lock::Acquire(file)) {
|
||||
_lock = std::make_unique<Lock>(descriptor);
|
||||
return true;
|
||||
}
|
||||
file.close();
|
||||
} while (Platform::CloseProcesses(file.fileName()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileLock::locked() const {
|
||||
return (_lock != nullptr);
|
||||
}
|
||||
|
||||
void FileLock::unlock() {
|
||||
_lock = nullptr;
|
||||
}
|
||||
|
||||
FileLock::~FileLock() = default;
|
||||
|
||||
} // namespace base
|
||||
423
Telegram/lib_base/base/flags.h
Normal file
423
Telegram/lib_base/base/flags.h
Normal file
@@ -0,0 +1,423 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename EnumType>
|
||||
class flags;
|
||||
|
||||
template <typename ExtendedEnum>
|
||||
struct extended_flags;
|
||||
|
||||
template <typename ExtendedEnum>
|
||||
using extended_flags_t = typename extended_flags<ExtendedEnum>::type;
|
||||
|
||||
namespace details {
|
||||
|
||||
struct flags_zero_helper_struct {
|
||||
};
|
||||
|
||||
using flags_zero_helper = void(base::details::flags_zero_helper_struct::*)();
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename Enum = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto extended_flag_convert(ExtendedEnum value) {
|
||||
return static_cast<Enum>(value);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename Enum = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto extended_flags_convert(ExtendedEnum value) {
|
||||
return flags<Enum>(extended_flag_convert(value));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename EnumType>
|
||||
class flags {
|
||||
public:
|
||||
using Enum = EnumType;
|
||||
using Type = std::underlying_type_t<Enum>;
|
||||
|
||||
constexpr flags() = default;
|
||||
constexpr flags(details::flags_zero_helper) noexcept {
|
||||
}
|
||||
constexpr flags(Enum value) noexcept
|
||||
: _value(static_cast<Type>(value)) {
|
||||
}
|
||||
static constexpr flags from_raw(Type value) noexcept {
|
||||
return flags(static_cast<Enum>(value));
|
||||
}
|
||||
|
||||
constexpr auto value() const noexcept {
|
||||
return _value;
|
||||
}
|
||||
constexpr operator Type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
|
||||
constexpr auto &operator|=(flags b) noexcept {
|
||||
_value |= b.value();
|
||||
return *this;
|
||||
}
|
||||
constexpr auto &operator&=(flags b) noexcept {
|
||||
_value &= b.value();
|
||||
return *this;
|
||||
}
|
||||
constexpr auto &operator^=(flags b) noexcept {
|
||||
_value ^= b.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator~() const noexcept {
|
||||
return from_raw(~value());
|
||||
}
|
||||
|
||||
constexpr auto operator|(flags b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
constexpr auto operator&(flags b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
constexpr auto operator^(flags b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
constexpr auto operator|(Enum b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
constexpr auto operator&(Enum b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
constexpr auto operator^(Enum b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
constexpr auto operator==(Enum b) const noexcept {
|
||||
return (value() == static_cast<Type>(b));
|
||||
}
|
||||
constexpr auto operator!=(Enum b) const noexcept {
|
||||
return !(*this == b);
|
||||
}
|
||||
constexpr auto operator<(Enum b) const noexcept {
|
||||
return value() < static_cast<Type>(b);
|
||||
}
|
||||
constexpr auto operator>(Enum b) const noexcept {
|
||||
return (b < *this);
|
||||
}
|
||||
constexpr auto operator<=(Enum b) const noexcept {
|
||||
return !(b < *this);
|
||||
}
|
||||
constexpr auto operator>=(Enum b) const noexcept {
|
||||
return !(*this < b);
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Enum>
|
||||
constexpr auto make_flags(Enum value) noexcept {
|
||||
return flags<Enum>(value);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, flags<Enum> b) noexcept {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator&(Enum a, flags<Enum> b) noexcept {
|
||||
return b & a;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator^(Enum a, flags<Enum> b) noexcept {
|
||||
return b ^ a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a | details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = extended_flags_t<ExtendedEnum>>
|
||||
inline constexpr auto operator&(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a & details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator&(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b & a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = extended_flags_t<ExtendedEnum>>
|
||||
inline constexpr auto operator^(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a ^ details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator^(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b ^ a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator&=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a &= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator|=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a |= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator^=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a ^= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator==(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a == details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator==(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return (b == a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator!=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator!=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a < details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return details::extended_flags_convert(a) < b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return (b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return (b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
// For bare-enum operators to work inside namespace base we duplicate them here.
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator!(Enum a) noexcept {
|
||||
return !make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator~(Enum a) noexcept {
|
||||
return ~make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, Enum b) noexcept {
|
||||
return make_flags(a) | b;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, details::flags_zero_helper) noexcept {
|
||||
return make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(details::flags_zero_helper, Enum b) noexcept {
|
||||
return make_flags(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, ExtendedEnum b) {
|
||||
return details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, typename extended_flags<ExtendedEnum>::type b) {
|
||||
return details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(typename extended_flags<ExtendedEnum>::type a, ExtendedEnum b) {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(details::flags_zero_helper, ExtendedEnum b) {
|
||||
return 0 | details::extended_flag_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, details::flags_zero_helper) {
|
||||
return details::extended_flag_convert(a) | 0;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator~(ExtendedEnum b) {
|
||||
return ~details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator!(Enum a) noexcept {
|
||||
return !base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator~(Enum a) noexcept {
|
||||
return ~base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, Enum b) noexcept {
|
||||
return base::make_flags(a) | b;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, base::details::flags_zero_helper) noexcept {
|
||||
return base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(base::details::flags_zero_helper, Enum b) noexcept {
|
||||
return base::make_flags(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, ExtendedEnum b) {
|
||||
return base::details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, typename base::extended_flags<ExtendedEnum>::type b) {
|
||||
return base::details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(typename base::extended_flags<ExtendedEnum>::type a, ExtendedEnum b) {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(base::details::flags_zero_helper, ExtendedEnum b) {
|
||||
return 0 | base::details::extended_flag_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, base::details::flags_zero_helper) {
|
||||
return base::details::extended_flag_convert(a) | 0;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator~(ExtendedEnum b) {
|
||||
return ~base::details::extended_flags_convert(b);
|
||||
}
|
||||
128
Telegram/lib_base/base/flags_tests.cpp
Normal file
128
Telegram/lib_base/base/flags_tests.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "base/flags.h"
|
||||
|
||||
namespace MethodNamespace {
|
||||
|
||||
template <typename Enum>
|
||||
void TestFlags(Enum a, Enum b, Enum c) {
|
||||
auto abc = a | b;
|
||||
abc |= c;
|
||||
auto test = abc != a;
|
||||
CHECK(abc != a);
|
||||
CHECK(abc != (a | b));
|
||||
CHECK((abc & a) == a);
|
||||
CHECK((abc & b) == b);
|
||||
CHECK((abc & c) == c);
|
||||
CHECK((abc & ~a) == (b | c));
|
||||
CHECK((abc & ~(b | c)) == a);
|
||||
CHECK((abc ^ a) == (abc & ~a));
|
||||
|
||||
auto another = a | b;
|
||||
another |= c;
|
||||
CHECK(abc == another);
|
||||
another &= ~b;
|
||||
CHECK(another == (a | c));
|
||||
another ^= a;
|
||||
CHECK(another == c);
|
||||
another = 0;
|
||||
another = nullptr;
|
||||
auto is_zero = ((another & abc) == 0);
|
||||
CHECK(is_zero);
|
||||
CHECK(!(another & abc));
|
||||
auto more = a | another;
|
||||
auto just = a | 0;
|
||||
CHECK(more == just);
|
||||
CHECK(just);
|
||||
}
|
||||
|
||||
} // namespace MethodNamespace
|
||||
|
||||
namespace FlagsNamespace {
|
||||
|
||||
enum class Flag : int {
|
||||
one = (1 << 0),
|
||||
two = (1 << 1),
|
||||
three = (1 << 2),
|
||||
};
|
||||
inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
||||
class Class {
|
||||
public:
|
||||
enum class Public : long {
|
||||
one = (1 << 2),
|
||||
two = (1 << 1),
|
||||
three = (1 << 0),
|
||||
};
|
||||
friend inline constexpr auto is_flag_type(Public) { return true; }
|
||||
|
||||
static void TestPrivate();
|
||||
|
||||
private:
|
||||
enum class Private : long {
|
||||
one = (1 << 0),
|
||||
two = (1 << 1),
|
||||
three = (1 << 2),
|
||||
};
|
||||
friend inline constexpr auto is_flag_type(Private) { return true; }
|
||||
|
||||
};
|
||||
|
||||
void Class::TestPrivate() {
|
||||
MethodNamespace::TestFlags(Private::one, Private::two, Private::three);
|
||||
}
|
||||
|
||||
} // namespace FlagsNamespace
|
||||
|
||||
namespace ExtendedNamespace {
|
||||
|
||||
enum class Flag : int {
|
||||
one = (1 << 3),
|
||||
two = (1 << 4),
|
||||
three = (1 << 5),
|
||||
};
|
||||
|
||||
} // namespace ExtendedNamespace
|
||||
|
||||
namespace base {
|
||||
|
||||
template<>
|
||||
struct extended_flags<ExtendedNamespace::Flag> {
|
||||
using type = FlagsNamespace::Flag;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
TEST_CASE("flags operators on scoped enums", "[flags]") {
|
||||
SECTION("testing non-member flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
FlagsNamespace::Flag::one,
|
||||
FlagsNamespace::Flag::two,
|
||||
FlagsNamespace::Flag::three);
|
||||
}
|
||||
SECTION("testing public member flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
FlagsNamespace::Class::Public::one,
|
||||
FlagsNamespace::Class::Public::two,
|
||||
FlagsNamespace::Class::Public::three);
|
||||
}
|
||||
SECTION("testing private member flags") {
|
||||
FlagsNamespace::Class::TestPrivate();
|
||||
}
|
||||
SECTION("testing extended flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
ExtendedNamespace::Flag::one,
|
||||
ExtendedNamespace::Flag::two,
|
||||
ExtendedNamespace::Flag::three);
|
||||
|
||||
auto onetwo = FlagsNamespace::Flag::one | ExtendedNamespace::Flag::two;
|
||||
auto twoone = ExtendedNamespace::Flag::two | FlagsNamespace::Flag::one;
|
||||
CHECK(onetwo == twoone);
|
||||
}
|
||||
}
|
||||
1095
Telegram/lib_base/base/flat_map.h
Normal file
1095
Telegram/lib_base/base/flat_map.h
Normal file
File diff suppressed because it is too large
Load Diff
120
Telegram/lib_base/base/flat_map_tests.cpp
Normal file
120
Telegram/lib_base/base/flat_map_tests.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include <string>
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") {
|
||||
base::flat_map<int, string> v;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(5, "b");
|
||||
v.emplace(4, "d");
|
||||
v.emplace(2, "e");
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(prev->first < i->first);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.emplace(3, "c");
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("simple flat_maps tests", "[flat_map]") {
|
||||
SECTION("copy constructor") {
|
||||
base::flat_map<int, string> v;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(2, "b");
|
||||
auto u = v;
|
||||
REQUIRE(u.size() == 2);
|
||||
REQUIRE(u.find(0) == u.begin());
|
||||
REQUIRE(u.find(2) == u.end() - 1);
|
||||
}
|
||||
SECTION("assignment") {
|
||||
base::flat_map<int, string> v, u;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(2, "b");
|
||||
u = v;
|
||||
REQUIRE(u.size() == 2);
|
||||
REQUIRE(u.find(0) == u.begin());
|
||||
REQUIRE(u.find(2) == u.end() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_maps custom comparator", "[flat_map]") {
|
||||
base::flat_map<int_wrap, string, int_wrap_comparator> v;
|
||||
v.emplace(int_wrap{ 0 }, "a");
|
||||
v.emplace(int_wrap{ 5 }, "b");
|
||||
v.emplace(int_wrap{ 4 }, "d");
|
||||
v.emplace(int_wrap{ 2 }, "e");
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(int_wrap_comparator()(prev->first, i->first));
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.emplace(int_wrap{ 3 }, "c");
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find({ 3 }) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_maps structured bindings", "[flat_map]") {
|
||||
base::flat_map<int, std::unique_ptr<double>> v;
|
||||
v.emplace(0, std::make_unique<double>(0.));
|
||||
v.emplace(1, std::make_unique<double>(1.));
|
||||
|
||||
SECTION("structred binded range-based for loop") {
|
||||
for (const auto &[key, value] : v) {
|
||||
REQUIRE(key == int(std::round(*value)));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("non-const structured binded range-based for loop") {
|
||||
base::flat_map<int, int> second = {
|
||||
{ 1, 1 },
|
||||
{ 2, 2 },
|
||||
{ 2, 3 },
|
||||
{ 3, 3 },
|
||||
};
|
||||
REQUIRE(second.size() == 3);
|
||||
//for (auto [a, b] : second) { // #MSVC Bug, reported
|
||||
// REQUIRE(a == b);
|
||||
//}
|
||||
for (const auto [a, b] : second) {
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
841
Telegram/lib_base/base/flat_set.h
Normal file
841
Telegram/lib_base/base/flat_set.h
Normal file
@@ -0,0 +1,841 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace base {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_set;
|
||||
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_multi_set;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_impl;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_impl {
|
||||
public:
|
||||
using iterator_category = typename iterator_impl::iterator_category;
|
||||
|
||||
using value_type = Type;
|
||||
using difference_type = typename iterator_impl::difference_type;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
constexpr flat_multi_set_iterator_impl(
|
||||
iterator_impl impl = iterator_impl()) noexcept
|
||||
: _impl(impl) {
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
constexpr flat_multi_set_iterator_impl(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &other) noexcept
|
||||
: _impl(other._impl) {
|
||||
}
|
||||
|
||||
constexpr reference operator*() const noexcept {
|
||||
return *_impl;
|
||||
}
|
||||
constexpr pointer operator->() const noexcept {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl &operator++() noexcept {
|
||||
++_impl;
|
||||
return *this;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl operator++(int) noexcept {
|
||||
return _impl++;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl &operator--() noexcept {
|
||||
--_impl;
|
||||
return *this;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl operator--(int) noexcept {
|
||||
return _impl--;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl &operator+=(
|
||||
difference_type offset) noexcept {
|
||||
_impl += offset;
|
||||
return *this;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl operator+(
|
||||
difference_type offset) const noexcept {
|
||||
return _impl + offset;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl &operator-=(
|
||||
difference_type offset) noexcept {
|
||||
_impl -= offset;
|
||||
return *this;
|
||||
}
|
||||
constexpr flat_multi_set_iterator_impl operator-(
|
||||
difference_type offset) const noexcept {
|
||||
return _impl - offset;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
constexpr difference_type operator-(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const noexcept {
|
||||
return _impl - right._impl;
|
||||
}
|
||||
constexpr reference operator[](difference_type offset) const noexcept {
|
||||
return _impl[offset];
|
||||
}
|
||||
|
||||
template <typename other_iterator_impl>
|
||||
constexpr bool operator==(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const noexcept {
|
||||
return _impl == right._impl;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
constexpr bool operator!=(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const noexcept {
|
||||
return _impl != right._impl;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
constexpr bool operator<(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const noexcept {
|
||||
return _impl < right._impl;
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_impl _impl;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_multi_set;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_set;
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename other_iterator_impl>
|
||||
friend class flat_multi_set_iterator_impl;
|
||||
|
||||
constexpr Type &wrapped() noexcept {
|
||||
return _impl->wrapped();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class flat_multi_set_const_wrap {
|
||||
public:
|
||||
constexpr flat_multi_set_const_wrap(const Type &value) noexcept
|
||||
: _value(value) {
|
||||
}
|
||||
constexpr flat_multi_set_const_wrap(Type &&value) noexcept
|
||||
: _value(std::move(value)) {
|
||||
}
|
||||
constexpr operator const Type&() const noexcept {
|
||||
return _value;
|
||||
}
|
||||
constexpr Type &wrapped() noexcept {
|
||||
return _value;
|
||||
}
|
||||
|
||||
friend inline constexpr bool operator<(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value < b._value;
|
||||
}
|
||||
friend inline constexpr bool operator>(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value > b._value;
|
||||
}
|
||||
friend inline constexpr bool operator<=(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value <= b._value;
|
||||
}
|
||||
friend inline constexpr bool operator>=(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value >= b._value;
|
||||
}
|
||||
friend inline constexpr bool operator==(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value == b._value;
|
||||
}
|
||||
friend inline constexpr bool operator!=(
|
||||
const flat_multi_set_const_wrap &a,
|
||||
const flat_multi_set_const_wrap &b) noexcept {
|
||||
return a._value != b._value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, typename Compare>
|
||||
class flat_multi_set {
|
||||
using const_wrap = flat_multi_set_const_wrap<Type>;
|
||||
using impl_t = std::vector<const_wrap>;
|
||||
|
||||
public:
|
||||
using value_type = Type;
|
||||
using size_type = typename impl_t::size_type;
|
||||
using difference_type = typename impl_t::difference_type;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
using iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::iterator>;
|
||||
using const_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_iterator>;
|
||||
using reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::reverse_iterator>;
|
||||
using const_reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_reverse_iterator>;
|
||||
|
||||
constexpr flat_multi_set() = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
constexpr flat_multi_set(Iterator first, Iterator last) noexcept
|
||||
: _data(first, last) {
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
constexpr flat_multi_set(std::initializer_list<Type> iter) noexcept
|
||||
: flat_multi_set(iter.begin(), iter.end()) {
|
||||
}
|
||||
|
||||
constexpr size_type size() const noexcept {
|
||||
return impl().size();
|
||||
}
|
||||
constexpr bool empty() const noexcept {
|
||||
return impl().empty();
|
||||
}
|
||||
constexpr void clear() noexcept {
|
||||
impl().clear();
|
||||
}
|
||||
constexpr void reserve(size_type size) noexcept {
|
||||
impl().reserve(size);
|
||||
}
|
||||
constexpr void shrink_to_fit() noexcept {
|
||||
impl().shrink_to_fit();
|
||||
}
|
||||
|
||||
constexpr iterator begin() noexcept {
|
||||
return impl().begin();
|
||||
}
|
||||
constexpr iterator end() noexcept {
|
||||
return impl().end();
|
||||
}
|
||||
constexpr const_iterator begin() const noexcept {
|
||||
return impl().begin();
|
||||
}
|
||||
constexpr const_iterator end() const noexcept {
|
||||
return impl().end();
|
||||
}
|
||||
constexpr const_iterator cbegin() const noexcept {
|
||||
return impl().cbegin();
|
||||
}
|
||||
constexpr const_iterator cend() const noexcept {
|
||||
return impl().cend();
|
||||
}
|
||||
constexpr reverse_iterator rbegin() noexcept {
|
||||
return impl().rbegin();
|
||||
}
|
||||
constexpr reverse_iterator rend() noexcept {
|
||||
return impl().rend();
|
||||
}
|
||||
constexpr const_reverse_iterator rbegin() const noexcept {
|
||||
return impl().rbegin();
|
||||
}
|
||||
constexpr const_reverse_iterator rend() const noexcept {
|
||||
return impl().rend();
|
||||
}
|
||||
constexpr const_reverse_iterator crbegin() const noexcept {
|
||||
return impl().crbegin();
|
||||
}
|
||||
constexpr const_reverse_iterator crend() const noexcept {
|
||||
return impl().crend();
|
||||
}
|
||||
|
||||
constexpr reference front() const noexcept {
|
||||
return *begin();
|
||||
}
|
||||
constexpr reference back() const noexcept {
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
constexpr iterator insert(const Type &value) noexcept {
|
||||
if (empty() || !compare()(value, back())) {
|
||||
impl().push_back(value);
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return impl().insert(where, value);
|
||||
}
|
||||
constexpr iterator insert(Type &&value) noexcept {
|
||||
if (empty() || !compare()(value, back())) {
|
||||
impl().push_back(std::move(value));
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return impl().insert(where, std::move(value));
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr iterator emplace(Args&&... args) noexcept {
|
||||
return insert(Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr bool removeOne(const OtherType &value) noexcept {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return false;
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
if (compare()(value, *where)) {
|
||||
return false;
|
||||
}
|
||||
impl().erase(where);
|
||||
return true;
|
||||
}
|
||||
constexpr bool removeOne(const Type &value) noexcept {
|
||||
return removeOne<Type>(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr int removeAll(const OtherType &value) noexcept {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
const auto result = (range.second - range.first);
|
||||
impl().erase(range.first, range.second);
|
||||
return result;
|
||||
}
|
||||
constexpr int removeAll(const Type &value) noexcept {
|
||||
return removeAll<Type>(value);
|
||||
}
|
||||
|
||||
constexpr iterator erase(const_iterator where) noexcept {
|
||||
return impl().erase(where._impl);
|
||||
}
|
||||
constexpr iterator erase(
|
||||
const_iterator from,
|
||||
const_iterator till) noexcept {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
constexpr int erase(const Type &value) noexcept {
|
||||
return removeAll(value);
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr iterator findFirst(const OtherType &value) noexcept {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
constexpr iterator findFirst(const Type &value) noexcept {
|
||||
return findFirst<Type>(value);
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr const_iterator findFirst(
|
||||
const OtherType &value) const noexcept {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
constexpr const_iterator findFirst(
|
||||
const Type &value) const noexcept {
|
||||
return findFirst<Type>(value);
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr bool contains(const OtherType &value) const noexcept {
|
||||
return findFirst(value) != end();
|
||||
}
|
||||
constexpr bool contains(const Type &value) const noexcept {
|
||||
return contains<Type>(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr int count(const OtherType &value) const noexcept {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
constexpr int count(const Type &value) const noexcept {
|
||||
return count<Type>(value);
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr iterator lower_bound(const OtherType &value) noexcept {
|
||||
return getLowerBound(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr const_iterator lower_bound(
|
||||
const OtherType &value) const noexcept {
|
||||
return getLowerBound(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr iterator upper_bound(const OtherType &value) noexcept {
|
||||
return getUpperBound(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr const_iterator upper_bound(
|
||||
const OtherType &value) const noexcept {
|
||||
return getUpperBound(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr std::pair<iterator, iterator> equal_range(
|
||||
const OtherType &value) noexcept {
|
||||
return getEqualRange(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
const OtherType &value) const noexcept {
|
||||
return getEqualRange(value);
|
||||
}
|
||||
|
||||
template <typename Action>
|
||||
constexpr auto modify(iterator which, Action action) noexcept {
|
||||
auto result = action(which.wrapped());
|
||||
for (auto i = which + 1, e = end(); i != e; ++i) {
|
||||
if (compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
constexpr void merge(Iterator first, Iterator last) noexcept {
|
||||
impl().insert(impl().end(), first, last);
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
constexpr void merge(
|
||||
const flat_multi_set<Type, Compare> &other) noexcept {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
|
||||
constexpr void merge(std::initializer_list<Type> list) noexcept {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
|
||||
friend inline constexpr bool operator<(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() < b.impl();
|
||||
}
|
||||
friend inline constexpr bool operator>(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() > b.impl();
|
||||
}
|
||||
friend inline constexpr bool operator<=(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() <= b.impl();
|
||||
}
|
||||
friend inline constexpr bool operator>=(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() >= b.impl();
|
||||
}
|
||||
friend inline constexpr bool operator==(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() == b.impl();
|
||||
}
|
||||
friend inline constexpr bool operator!=(
|
||||
const flat_multi_set &a,
|
||||
const flat_multi_set &b) noexcept {
|
||||
return a.impl() != b.impl();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class flat_set<Type, Compare>;
|
||||
|
||||
struct transparent_compare : Compare {
|
||||
constexpr const Compare &initial() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
!std::is_same_v<std::decay_t<OtherType2>, const_wrap>>>
|
||||
constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const noexcept {
|
||||
return initial()(
|
||||
std::forward<OtherType1>(a),
|
||||
std::forward<OtherType2>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2>
|
||||
constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const noexcept -> std::enable_if_t<
|
||||
std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
std::is_same_v<std::decay_t<OtherType2>, const_wrap>, bool> {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
constexpr auto operator()(
|
||||
const const_wrap &a,
|
||||
OtherType &&b) const noexcept {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
std::forward<OtherType>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
constexpr auto operator()(
|
||||
OtherType &&a,
|
||||
const const_wrap &b) const noexcept {
|
||||
return initial()(
|
||||
std::forward<OtherType>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
|
||||
};
|
||||
struct Data : transparent_compare {
|
||||
template <typename ...Args>
|
||||
constexpr Data(Args &&...args) noexcept
|
||||
: elements(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
impl_t elements;
|
||||
};
|
||||
|
||||
Data _data;
|
||||
|
||||
constexpr const transparent_compare &compare() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
constexpr const impl_t &impl() const noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
constexpr impl_t &impl() noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr typename impl_t::iterator getLowerBound(
|
||||
const OtherType &value) noexcept {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr typename impl_t::const_iterator getLowerBound(
|
||||
const OtherType &value) const noexcept {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr typename impl_t::iterator getUpperBound(
|
||||
const OtherType &value) noexcept {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr typename impl_t::const_iterator getUpperBound(
|
||||
const OtherType &value) const noexcept {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr std::pair<
|
||||
typename impl_t::iterator,
|
||||
typename impl_t::iterator
|
||||
> getEqualRange(const OtherType &value) noexcept {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr std::pair<
|
||||
typename impl_t::const_iterator,
|
||||
typename impl_t::const_iterator
|
||||
> getEqualRange(const OtherType &value) const noexcept {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, typename Compare>
|
||||
class flat_set : private flat_multi_set<Type, Compare> {
|
||||
using parent = flat_multi_set<Type, Compare>;
|
||||
|
||||
public:
|
||||
using iterator = typename parent::iterator;
|
||||
using const_iterator = typename parent::const_iterator;
|
||||
using reverse_iterator = typename parent::reverse_iterator;
|
||||
using const_reverse_iterator = typename parent::const_reverse_iterator;
|
||||
using value_type = typename parent::value_type;
|
||||
using size_type = typename parent::size_type;
|
||||
using difference_type = typename parent::difference_type;
|
||||
using pointer = typename parent::pointer;
|
||||
using reference = typename parent::reference;
|
||||
|
||||
constexpr flat_set() = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category
|
||||
>
|
||||
constexpr flat_set(Iterator first, Iterator last) noexcept
|
||||
: parent(first, last) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
constexpr flat_set(std::initializer_list<Type> iter) noexcept
|
||||
: parent(iter.begin(), iter.end()) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
using parent::parent;
|
||||
using parent::size;
|
||||
using parent::empty;
|
||||
using parent::clear;
|
||||
using parent::reserve;
|
||||
using parent::shrink_to_fit;
|
||||
using parent::begin;
|
||||
using parent::end;
|
||||
using parent::cbegin;
|
||||
using parent::cend;
|
||||
using parent::rbegin;
|
||||
using parent::rend;
|
||||
using parent::crbegin;
|
||||
using parent::crend;
|
||||
using parent::front;
|
||||
using parent::back;
|
||||
using parent::contains;
|
||||
using parent::erase;
|
||||
using parent::lower_bound;
|
||||
using parent::upper_bound;
|
||||
using parent::equal_range;
|
||||
|
||||
constexpr std::pair<iterator, bool> insert(const Type &value) noexcept {
|
||||
if (this->empty() || this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(value);
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return std::make_pair(this->impl().insert(where, value), true);
|
||||
}
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
constexpr std::pair<iterator, bool> insert(Type &&value) noexcept {
|
||||
if (this->empty() || this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return std::make_pair(
|
||||
this->impl().insert(where, std::move(value)),
|
||||
true);
|
||||
}
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr std::pair<iterator, bool> emplace(Args&&... args) noexcept {
|
||||
return this->insert(Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr bool remove(const OtherType &value) noexcept {
|
||||
return this->removeOne(value);
|
||||
}
|
||||
constexpr bool remove(const Type &value) noexcept {
|
||||
return remove<Type>(value);
|
||||
}
|
||||
|
||||
template <typename OtherType>
|
||||
constexpr iterator find(const OtherType &value) noexcept {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
constexpr iterator find(const Type &value) noexcept {
|
||||
return find<Type>(value);
|
||||
}
|
||||
template <typename OtherType>
|
||||
constexpr const_iterator find(const OtherType &value) const noexcept {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
constexpr const_iterator find(const Type &value) const noexcept {
|
||||
return find<Type>(value);
|
||||
}
|
||||
|
||||
template <typename Action>
|
||||
constexpr void modify(iterator which, Action action) noexcept {
|
||||
action(which.wrapped());
|
||||
for (auto i = iterator(which + 1), e = end(); i != e; ++i) {
|
||||
if (this->compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*which, *i)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (this->compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*i, *which)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
constexpr void merge(Iterator first, Iterator last) noexcept {
|
||||
parent::merge(first, last);
|
||||
finalize();
|
||||
}
|
||||
|
||||
constexpr void merge(
|
||||
const flat_multi_set<Type, Compare> &other) noexcept {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
|
||||
constexpr void merge(std::initializer_list<Type> list) noexcept {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
|
||||
friend inline constexpr bool operator<(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) < static_cast<const parent&>(b);
|
||||
}
|
||||
friend inline constexpr bool operator>(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) > static_cast<const parent&>(b);
|
||||
}
|
||||
friend inline constexpr bool operator<=(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) <= static_cast<const parent&>(b);
|
||||
}
|
||||
friend inline constexpr bool operator>=(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) >= static_cast<const parent&>(b);
|
||||
}
|
||||
friend inline constexpr bool operator==(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) == static_cast<const parent&>(b);
|
||||
}
|
||||
friend inline constexpr bool operator!=(
|
||||
const flat_set &a,
|
||||
const flat_set &b) noexcept {
|
||||
return static_cast<const parent&>(a) != static_cast<const parent&>(b);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void finalize() noexcept {
|
||||
this->impl().erase(
|
||||
std::unique(
|
||||
std::begin(this->impl()),
|
||||
std::end(this->impl()),
|
||||
[&](auto &&a, auto &&b) {
|
||||
return !this->compare()(a, b);
|
||||
}
|
||||
),
|
||||
std::end(this->impl()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
83
Telegram/lib_base/base/flat_set_tests.cpp
Normal file
83
Telegram/lib_base/base/flat_set_tests.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "base/flat_set.h"
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
using is_transparent = void;
|
||||
inline bool operator()(const int &a, const int_wrap &b) const {
|
||||
return a < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int &b) const {
|
||||
return a.value < b;
|
||||
}
|
||||
inline bool operator()(const int &a, const int &b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("flat_sets should keep items sorted", "[flat_set]") {
|
||||
|
||||
base::flat_set<int> v;
|
||||
v.insert(0);
|
||||
v.insert(5);
|
||||
v.insert(4);
|
||||
v.insert(2);
|
||||
|
||||
REQUIRE(v.contains(4));
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(*prev < *i);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.insert(3);
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_sets with custom comparators", "[flat_set]") {
|
||||
base::flat_set<int_wrap, int_wrap_comparator> v;
|
||||
v.insert({ 0 });
|
||||
v.insert({ 5 });
|
||||
v.insert({ 4 });
|
||||
v.insert({ 2 });
|
||||
|
||||
REQUIRE(v.find(4) != v.end());
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(prev->value < i->value);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.insert({ 3 });
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
40
Telegram/lib_base/base/functors.h
Normal file
40
Telegram/lib_base/base/functors.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace functors {
|
||||
|
||||
struct abs_helper {
|
||||
template <
|
||||
typename Type,
|
||||
typename = decltype(0 < std::declval<Type>()),
|
||||
typename = decltype(-std::declval<Type>())>
|
||||
constexpr Type operator()(Type value) const {
|
||||
return (0 < value) ? value : (-value);
|
||||
}
|
||||
};
|
||||
constexpr auto abs = abs_helper{};
|
||||
|
||||
constexpr auto add = [](auto value) {
|
||||
return [value](auto other) {
|
||||
return value + other;
|
||||
};
|
||||
};
|
||||
|
||||
struct negate_helper {
|
||||
template <
|
||||
typename Type,
|
||||
typename = decltype(-std::declval<Type>())>
|
||||
constexpr Type operator()(Type value) const {
|
||||
return -value;
|
||||
}
|
||||
};
|
||||
constexpr auto negate = negate_helper{};
|
||||
|
||||
} // namespace functors
|
||||
} // namespace base
|
||||
43
Telegram/lib_base/base/global_shortcuts.h
Normal file
43
Telegram/lib_base/base/global_shortcuts.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
class GlobalShortcutValue {
|
||||
public:
|
||||
[[nodiscard]] virtual QString toDisplayString() = 0;
|
||||
[[nodiscard]] virtual QByteArray serialize() = 0;
|
||||
|
||||
virtual ~GlobalShortcutValue() = default;
|
||||
};
|
||||
|
||||
using GlobalShortcut = std::shared_ptr<GlobalShortcutValue>;
|
||||
|
||||
// Callbacks are called from unspecified thread.
|
||||
class GlobalShortcutManager {
|
||||
public:
|
||||
virtual void startRecording(
|
||||
Fn<void(GlobalShortcut)> progress,
|
||||
Fn<void(GlobalShortcut)> done) = 0;
|
||||
virtual void stopRecording() = 0;
|
||||
virtual void startWatching(
|
||||
GlobalShortcut shortcut,
|
||||
Fn<void(bool pressed)> callback) = 0;
|
||||
virtual void stopWatching(GlobalShortcut shortcut) = 0;
|
||||
|
||||
[[nodiscard]] virtual GlobalShortcut shortcutFromSerialized(
|
||||
QByteArray serialized) = 0;
|
||||
|
||||
virtual ~GlobalShortcutManager() = default;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool GlobalShortcutsAvailable();
|
||||
[[nodiscard]] bool GlobalShortcutsAllowed();
|
||||
[[nodiscard]] std::unique_ptr<GlobalShortcutManager> CreateGlobalShortcutManager();
|
||||
|
||||
} // namespace base
|
||||
302
Telegram/lib_base/base/global_shortcuts_generic.cpp
Normal file
302
Telegram/lib_base/base/global_shortcuts_generic.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/global_shortcuts_generic.h"
|
||||
|
||||
#include "base/platform/base_platform_global_shortcuts.h"
|
||||
#include "base/invoke_queued.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
constexpr auto kShortcutLimit = 4;
|
||||
|
||||
std::mutex GlobalMutex;
|
||||
std::vector<not_null<GlobalShortcutManagerGeneric*>> Managers;
|
||||
|
||||
[[nodiscard]] GlobalShortcut MakeShortcut(
|
||||
std::vector<GlobalShortcutKeyGeneric> descriptors) {
|
||||
return std::make_shared<GlobalShortcutValueGeneric>(
|
||||
std::move(descriptors));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Matches(
|
||||
const std::vector<GlobalShortcutKeyGeneric> &sorted,
|
||||
const flat_set<GlobalShortcutKeyGeneric> &down) {
|
||||
if (sorted.size() > down.size()) {
|
||||
return false;
|
||||
}
|
||||
auto j = begin(down);
|
||||
for (const auto descriptor : sorted) {
|
||||
while (true) {
|
||||
if (*j > descriptor) {
|
||||
return false;
|
||||
} else if (*j < descriptor) {
|
||||
++j;
|
||||
if (j == end(down)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScheduleForAll(GlobalShortcutKeyGeneric descriptor, bool down) {
|
||||
std::unique_lock lock{ GlobalMutex };
|
||||
for (const auto manager : Managers) {
|
||||
manager->schedule(descriptor, down);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<GlobalShortcutManager> CreateGlobalShortcutManager() {
|
||||
return std::make_unique<GlobalShortcutManagerGeneric>();
|
||||
}
|
||||
|
||||
bool GlobalShortcutsAvailable() {
|
||||
return Platform::GlobalShortcuts::Available();
|
||||
}
|
||||
|
||||
bool GlobalShortcutsAllowed() {
|
||||
return Platform::GlobalShortcuts::Allowed();
|
||||
}
|
||||
|
||||
GlobalShortcutValueGeneric::GlobalShortcutValueGeneric(
|
||||
std::vector<GlobalShortcutKeyGeneric> descriptors)
|
||||
: _descriptors(std::move(descriptors)) {
|
||||
Expects(!_descriptors.empty());
|
||||
}
|
||||
|
||||
QString GlobalShortcutValueGeneric::toDisplayString() {
|
||||
auto result = QStringList();
|
||||
result.reserve(_descriptors.size());
|
||||
for (const auto descriptor : _descriptors) {
|
||||
result.push_back(Platform::GlobalShortcuts::KeyName(descriptor));
|
||||
}
|
||||
return result.join(" + ");
|
||||
}
|
||||
|
||||
QByteArray GlobalShortcutValueGeneric::serialize() {
|
||||
static_assert(sizeof(GlobalShortcutKeyGeneric) == sizeof(uint64));
|
||||
|
||||
const auto size = sizeof(GlobalShortcutKeyGeneric) * _descriptors.size();
|
||||
auto result = QByteArray(size, Qt::Uninitialized);
|
||||
memcpy(result.data(), _descriptors.data(), size);
|
||||
return result;
|
||||
}
|
||||
|
||||
GlobalShortcutManagerGeneric::GlobalShortcutManagerGeneric() {
|
||||
std::unique_lock lock{ GlobalMutex };
|
||||
const auto start = Managers.empty();
|
||||
Managers.push_back(this);
|
||||
lock.unlock();
|
||||
|
||||
if (start) {
|
||||
Platform::GlobalShortcuts::Start(ScheduleForAll);
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcutManagerGeneric::~GlobalShortcutManagerGeneric() {
|
||||
std::unique_lock lock{ GlobalMutex };
|
||||
Managers.erase(ranges::remove(Managers, not_null{ this }), end(Managers));
|
||||
const auto stop = Managers.empty();
|
||||
lock.unlock();
|
||||
|
||||
if (stop) {
|
||||
Platform::GlobalShortcuts::Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::startRecording(
|
||||
Fn<void(GlobalShortcut)> progress,
|
||||
Fn<void(GlobalShortcut)> done) {
|
||||
Expects(done != nullptr);
|
||||
|
||||
_recordingDown.clear();
|
||||
_recordingUp.clear();
|
||||
_recording = true;
|
||||
_recordingProgress = std::move(progress);
|
||||
_recordingDone = std::move(done);
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::stopRecording() {
|
||||
_recordingDown.clear();
|
||||
_recordingUp.clear();
|
||||
_recording = false;
|
||||
_recordingDone = nullptr;
|
||||
_recordingProgress = nullptr;
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::startWatching(
|
||||
GlobalShortcut shortcut,
|
||||
Fn<void(bool pressed)> callback) {
|
||||
Expects(shortcut != nullptr);
|
||||
Expects(callback != nullptr);
|
||||
|
||||
const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
|
||||
if (i != end(_watchlist)) {
|
||||
i->callback = std::move(callback);
|
||||
} else {
|
||||
auto sorted = static_cast<GlobalShortcutValueGeneric*>(
|
||||
shortcut.get())->descriptors();
|
||||
std::sort(begin(sorted), end(sorted));
|
||||
_watchlist.push_back(Watch{
|
||||
std::move(shortcut),
|
||||
std::move(sorted),
|
||||
std::move(callback)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::stopWatching(GlobalShortcut shortcut) {
|
||||
const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
|
||||
if (i != end(_watchlist)) {
|
||||
_watchlist.erase(i);
|
||||
}
|
||||
_pressed.erase(ranges::find(_pressed, shortcut), end(_pressed));
|
||||
}
|
||||
|
||||
GlobalShortcut GlobalShortcutManagerGeneric::shortcutFromSerialized(
|
||||
QByteArray serialized) {
|
||||
const auto single = sizeof(GlobalShortcutKeyGeneric);
|
||||
if (serialized.isEmpty() || serialized.size() % single) {
|
||||
return nullptr;
|
||||
}
|
||||
auto count = serialized.size() / single;
|
||||
auto list = std::vector<GlobalShortcutKeyGeneric>(count);
|
||||
memcpy(list.data(), serialized.constData(), serialized.size());
|
||||
return MakeShortcut(std::move(list));
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::schedule(
|
||||
GlobalShortcutKeyGeneric descriptor,
|
||||
bool down) {
|
||||
InvokeQueued(this, [=] { process(descriptor, down); });
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::process(
|
||||
GlobalShortcutKeyGeneric descriptor,
|
||||
bool down) {
|
||||
if (!down) {
|
||||
_down.remove(descriptor);
|
||||
}
|
||||
if (_recording) {
|
||||
processRecording(descriptor, down);
|
||||
return;
|
||||
}
|
||||
auto scheduled = std::vector<Fn<void(bool pressed)>>();
|
||||
if (down) {
|
||||
_down.emplace(descriptor);
|
||||
for (const auto &watch : _watchlist) {
|
||||
if (watch.sorted.size() > _down.size()
|
||||
|| ranges::contains(_pressed, watch.shortcut)) {
|
||||
continue;
|
||||
} else if (Matches(watch.sorted, _down)) {
|
||||
_pressed.push_back(watch.shortcut);
|
||||
scheduled.push_back(watch.callback);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_down.remove(descriptor);
|
||||
for (auto i = begin(_pressed); i != end(_pressed);) {
|
||||
const auto generic = static_cast<GlobalShortcutValueGeneric*>(
|
||||
i->get());
|
||||
if (!ranges::contains(generic->descriptors(), descriptor)) {
|
||||
++i;
|
||||
} else {
|
||||
const auto j = ranges::find(
|
||||
_watchlist,
|
||||
*i,
|
||||
&Watch::shortcut);
|
||||
Assert(j != end(_watchlist));
|
||||
scheduled.push_back(j->callback);
|
||||
|
||||
i = _pressed.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &callback : scheduled) {
|
||||
callback(down);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::processRecording(
|
||||
GlobalShortcutKeyGeneric descriptor,
|
||||
bool down) {
|
||||
if (down) {
|
||||
processRecordingPress(descriptor);
|
||||
} else {
|
||||
processRecordingRelease(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::processRecordingPress(
|
||||
GlobalShortcutKeyGeneric descriptor) {
|
||||
auto changed = false;
|
||||
_recordingUp.remove(descriptor);
|
||||
for (const auto descriptor : _recordingUp) {
|
||||
const auto i = ranges::remove(_recordingDown, descriptor);
|
||||
if (i != end(_recordingDown)) {
|
||||
_recordingDown.erase(i, end(_recordingDown));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
_recordingUp.clear();
|
||||
|
||||
const auto i = std::find(
|
||||
begin(_recordingDown),
|
||||
end(_recordingDown),
|
||||
descriptor);
|
||||
if (i == _recordingDown.end()) {
|
||||
_recordingDown.push_back(descriptor);
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) {
|
||||
return;
|
||||
} else if (_recordingDown.size() == kShortcutLimit) {
|
||||
finishRecording();
|
||||
} else if (const auto onstack = _recordingProgress) {
|
||||
onstack(MakeShortcut(_recordingDown));
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::processRecordingRelease(
|
||||
GlobalShortcutKeyGeneric descriptor) {
|
||||
const auto i = std::find(
|
||||
begin(_recordingDown),
|
||||
end(_recordingDown),
|
||||
descriptor);
|
||||
if (i == end(_recordingDown)) {
|
||||
return;
|
||||
}
|
||||
_recordingUp.emplace(descriptor);
|
||||
Assert(_recordingUp.size() <= _recordingDown.size());
|
||||
if (_recordingUp.size() == _recordingDown.size()) {
|
||||
// All keys are up, we got the shortcut.
|
||||
// Some down keys are not up yet.
|
||||
finishRecording();
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutManagerGeneric::finishRecording() {
|
||||
Expects(!_recordingDown.empty());
|
||||
|
||||
auto result = MakeShortcut(std::move(_recordingDown));
|
||||
_recordingDown.clear();
|
||||
_recordingUp.clear();
|
||||
_recording = false;
|
||||
const auto done = _recordingDone;
|
||||
_recordingDone = nullptr;
|
||||
_recordingProgress = nullptr;
|
||||
|
||||
done(std::move(result));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
77
Telegram/lib_base/base/global_shortcuts_generic.h
Normal file
77
Telegram/lib_base/base/global_shortcuts_generic.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/global_shortcuts.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
using GlobalShortcutKeyGeneric = uint64;
|
||||
|
||||
class GlobalShortcutValueGeneric final : public GlobalShortcutValue {
|
||||
public:
|
||||
GlobalShortcutValueGeneric(
|
||||
std::vector<GlobalShortcutKeyGeneric> descriptors);
|
||||
|
||||
QString toDisplayString() override;
|
||||
QByteArray serialize() override;
|
||||
|
||||
const std::vector<GlobalShortcutKeyGeneric> &descriptors() const {
|
||||
return _descriptors;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<GlobalShortcutKeyGeneric> _descriptors;
|
||||
|
||||
};
|
||||
|
||||
class GlobalShortcutManagerGeneric final
|
||||
: public GlobalShortcutManager
|
||||
, public QObject {
|
||||
public:
|
||||
GlobalShortcutManagerGeneric();
|
||||
~GlobalShortcutManagerGeneric();
|
||||
|
||||
void startRecording(
|
||||
Fn<void(GlobalShortcut)> progress,
|
||||
Fn<void(GlobalShortcut)> done) override;
|
||||
void stopRecording() override;
|
||||
void startWatching(
|
||||
GlobalShortcut shortcut,
|
||||
Fn<void(bool pressed)> callback) override;
|
||||
void stopWatching(GlobalShortcut shortcut) override;
|
||||
|
||||
GlobalShortcut shortcutFromSerialized(QByteArray serialized) override;
|
||||
|
||||
// Thread-safe.
|
||||
void schedule(GlobalShortcutKeyGeneric descriptor, bool down);
|
||||
|
||||
private:
|
||||
struct Watch {
|
||||
GlobalShortcut shortcut;
|
||||
std::vector<GlobalShortcutKeyGeneric> sorted;
|
||||
Fn<void(bool pressed)> callback;
|
||||
};
|
||||
void process(GlobalShortcutKeyGeneric descriptor, bool down);
|
||||
void processRecording(GlobalShortcutKeyGeneric descriptor, bool down);
|
||||
void processRecordingPress(GlobalShortcutKeyGeneric descriptor);
|
||||
void processRecordingRelease(GlobalShortcutKeyGeneric descriptor);
|
||||
void finishRecording();
|
||||
|
||||
Fn<void(GlobalShortcut)> _recordingProgress;
|
||||
Fn<void(GlobalShortcut)> _recordingDone;
|
||||
std::vector<GlobalShortcutKeyGeneric> _recordingDown;
|
||||
flat_set<GlobalShortcutKeyGeneric> _recordingUp;
|
||||
flat_set<GlobalShortcutKeyGeneric> _down;
|
||||
std::vector<Watch> _watchlist;
|
||||
std::vector<GlobalShortcut> _pressed;
|
||||
|
||||
bool _recording = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
129
Telegram/lib_base/base/index_based_iterator.h
Normal file
129
Telegram/lib_base/base/index_based_iterator.h
Normal file
@@ -0,0 +1,129 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/assertion.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Container>
|
||||
class index_based_iterator {
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
using value_type = typename Container::value_type;
|
||||
using difference_type = typename Container::difference_type;
|
||||
using pointer = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_pointer,
|
||||
typename Container::pointer>;
|
||||
using reference = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_reference,
|
||||
typename Container::reference>;
|
||||
using base_type = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_iterator,
|
||||
typename Container::iterator>;
|
||||
|
||||
index_based_iterator(Container *container, base_type impl)
|
||||
: _container(container)
|
||||
, _index(impl - _container->begin()) {
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *(_container->begin() + _index);
|
||||
}
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
index_based_iterator &operator++() {
|
||||
++_index;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator++(int) {
|
||||
auto copy = *this;
|
||||
++*this;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator--() {
|
||||
--_index;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator--(int) {
|
||||
auto copy = *this;
|
||||
--*this;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator+=(difference_type offset) {
|
||||
_index += offset;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator+(difference_type offset) const {
|
||||
auto copy = *this;
|
||||
copy += offset;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator-=(difference_type offset) {
|
||||
_index -= offset;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator-(difference_type offset) const {
|
||||
auto copy = *this;
|
||||
copy -= offset;
|
||||
return copy;
|
||||
}
|
||||
difference_type operator-(const index_based_iterator &other) const {
|
||||
return _index - other._index;
|
||||
}
|
||||
reference operator[](difference_type offset) const {
|
||||
return *(*this + offset);
|
||||
}
|
||||
|
||||
bool operator==(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index == other._index;
|
||||
}
|
||||
bool operator!=(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index != other._index;
|
||||
}
|
||||
bool operator<(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index < other._index;
|
||||
}
|
||||
bool operator>(const index_based_iterator &other) const {
|
||||
return other < *this;
|
||||
}
|
||||
bool operator<=(const index_based_iterator &other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
bool operator>=(const index_based_iterator &other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
base_type base() const {
|
||||
return _container->begin() + _index;
|
||||
}
|
||||
|
||||
private:
|
||||
Container *_container = nullptr;
|
||||
difference_type _index = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
index_based_iterator<Container> index_based_begin(Container &container) {
|
||||
return { &container, std::begin(container) };
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
index_based_iterator<Container> index_based_end(Container &container) {
|
||||
return { &container, std::end(container) };
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
73
Telegram/lib_base/base/integration.cpp
Normal file
73
Telegram/lib_base/base/integration.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/assertion.h"
|
||||
|
||||
#include "base/integration.h"
|
||||
#include "base/platform/base_platform_file_utilities.h"
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
Integration *IntegrationInstance = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
void Integration::Set(not_null<Integration*> instance) {
|
||||
IntegrationInstance = instance;
|
||||
}
|
||||
|
||||
Integration &Integration::Instance() {
|
||||
Expects(IntegrationInstance != nullptr);
|
||||
|
||||
return *IntegrationInstance;
|
||||
}
|
||||
|
||||
bool Integration::Exists() {
|
||||
return (IntegrationInstance != nullptr);
|
||||
}
|
||||
|
||||
Integration::Integration(int argc, char *argv[]) {
|
||||
const auto path = Platform::CurrentExecutablePath(argc, argv);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto info = QFileInfo(path);
|
||||
if (!info.exists()) {
|
||||
return;
|
||||
}
|
||||
info = QFileInfo(info.canonicalFilePath());
|
||||
const auto dir = info.path();
|
||||
_executableDir = dir.endsWith('/') ? dir : (dir + '/');
|
||||
_executableName = info.fileName();
|
||||
}
|
||||
|
||||
void Integration::logAssertionViolation(const QString &info) {
|
||||
logMessage("Assertion Failed! " + info);
|
||||
}
|
||||
|
||||
void Integration::setCrashAnnotation(
|
||||
const std::string &key,
|
||||
const QString &value) {
|
||||
}
|
||||
|
||||
QString Integration::executableDir() const {
|
||||
return _executableDir;
|
||||
}
|
||||
|
||||
QString Integration::executableName() const {
|
||||
return _executableName;
|
||||
}
|
||||
|
||||
QString Integration::executablePath() const {
|
||||
return _executableDir + _executableName;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
43
Telegram/lib_base/base/integration.h
Normal file
43
Telegram/lib_base/base/integration.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class Integration {
|
||||
public:
|
||||
static void Set(not_null<Integration*> instance);
|
||||
static Integration &Instance();
|
||||
static bool Exists();
|
||||
|
||||
Integration(int argc, char *argv[]);
|
||||
|
||||
virtual void enterFromEventLoop(FnMut<void()> &&method) = 0;
|
||||
|
||||
virtual bool logSkipDebug() = 0;
|
||||
virtual void logMessageDebug(const QString &message) = 0;
|
||||
virtual void logMessage(const QString &message) = 0;
|
||||
virtual void logAssertionViolation(const QString &info);
|
||||
virtual void setCrashAnnotation(
|
||||
const std::string &key,
|
||||
const QString &value);
|
||||
|
||||
[[nodiscard]] QString executableDir() const;
|
||||
[[nodiscard]] QString executableName() const;
|
||||
[[nodiscard]] QString executablePath() const;
|
||||
|
||||
virtual ~Integration() = default;
|
||||
|
||||
private:
|
||||
QString _executableDir;
|
||||
QString _executableName;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
64
Telegram/lib_base/base/invoke_queued.h
Normal file
64
Telegram/lib_base/base/invoke_queued.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class InvokeQueuedEvent : public QEvent {
|
||||
public:
|
||||
static auto Type() {
|
||||
static const auto Result = QEvent::Type(QEvent::registerEventType());
|
||||
return Result;
|
||||
}
|
||||
|
||||
explicit InvokeQueuedEvent(FnMut<void()> &&method)
|
||||
: QEvent(Type())
|
||||
, _method(std::move(method)) {
|
||||
}
|
||||
|
||||
void invoke() {
|
||||
_method();
|
||||
}
|
||||
|
||||
private:
|
||||
FnMut<void()> _method;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Lambda>
|
||||
inline void InvokeQueued(const QObject *context, Lambda &&lambda) {
|
||||
QCoreApplication::postEvent(
|
||||
const_cast<QObject*>(context),
|
||||
new base::InvokeQueuedEvent(std::forward<Lambda>(lambda)));
|
||||
}
|
||||
|
||||
class SingleQueuedInvokation : public QObject {
|
||||
public:
|
||||
SingleQueuedInvokation(Fn<void()> callback) : _callback(callback) {
|
||||
}
|
||||
void call() {
|
||||
if (_pending.testAndSetAcquire(0, 1)) {
|
||||
InvokeQueued(this, [this] {
|
||||
if (_pending.testAndSetRelease(1, 0)) {
|
||||
_callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Fn<void()> _callback;
|
||||
QAtomicInt _pending = { 0 };
|
||||
|
||||
};
|
||||
68
Telegram/lib_base/base/last_used_cache.h
Normal file
68
Telegram/lib_base/base/last_used_cache.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Entry>
|
||||
class last_used_cache {
|
||||
public:
|
||||
void up(Entry entry);
|
||||
void remove(Entry entry);
|
||||
void clear();
|
||||
|
||||
Entry take_lowest();
|
||||
|
||||
private:
|
||||
std::list<Entry> _queue;
|
||||
std::unordered_map<Entry, typename std::list<Entry>::iterator> _map;
|
||||
|
||||
};
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::up(Entry entry) {
|
||||
if (!_queue.empty() && _queue.back() == entry) {
|
||||
return;
|
||||
}
|
||||
const auto i = _map.find(entry);
|
||||
if (i != end(_map)) {
|
||||
_queue.splice(end(_queue), _queue, i->second);
|
||||
} else {
|
||||
_map.emplace(entry, _queue.insert(end(_queue), entry));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::remove(Entry entry) {
|
||||
const auto i = _map.find(entry);
|
||||
if (i != end(_map)) {
|
||||
_queue.erase(i->second);
|
||||
_map.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::clear() {
|
||||
_queue.clear();
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
Entry last_used_cache<Entry>::take_lowest() {
|
||||
if (_queue.empty()) {
|
||||
return Entry();
|
||||
}
|
||||
auto result = std::move(_queue.front());
|
||||
_queue.erase(begin(_queue));
|
||||
_map.erase(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
49
Telegram/lib_base/base/last_user_input.cpp
Normal file
49
Telegram/lib_base/base/last_user_input.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/last_user_input.h"
|
||||
|
||||
#include "base/event_filter.h"
|
||||
#include "base/platform/base_platform_last_input.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace base {
|
||||
|
||||
crl::time LastUserInputTime() {
|
||||
Expects(QCoreApplication::instance() != nullptr);
|
||||
|
||||
if (const auto specific = base::Platform::LastUserInputTime()) {
|
||||
return *specific;
|
||||
}
|
||||
static auto result = crl::time(0);
|
||||
static const auto isInputEvent = [](not_null<QEvent*> e) {
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::Wheel: return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static const auto updateResult = [](not_null<QEvent*> e) {
|
||||
if (isInputEvent(e)) {
|
||||
result = crl::now();
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
};
|
||||
[[maybe_unused]] static const auto watcher = base::install_event_filter(
|
||||
QCoreApplication::instance(),
|
||||
updateResult);
|
||||
return result;
|
||||
}
|
||||
|
||||
crl::time SinceLastUserInput() {
|
||||
return crl::now() - LastUserInputTime();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
14
Telegram/lib_base/base/last_user_input.h
Normal file
14
Telegram/lib_base/base/last_user_input.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
[[nodiscard]] crl::time LastUserInputTime();
|
||||
[[nodiscard]] crl::time SinceLastUserInput();
|
||||
|
||||
} // namespace base
|
||||
51
Telegram/lib_base/base/match_method.h
Normal file
51
Telegram/lib_base/base/match_method.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <rpl/details/callable.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Data, typename Method, typename ...Methods>
|
||||
inline decltype(auto) match_method(
|
||||
Data &&data,
|
||||
Method &&method,
|
||||
Methods &&...methods) {
|
||||
using namespace rpl::details;
|
||||
if constexpr (is_callable_plain_v<Method, Data&&>) {
|
||||
return std::forward<Method>(method)(std::forward<Data>(data));
|
||||
} else {
|
||||
return match_method(
|
||||
std::forward<Data>(data),
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Data1,
|
||||
typename Data2,
|
||||
typename Method,
|
||||
typename ...Methods>
|
||||
inline decltype(auto) match_method2(
|
||||
Data1 &&data1,
|
||||
Data2 &&data2,
|
||||
Method &&method,
|
||||
Methods &&...methods) {
|
||||
using namespace rpl::details;
|
||||
if constexpr (is_callable_plain_v<Method, Data1&&, Data2&&>) {
|
||||
return std::forward<Method>(method)(
|
||||
std::forward<Data1>(data1),
|
||||
std::forward<Data2>(data2));
|
||||
} else {
|
||||
return match_method2(
|
||||
std::forward<Data1>(data1),
|
||||
std::forward<Data2>(data2),
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
112
Telegram/lib_base/base/network_reachability.cpp
Normal file
112
Telegram/lib_base/base/network_reachability.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/network_reachability.h"
|
||||
|
||||
#include "base/platform/base_platform_network_reachability.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
|
||||
#include <QtNetwork/QNetworkInformation>
|
||||
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Qt >= 6.2.0
|
||||
#include <QtNetwork/QNetworkConfigurationManager>
|
||||
#endif // Qt >= 6.2.0 || Qt < 6.0.0
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
std::weak_ptr<NetworkReachability> GlobalNetworkReachability;
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined __clang__ // __GNUC__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined _MSC_VER // __GNUC__ || __clang__
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996)
|
||||
#endif // __GNUC__ || __clang__ || _MSC_VER
|
||||
|
||||
struct NetworkReachability::Private {
|
||||
Private() : platformHelper(Platform::NetworkReachability::Create()) {
|
||||
}
|
||||
|
||||
rpl::variable<bool> available = true;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QNetworkConfigurationManager configurationManager;
|
||||
#endif // Qt < 6.0.0
|
||||
std::unique_ptr<Platform::NetworkReachability> platformHelper;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
|
||||
NetworkReachability::NetworkReachability()
|
||||
: _private(std::make_unique<Private>()) {
|
||||
if (_private->platformHelper) {
|
||||
_private->platformHelper->availableValue(
|
||||
) | rpl::on_next([=](bool available) {
|
||||
_private->available = available;
|
||||
}, _private->lifetime);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
|
||||
} else if (QNetworkInformation::load(
|
||||
QNetworkInformation::Feature::Reachability)) {
|
||||
_private->available = QNetworkInformation::instance()->reachability()
|
||||
== QNetworkInformation::Reachability::Online;
|
||||
base::qt_signal_producer(
|
||||
QNetworkInformation::instance(),
|
||||
&QNetworkInformation::reachabilityChanged
|
||||
) | rpl::on_next([=](
|
||||
QNetworkInformation::Reachability newReachability) {
|
||||
_private->available = newReachability
|
||||
== QNetworkInformation::Reachability::Online;
|
||||
}, _private->lifetime);
|
||||
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Qt >= 6.2.0
|
||||
} else {
|
||||
_private->available = _private->configurationManager.isOnline();
|
||||
QObject::connect(
|
||||
&_private->configurationManager,
|
||||
&QNetworkConfigurationManager::onlineStateChanged,
|
||||
[=](bool isOnline) {
|
||||
_private->available = isOnline;
|
||||
});
|
||||
#endif // Qt >= 6.2.0 || Qt < 6.0.0
|
||||
}
|
||||
}
|
||||
|
||||
NetworkReachability::~NetworkReachability() {
|
||||
}
|
||||
|
||||
std::shared_ptr<NetworkReachability> NetworkReachability::Instance() {
|
||||
auto result = GlobalNetworkReachability.lock();
|
||||
if (!result) {
|
||||
GlobalNetworkReachability = result = std::make_shared<NetworkReachability>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NetworkReachability::available() const {
|
||||
return _private->available.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> NetworkReachability::availableChanges() const {
|
||||
return _private->available.changes();
|
||||
}
|
||||
|
||||
rpl::producer<bool> NetworkReachability::availableValue() const {
|
||||
return _private->available.value();
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#elif defined __clang__ // __GNUC__
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined _MSC_VER // __GNUC__ || __clang__
|
||||
#pragma warning(pop)
|
||||
#endif // __GNUC__ || __clang__ || _MSC_VER
|
||||
|
||||
} // namespace base
|
||||
27
Telegram/lib_base/base/network_reachability.h
Normal file
27
Telegram/lib_base/base/network_reachability.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
class NetworkReachability final {
|
||||
public:
|
||||
NetworkReachability();
|
||||
~NetworkReachability();
|
||||
|
||||
[[nodiscard]] static std::shared_ptr<NetworkReachability> Instance();
|
||||
|
||||
[[nodiscard]] bool available() const;
|
||||
[[nodiscard]] rpl::producer<bool> availableChanges() const;
|
||||
[[nodiscard]] rpl::producer<bool> availableValue() const;
|
||||
|
||||
private:
|
||||
struct Private;
|
||||
const std::unique_ptr<Private> _private;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
62
Telegram/lib_base/base/never_freed_pointer.h
Normal file
62
Telegram/lib_base/base/never_freed_pointer.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/assertion.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// This pointer is used for global non-POD variables that are allocated
|
||||
// on demand by createIfNull(lambda) and are never automatically freed.
|
||||
template <typename T>
|
||||
class NeverFreedPointer {
|
||||
public:
|
||||
NeverFreedPointer() = default;
|
||||
NeverFreedPointer(const NeverFreedPointer<T> &other) = delete;
|
||||
NeverFreedPointer &operator=(const NeverFreedPointer<T> &other) = delete;
|
||||
|
||||
template <typename... Args>
|
||||
void createIfNull(Args&&... args) {
|
||||
if (isNull()) {
|
||||
reset(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
};
|
||||
|
||||
T *data() const {
|
||||
return _p;
|
||||
}
|
||||
T *release() {
|
||||
return base::take(_p);
|
||||
}
|
||||
void reset(T *p = nullptr) {
|
||||
delete _p;
|
||||
_p = p;
|
||||
}
|
||||
bool isNull() const {
|
||||
return data() == nullptr;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
reset();
|
||||
}
|
||||
T *operator->() const {
|
||||
return data();
|
||||
}
|
||||
T &operator*() const {
|
||||
Assert(!isNull());
|
||||
return *data();
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return !isNull();
|
||||
}
|
||||
|
||||
private:
|
||||
T *_p;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
124
Telegram/lib_base/base/object_ptr.h
Normal file
124
Telegram/lib_base/base/object_ptr.h
Normal file
@@ -0,0 +1,124 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
// Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent.
|
||||
template <typename Object>
|
||||
class object_ptr {
|
||||
public:
|
||||
object_ptr(std::nullptr_t) noexcept {
|
||||
}
|
||||
|
||||
// No default constructor, but constructors with at least
|
||||
// one argument are simply make functions.
|
||||
template <typename Parent, typename... Args>
|
||||
explicit object_ptr(Parent &&parent, Args&&... args)
|
||||
: _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
|
||||
}
|
||||
static object_ptr<Object> fromRaw(Object *value) noexcept {
|
||||
object_ptr<Object> result = { nullptr };
|
||||
result._object = value;
|
||||
return result;
|
||||
}
|
||||
Object *release() noexcept {
|
||||
return static_cast<Object*>(base::take(_object).data());
|
||||
}
|
||||
|
||||
object_ptr(const object_ptr &other) = delete;
|
||||
object_ptr &operator=(const object_ptr &other) = delete;
|
||||
object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) {
|
||||
}
|
||||
object_ptr &operator=(object_ptr &&other) noexcept {
|
||||
auto temp = std::move(other);
|
||||
destroy();
|
||||
std::swap(_object, temp._object);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherObject,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Object, OtherObject>>>
|
||||
object_ptr(object_ptr<OtherObject> &&other) noexcept
|
||||
: _object(base::take(other._object)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherObject,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Object, OtherObject>>>
|
||||
object_ptr &operator=(object_ptr<OtherObject> &&other) noexcept {
|
||||
_object = base::take(other._object);
|
||||
return *this;
|
||||
}
|
||||
|
||||
object_ptr &operator=(std::nullptr_t) noexcept {
|
||||
_object = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Object *get() const noexcept {
|
||||
return static_cast<Object*>(_object.data());
|
||||
}
|
||||
|
||||
// So we can pass this pointer to methods like connect().
|
||||
Object *data() const noexcept {
|
||||
return get();
|
||||
}
|
||||
operator Object*() const noexcept {
|
||||
return get();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return _object.data() != nullptr;
|
||||
}
|
||||
|
||||
Object *operator->() const noexcept {
|
||||
return get();
|
||||
}
|
||||
Object &operator*() const noexcept {
|
||||
return *get();
|
||||
}
|
||||
|
||||
// Use that instead "= new Object(parent, ...)"
|
||||
template <typename Parent, typename... Args>
|
||||
Object *create(Parent &&parent, Args&&... args) {
|
||||
destroy();
|
||||
_object = new Object(
|
||||
std::forward<Parent>(parent),
|
||||
std::forward<Args>(args)...);
|
||||
return get();
|
||||
}
|
||||
void destroy() noexcept {
|
||||
delete base::take(_object);
|
||||
}
|
||||
void destroyDelayed() {
|
||||
if (_object) {
|
||||
if (auto widget = base::up_cast<QWidget*>(get())) {
|
||||
widget->hide();
|
||||
}
|
||||
base::take(_object)->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
~object_ptr() noexcept {
|
||||
if (auto pointer = _object) {
|
||||
if (!pointer->parent()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OtherObject>
|
||||
friend class object_ptr;
|
||||
|
||||
QPointer<QObject> _object;
|
||||
|
||||
};
|
||||
629
Telegram/lib_base/base/openssl_help.h
Normal file
629
Telegram/lib_base/base/openssl_help.h
Normal file
@@ -0,0 +1,629 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/bytes.h"
|
||||
#include "base/algorithm.h"
|
||||
#include "base/assertion.h"
|
||||
#include "base/basic_types.h"
|
||||
|
||||
extern "C" {
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/modes.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace openssl {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context() : _data(BN_CTX_new()) {
|
||||
}
|
||||
Context(const Context &other) = delete;
|
||||
Context(Context &&other) : _data(base::take(other._data)) {
|
||||
}
|
||||
Context &operator=(const Context &other) = delete;
|
||||
Context &operator=(Context &&other) {
|
||||
_data = base::take(other._data);
|
||||
return *this;
|
||||
}
|
||||
~Context() {
|
||||
if (_data) {
|
||||
BN_CTX_free(_data);
|
||||
}
|
||||
}
|
||||
|
||||
BN_CTX *raw() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
BN_CTX *_data = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class BigNum {
|
||||
public:
|
||||
BigNum() = default;
|
||||
BigNum(const BigNum &other)
|
||||
: _data((other.failed() || other.isZero())
|
||||
? nullptr
|
||||
: BN_dup(other.raw()))
|
||||
, _failed(other._failed) {
|
||||
}
|
||||
BigNum(BigNum &&other)
|
||||
: _data(std::exchange(other._data, nullptr))
|
||||
, _failed(std::exchange(other._failed, false)) {
|
||||
}
|
||||
BigNum &operator=(const BigNum &other) {
|
||||
if (other.failed()) {
|
||||
_failed = true;
|
||||
} else if (other.isZero()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else if (!_data) {
|
||||
_data = BN_dup(other.raw());
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_copy(raw(), other.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &operator=(BigNum &&other) {
|
||||
std::swap(_data, other._data);
|
||||
std::swap(_failed, other._failed);
|
||||
return *this;
|
||||
}
|
||||
~BigNum() {
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit BigNum(unsigned int word) : BigNum() {
|
||||
setWord(word);
|
||||
}
|
||||
explicit BigNum(bytes::const_span bytes) : BigNum() {
|
||||
setBytes(bytes);
|
||||
}
|
||||
|
||||
BigNum &setWord(unsigned int word) {
|
||||
if (!word) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_set_word(raw(), word);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setBytes(bytes::const_span bytes) {
|
||||
if (bytes.empty()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_bin2bn(
|
||||
reinterpret_cast<const unsigned char*>(bytes.data()),
|
||||
bytes.size(),
|
||||
raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNum &setAdd(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_add(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setSub(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_sub(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_sub(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_mul(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (base.failed() || power.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (base.isNegative() || power.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_exp(raw(), base.raw(), power.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setGcd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_gcd(raw(), a.raw(), b.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isZero() const {
|
||||
return !failed() && (!_data || BN_is_zero(raw()));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isOne() const {
|
||||
return !failed() && _data && BN_is_one(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNegative() const {
|
||||
return !failed() && _data && BN_is_negative(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
|
||||
if (failed() || !_data) {
|
||||
return false;
|
||||
}
|
||||
constexpr auto kMillerRabinIterationCount = 64;
|
||||
const auto result = BN_is_prime_ex(
|
||||
raw(),
|
||||
kMillerRabinIterationCount,
|
||||
context.raw(),
|
||||
nullptr);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
} else if (result != 0) {
|
||||
_failed = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BigNum &subWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return *this;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
|
||||
Expects(word != 0);
|
||||
|
||||
const auto result = failed()
|
||||
? (BN_ULONG)-1
|
||||
: BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
_failed = true;
|
||||
}
|
||||
if (mod) {
|
||||
*mod = result;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
|
||||
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
|
||||
}
|
||||
|
||||
[[nodiscard]] int bitsSize() const {
|
||||
return failed() ? 0 : BN_num_bits(raw());
|
||||
}
|
||||
[[nodiscard]] int bytesSize() const {
|
||||
return failed() ? 0 : BN_num_bytes(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bytes::vector getBytes() const {
|
||||
if (failed()) {
|
||||
return {};
|
||||
}
|
||||
auto length = BN_num_bytes(raw());
|
||||
auto result = bytes::vector(length);
|
||||
auto resultSize = BN_bn2bin(
|
||||
raw(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
Assert(resultSize == length);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] BIGNUM *raw() {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
[[nodiscard]] const BIGNUM *raw() const {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
[[nodiscard]] BIGNUM *takeRaw() {
|
||||
return _failed
|
||||
? nullptr
|
||||
: _data
|
||||
? std::exchange(_data, nullptr)
|
||||
: BN_new();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool failed() const {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setAdd(a, b);
|
||||
}
|
||||
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setSub(a, b);
|
||||
}
|
||||
[[nodiscard]] static BigNum Mul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setMul(a, b, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModAdd(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModSub(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModMul(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModInverse(a, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModExp(base, power, mod, context);
|
||||
}
|
||||
[[nodiscard]] static int Compare(const BigNum &a, const BigNum &b) {
|
||||
return a.failed() ? -1 : b.failed() ? 1 : BN_cmp(a.raw(), b.raw());
|
||||
}
|
||||
static void Div(
|
||||
BigNum *dv,
|
||||
BigNum *rem,
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (!dv && !rem) {
|
||||
return;
|
||||
} else if (a.failed()
|
||||
|| b.failed()
|
||||
|| !BN_div(
|
||||
dv ? dv->raw() : nullptr,
|
||||
rem ? rem->raw() : nullptr,
|
||||
a.raw(),
|
||||
b.raw(),
|
||||
context.raw())) {
|
||||
if (dv) {
|
||||
dv->_failed = true;
|
||||
}
|
||||
if (rem) {
|
||||
rem->_failed = true;
|
||||
}
|
||||
} else {
|
||||
if (dv) {
|
||||
dv->_failed = false;
|
||||
}
|
||||
if (rem) {
|
||||
rem->_failed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
[[nodiscard]] static BigNum Failed() {
|
||||
auto result = BigNum();
|
||||
result._failed = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void clear() {
|
||||
BN_clear_free(std::exchange(_data, nullptr));
|
||||
}
|
||||
|
||||
mutable BIGNUM *_data = nullptr;
|
||||
mutable bool _failed = false;
|
||||
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Context, typename Method, typename Arg>
|
||||
inline void ShaUpdate(Context context, Method method, Arg &&arg) {
|
||||
const auto span = bytes::make_span(arg);
|
||||
method(context, span.data(), span.size());
|
||||
}
|
||||
|
||||
template <typename Context, typename Method, typename Arg, typename ...Args>
|
||||
inline void ShaUpdate(Context context, Method method, Arg &&arg, Args &&...args) {
|
||||
const auto span = bytes::make_span(arg);
|
||||
method(context, span.data(), span.size());
|
||||
ShaUpdate(context, method, args...);
|
||||
}
|
||||
|
||||
template <size_type Size, typename Method>
|
||||
inline void Sha(
|
||||
bytes::span dst,
|
||||
Method method,
|
||||
bytes::const_span data) {
|
||||
Expects(dst.size() >= Size);
|
||||
|
||||
method(
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(dst.data()));
|
||||
}
|
||||
|
||||
template <size_type Size, typename Method>
|
||||
[[nodiscard]] inline bytes::vector Sha(
|
||||
Method method,
|
||||
bytes::const_span data) {
|
||||
auto bytes = bytes::vector(Size);
|
||||
Sha<Size>(bytes, method, data);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
template <
|
||||
size_type Size,
|
||||
typename Context,
|
||||
typename Init,
|
||||
typename Update,
|
||||
typename Finalize,
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
[[nodiscard]] bytes::vector Sha(
|
||||
Context context,
|
||||
Init init,
|
||||
Update update,
|
||||
Finalize finalize,
|
||||
Args &&...args) {
|
||||
auto bytes = bytes::vector(Size);
|
||||
|
||||
init(&context);
|
||||
ShaUpdate(&context, update, args...);
|
||||
finalize(reinterpret_cast<unsigned char*>(bytes.data()), &context);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
template <
|
||||
size_type Size,
|
||||
typename Evp>
|
||||
[[nodiscard]] bytes::vector Pbkdf2(
|
||||
bytes::const_span password,
|
||||
bytes::const_span salt,
|
||||
int iterations,
|
||||
Evp evp) {
|
||||
auto result = bytes::vector(Size);
|
||||
PKCS5_PBKDF2_HMAC(
|
||||
reinterpret_cast<const char*>(password.data()),
|
||||
password.size(),
|
||||
reinterpret_cast<const unsigned char*>(salt.data()),
|
||||
salt.size(),
|
||||
iterations,
|
||||
evp,
|
||||
result.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
constexpr auto kSha1Size = size_type(SHA_DIGEST_LENGTH);
|
||||
constexpr auto kSha256Size = size_type(SHA256_DIGEST_LENGTH);
|
||||
constexpr auto kSha512Size = size_type(SHA512_DIGEST_LENGTH);
|
||||
|
||||
[[nodiscard]] inline bytes::vector Sha1(bytes::const_span data) {
|
||||
return details::Sha<kSha1Size>(SHA1, data);
|
||||
}
|
||||
|
||||
inline void Sha1To(bytes::span dst, bytes::const_span data) {
|
||||
details::Sha<kSha1Size>(dst, SHA1, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
[[nodiscard]] inline bytes::vector Sha1(Args &&...args) {
|
||||
return details::Sha<kSha1Size>(
|
||||
SHA_CTX(),
|
||||
SHA1_Init,
|
||||
SHA1_Update,
|
||||
SHA1_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bytes::vector Sha256(bytes::const_span data) {
|
||||
return details::Sha<kSha256Size>(SHA256, data);
|
||||
}
|
||||
|
||||
inline void Sha256To(bytes::span dst, bytes::const_span data) {
|
||||
details::Sha<kSha256Size>(dst, SHA256, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
[[nodiscard]] inline bytes::vector Sha256(Args &&...args) {
|
||||
return details::Sha<kSha256Size>(
|
||||
SHA256_CTX(),
|
||||
SHA256_Init,
|
||||
SHA256_Update,
|
||||
SHA256_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bytes::vector Sha512(bytes::const_span data) {
|
||||
return details::Sha<kSha512Size>(SHA512, data);
|
||||
}
|
||||
|
||||
inline void Sha512To(bytes::span dst, bytes::const_span data) {
|
||||
details::Sha<kSha512Size>(dst, SHA512, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
[[nodiscard]] inline bytes::vector Sha512(Args &&...args) {
|
||||
return details::Sha<kSha512Size>(
|
||||
SHA512_CTX(),
|
||||
SHA512_Init,
|
||||
SHA512_Update,
|
||||
SHA512_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
inline bytes::vector Pbkdf2Sha512(
|
||||
bytes::const_span password,
|
||||
bytes::const_span salt,
|
||||
int iterations) {
|
||||
return details::Pbkdf2<kSha512Size>(
|
||||
password,
|
||||
salt,
|
||||
iterations,
|
||||
EVP_sha512());
|
||||
}
|
||||
|
||||
inline bytes::vector HmacSha256(
|
||||
bytes::const_span key,
|
||||
bytes::const_span data) {
|
||||
auto result = bytes::vector(kSha256Size);
|
||||
auto length = (unsigned int)kSha256Size;
|
||||
|
||||
HMAC(
|
||||
EVP_sha256(),
|
||||
key.data(),
|
||||
key.size(),
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()),
|
||||
&length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace openssl
|
||||
57
Telegram/lib_base/base/optional.h
Normal file
57
Telegram/lib_base/base/optional.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Type>
|
||||
struct optional_wrap_once {
|
||||
using type = std::optional<Type>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct optional_wrap_once<std::optional<Type>> {
|
||||
using type = std::optional<Type>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using optional_wrap_once_t = typename optional_wrap_once<std::decay_t<Type>>::type;
|
||||
|
||||
template <typename Type>
|
||||
struct optional_chain_result {
|
||||
using type = optional_wrap_once_t<Type>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct optional_chain_result<void> {
|
||||
using type = bool;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using optional_chain_result_t = typename optional_chain_result<Type>::type;
|
||||
|
||||
template <typename Type>
|
||||
optional_wrap_once_t<Type> make_optional(Type &&value) {
|
||||
return optional_wrap_once_t<Type> { std::forward<Type>(value) };
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Type, typename Method>
|
||||
inline auto operator|(const std::optional<Type> &value, Method method)
|
||||
-> base::optional_chain_result_t<decltype(method(*value))> {
|
||||
if constexpr (std::is_same_v<decltype(method(*value)), void>) {
|
||||
return value ? (method(*value), true) : false;
|
||||
} else {
|
||||
return value
|
||||
? base::optional_chain_result_t<decltype(method(*value))>(
|
||||
method(*value))
|
||||
: std::nullopt;
|
||||
}
|
||||
}
|
||||
245
Telegram/lib_base/base/options.cpp
Normal file
245
Telegram/lib_base/base/options.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/options.h"
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/variant.h"
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
namespace base::options {
|
||||
namespace details {
|
||||
namespace {
|
||||
|
||||
constexpr auto kSaveDelay = crl::time(1000);
|
||||
|
||||
bool WriteScheduled/* = false*/;
|
||||
|
||||
struct Compare {
|
||||
bool operator()(const char *a, const char *b) const noexcept {
|
||||
return strcmp(a, b) < 0;
|
||||
}
|
||||
};
|
||||
using MapType = base::flat_map<const char*, not_null<BasicOption*>, Compare>;
|
||||
|
||||
[[nodiscard]] MapType &Map() {
|
||||
static auto result = MapType();
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString &LocalPath() {
|
||||
static auto result = QString();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Read(const QString &path) {
|
||||
auto file = QFile(path);
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
} else if (!file.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Experimental: Error opening file from '%1'.").arg(path));
|
||||
return;
|
||||
}
|
||||
auto error = QJsonParseError();
|
||||
const auto parsed = QJsonDocument::fromJson(file.readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
LOG(("Experimental: Error parsing json from '%1': %2 (%3)"
|
||||
).arg(path
|
||||
).arg(error.error
|
||||
).arg(error.errorString()));
|
||||
return;
|
||||
} else if (!parsed.isObject()) {
|
||||
LOG(("Experimental: Non object in json from '%1'.").arg(path));
|
||||
return;
|
||||
}
|
||||
auto &map = Map();
|
||||
const auto values = parsed.object();
|
||||
for (auto i = values.begin(); i != values.end(); ++i) {
|
||||
const auto key = i.key().toLatin1() + char(0);
|
||||
const auto j = map.find(key.data());
|
||||
if (j == end(map)) {
|
||||
LOG(("Experimental: Unknown option '%1'.").arg(i.key()));
|
||||
continue;
|
||||
}
|
||||
const auto value = *i;
|
||||
v::match(j->second->value(), [&](const auto ¤t) {
|
||||
using T = std::remove_cvref_t<decltype(current)>;
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
if (value.isBool()) {
|
||||
j->second->set(value.toBool());
|
||||
return;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
if (value.isDouble()) {
|
||||
j->second->set(value.toInt());
|
||||
return;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, QString>) {
|
||||
if (value.isString()) {
|
||||
j->second->set(value.toString());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
static_assert(unsupported_type(T()));
|
||||
}
|
||||
LOG(("Experimental: Wrong option value type for '%1'."
|
||||
).arg(i.key()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Write() {
|
||||
const auto &path = LocalPath();
|
||||
if (!WriteScheduled || path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
WriteScheduled = false;
|
||||
|
||||
auto map = QJsonObject();
|
||||
for (const auto &[name, option] : Map()) {
|
||||
const auto &value = option->value();
|
||||
if (value != option->defaultValue()) {
|
||||
map.insert(name, v::match(value, [](const auto ¤t) {
|
||||
using T = std::remove_cvref_t<decltype(current)>;
|
||||
if constexpr (std::is_same_v<T, bool>
|
||||
|| std::is_same_v<T, int>
|
||||
|| std::is_same_v<T, QString>) {
|
||||
return QJsonValue(current);
|
||||
} else {
|
||||
static_assert(unsupported_type(T()));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (map.isEmpty()) {
|
||||
QFile(path).remove();
|
||||
} else if (auto file = QFile(path); file.open(QIODevice::WriteOnly)) {
|
||||
file.write(QJsonDocument(map).toJson(QJsonDocument::Indented));
|
||||
} else {
|
||||
LOG(("Experimental: Could not write '%1'.").arg(path));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BasicOption::BasicOption(
|
||||
const char id[],
|
||||
const char name[],
|
||||
const char description[],
|
||||
ValueType defaultValue,
|
||||
Scope scope,
|
||||
bool restartRequired)
|
||||
: _value(defaultValue)
|
||||
, _defaultValue(std::move(defaultValue))
|
||||
, _id(QString::fromUtf8(id))
|
||||
, _name(QString::fromUtf8(name))
|
||||
, _description(QString::fromUtf8(description))
|
||||
, _scope(scope)
|
||||
, _restartRequired(restartRequired) {
|
||||
const auto [i, ok] = Map().emplace(id, this);
|
||||
|
||||
Ensures(ok);
|
||||
}
|
||||
|
||||
void BasicOption::set(ValueType value) {
|
||||
Expects(value.index() == _value.index());
|
||||
|
||||
_value = std::move(value);
|
||||
if (!WriteScheduled && !LocalPath().isEmpty()) {
|
||||
WriteScheduled = true;
|
||||
call_delayed(kSaveDelay, [] { Write(); });
|
||||
}
|
||||
}
|
||||
|
||||
const ValueType &BasicOption::value() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
const ValueType &BasicOption::defaultValue() const {
|
||||
return _defaultValue;
|
||||
}
|
||||
|
||||
const QString &BasicOption::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
const QString &BasicOption::name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
const QString &BasicOption::description() const {
|
||||
return _description;
|
||||
}
|
||||
|
||||
bool BasicOption::relevant() const {
|
||||
const auto scopeFn = std::get_if<ScopeFn>(&_scope);
|
||||
if (scopeFn) {
|
||||
return (*scopeFn)();
|
||||
}
|
||||
|
||||
const auto scopeFlags = v::get<ScopeFlags>(_scope);
|
||||
#ifdef Q_OS_WIN
|
||||
return scopeFlags & windows;
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
return scopeFlags & macos;
|
||||
#else // Q_OS_MAC || Q_OS_WIN
|
||||
return scopeFlags & linux;
|
||||
#endif // Q_OS_MAC || Q_OS_WIN
|
||||
}
|
||||
|
||||
bool BasicOption::restartRequired() const {
|
||||
return _restartRequired;
|
||||
}
|
||||
|
||||
Scope BasicOption::scope() const {
|
||||
return _scope;
|
||||
}
|
||||
|
||||
BasicOption &Lookup(const char id[]) {
|
||||
const auto i = Map().find(id);
|
||||
|
||||
Ensures(i != end(Map()));
|
||||
return *i->second;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
bool changed() {
|
||||
for (const auto &[name, option] : details::Map()) {
|
||||
if (option->value() != option->defaultValue()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (const auto &[name, option] : details::Map()) {
|
||||
if (option->value() != option->defaultValue()) {
|
||||
option->set(option->defaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(const QString &path) {
|
||||
Expects(details::LocalPath().isEmpty());
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
details::Read(path);
|
||||
|
||||
details::LocalPath() = path;
|
||||
static const auto guard = gsl::finally([] {
|
||||
details::Write();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base::options
|
||||
140
Telegram/lib_base/base/options.h
Normal file
140
Telegram/lib_base/base/options.h
Normal file
@@ -0,0 +1,140 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/flags.h"
|
||||
#include "base/variant.h"
|
||||
#include "base/required.h"
|
||||
|
||||
#ifdef linux // GCC, cmon..
|
||||
#undef linux
|
||||
#endif // linux
|
||||
|
||||
namespace base::options {
|
||||
namespace details {
|
||||
|
||||
using ValueType = std::variant<bool, int, QString>;
|
||||
|
||||
enum class ScopeFlag : uchar {
|
||||
Windows = (1 << 1),
|
||||
Mac = (1 << 2),
|
||||
Linux = (1 << 3),
|
||||
};
|
||||
inline constexpr bool is_flag_type(ScopeFlag) { return true; }
|
||||
using ScopeFlags = base::flags<ScopeFlag>;
|
||||
using ScopeFn = Fn<bool()>;
|
||||
using Scope = std::variant<ScopeFlags, ScopeFn>;
|
||||
|
||||
class BasicOption {
|
||||
public:
|
||||
BasicOption(
|
||||
const char id[],
|
||||
const char name[],
|
||||
const char description[],
|
||||
ValueType defaultValue,
|
||||
Scope scope,
|
||||
bool restartRequired);
|
||||
BasicOption(const BasicOption&) = delete;
|
||||
BasicOption operator=(const BasicOption&) = delete;
|
||||
|
||||
void set(ValueType value);
|
||||
[[nodiscard]] const ValueType &value() const;
|
||||
[[nodiscard]] const ValueType &defaultValue() const;
|
||||
|
||||
[[nodiscard]] const QString &id() const;
|
||||
[[nodiscard]] const QString &name() const;
|
||||
[[nodiscard]] const QString &description() const;
|
||||
|
||||
[[nodiscard]] bool relevant() const;
|
||||
[[nodiscard]] Scope scope() const;
|
||||
|
||||
[[nodiscard]] bool restartRequired() const;
|
||||
|
||||
private:
|
||||
ValueType _value;
|
||||
ValueType _defaultValue;
|
||||
|
||||
QString _id;
|
||||
QString _name;
|
||||
QString _description;
|
||||
Scope _scope;
|
||||
bool _restartRequired = false;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] BasicOption &Lookup(const char name[]);
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline constexpr auto windows = details::ScopeFlag::Windows;
|
||||
inline constexpr auto macos = details::ScopeFlag::Mac;
|
||||
inline constexpr auto linux = details::ScopeFlag::Linux;
|
||||
|
||||
template <typename Type>
|
||||
struct descriptor {
|
||||
required<const char*> id;
|
||||
const char *name = "";
|
||||
const char *description = "";
|
||||
Type defaultValue = Type();
|
||||
details::Scope scope = windows | macos | linux;
|
||||
bool restartRequired = false;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class option final : details::BasicOption {
|
||||
public:
|
||||
option(descriptor<Type> &&fields)
|
||||
: BasicOption(
|
||||
fields.id,
|
||||
fields.name,
|
||||
fields.description,
|
||||
std::move(fields.defaultValue),
|
||||
fields.scope,
|
||||
fields.restartRequired) {
|
||||
}
|
||||
|
||||
using BasicOption::id;
|
||||
using BasicOption::name;
|
||||
using BasicOption::description;
|
||||
using BasicOption::relevant;
|
||||
using BasicOption::scope;
|
||||
using BasicOption::restartRequired;
|
||||
|
||||
void set(Type value) {
|
||||
BasicOption::set(std::move(value));
|
||||
}
|
||||
[[nodiscard]] Type value() const {
|
||||
return v::get<Type>(BasicOption::value());
|
||||
}
|
||||
[[nodiscard]] Type defaultValue() const {
|
||||
return v::get<Type>(BasicOption::defaultValue());
|
||||
}
|
||||
|
||||
[[nodiscard]] static option &Wrap(BasicOption &that) {
|
||||
Expects(v::is<Type>(that.value()));
|
||||
|
||||
return static_cast<option&>(that);
|
||||
}
|
||||
};
|
||||
|
||||
using toggle = option<bool>;
|
||||
|
||||
template <typename Type>
|
||||
[[nodiscard]] inline Type value(const char id[]) {
|
||||
return v::get<Type>(details::Lookup(id).value());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
[[nodiscard]] inline option<Type> &lookup(const char id[]) {
|
||||
return option<Type>::Wrap(details::Lookup(id));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool changed();
|
||||
void reset();
|
||||
void init(const QString &path);
|
||||
|
||||
} // namespace base::options
|
||||
152
Telegram/lib_base/base/ordered_set.h
Normal file
152
Telegram/lib_base/base/ordered_set.h
Normal file
@@ -0,0 +1,152 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QMap>
|
||||
|
||||
// ordered set template based on QMap
|
||||
template <typename T>
|
||||
class OrderedSet {
|
||||
struct NullType {
|
||||
};
|
||||
using Self = OrderedSet<T>;
|
||||
using Impl = QMap<T, NullType>;
|
||||
using IteratorImpl = typename Impl::iterator;
|
||||
using ConstIteratorImpl = typename Impl::const_iterator;
|
||||
Impl impl_;
|
||||
|
||||
public:
|
||||
OrderedSet() = default;
|
||||
OrderedSet(const OrderedSet &other) = default;
|
||||
OrderedSet(OrderedSet &&other) = default;
|
||||
OrderedSet &operator=(const OrderedSet &other) = default;
|
||||
OrderedSet &operator=(OrderedSet &&other) = default;
|
||||
~OrderedSet() = default;
|
||||
|
||||
inline bool operator==(const Self &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const Self &other) const { return impl_ != other.impl_; }
|
||||
inline int size() const { return impl_.size(); }
|
||||
inline bool isEmpty() const { return impl_.isEmpty(); }
|
||||
inline void detach() { return impl_.detach(); }
|
||||
inline bool isDetached() const { return impl_.isDetached(); }
|
||||
inline void clear() { return impl_.clear(); }
|
||||
inline QList<T> values() const { return impl_.keys(); }
|
||||
inline const T &first() const { return impl_.firstKey(); }
|
||||
inline const T &last() const { return impl_.lastKey(); }
|
||||
|
||||
class const_iterator;
|
||||
class iterator {
|
||||
public:
|
||||
typedef typename IteratorImpl::iterator_category iterator_category;
|
||||
typedef typename IteratorImpl::difference_type difference_type;
|
||||
typedef T value_type;
|
||||
typedef T *pointer;
|
||||
typedef T &reference;
|
||||
|
||||
iterator() = default;
|
||||
iterator(const iterator &other) = default;
|
||||
iterator &operator=(const iterator &other) = default;
|
||||
inline const T &operator*() const { return impl_.key(); }
|
||||
inline const T *operator->() const { return &impl_.key(); }
|
||||
inline bool operator==(const iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; }
|
||||
inline iterator &operator++() { ++impl_; return *this; }
|
||||
inline iterator operator++(int) { return iterator(impl_++); }
|
||||
inline iterator &operator--() { --impl_; return *this; }
|
||||
inline iterator operator--(int) { return iterator(impl_--); }
|
||||
inline iterator operator+(int j) const { return iterator(impl_ + j); }
|
||||
inline iterator operator-(int j) const { return iterator(impl_ - j); }
|
||||
inline iterator &operator+=(int j) { impl_ += j; return *this; }
|
||||
inline iterator &operator-=(int j) { impl_ -= j; return *this; }
|
||||
|
||||
friend class const_iterator;
|
||||
inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; }
|
||||
|
||||
private:
|
||||
explicit iterator(const IteratorImpl &impl) : impl_(impl) {
|
||||
}
|
||||
IteratorImpl impl_;
|
||||
friend class OrderedSet<T>;
|
||||
|
||||
};
|
||||
friend class iterator;
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
typedef typename IteratorImpl::iterator_category iterator_category;
|
||||
typedef typename IteratorImpl::difference_type difference_type;
|
||||
typedef T value_type;
|
||||
typedef T *pointer;
|
||||
typedef T &reference;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const const_iterator &other) = default;
|
||||
const_iterator &operator=(const const_iterator &other) = default;
|
||||
const_iterator(const iterator &other) : impl_(other.impl_) {
|
||||
}
|
||||
const_iterator &operator=(const iterator &other) {
|
||||
impl_ = other.impl_;
|
||||
return *this;
|
||||
}
|
||||
inline const T &operator*() const { return impl_.key(); }
|
||||
inline const T *operator->() const { return &impl_.key(); }
|
||||
inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; }
|
||||
inline const_iterator &operator++() { ++impl_; return *this; }
|
||||
inline const_iterator operator++(int) { return const_iterator(impl_++); }
|
||||
inline const_iterator &operator--() { --impl_; return *this; }
|
||||
inline const_iterator operator--(int) { return const_iterator(impl_--); }
|
||||
inline const_iterator operator+(int j) const { return const_iterator(impl_ + j); }
|
||||
inline const_iterator operator-(int j) const { return const_iterator(impl_ - j); }
|
||||
inline const_iterator &operator+=(int j) { impl_ += j; return *this; }
|
||||
inline const_iterator &operator-=(int j) { impl_ -= j; return *this; }
|
||||
|
||||
friend class iterator;
|
||||
inline bool operator==(const iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; }
|
||||
|
||||
private:
|
||||
explicit const_iterator(const ConstIteratorImpl &impl) : impl_(impl) {
|
||||
}
|
||||
ConstIteratorImpl impl_;
|
||||
friend class OrderedSet<T>;
|
||||
|
||||
};
|
||||
friend class const_iterator;
|
||||
|
||||
// STL style
|
||||
inline iterator begin() { return iterator(impl_.begin()); }
|
||||
inline const_iterator begin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline const_iterator constBegin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline const_iterator cbegin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline iterator end() { detach(); return iterator(impl_.end()); }
|
||||
inline const_iterator end() const { return const_iterator(impl_.cend()); }
|
||||
inline const_iterator constEnd() const { return const_iterator(impl_.cend()); }
|
||||
inline const_iterator cend() const { return const_iterator(impl_.cend()); }
|
||||
inline iterator erase(iterator it) { return iterator(impl_.erase(it.impl_)); }
|
||||
|
||||
inline iterator insert(const T &value) { return iterator(impl_.insert(value, NullType())); }
|
||||
inline iterator insert(const_iterator pos, const T &value) { return iterator(impl_.insert(pos.impl_, value, NullType())); }
|
||||
inline int remove(const T &value) { return impl_.remove(value); }
|
||||
inline bool contains(const T &value) const { return impl_.contains(value); }
|
||||
|
||||
// more Qt
|
||||
typedef iterator Iterator;
|
||||
typedef const_iterator ConstIterator;
|
||||
inline int count() const { return impl_.count(); }
|
||||
inline iterator find(const T &value) { return iterator(impl_.find(value)); }
|
||||
inline const_iterator find(const T &value) const { return const_iterator(impl_.constFind(value)); }
|
||||
inline const_iterator constFind(const T &value) const { return const_iterator(impl_.constFind(value)); }
|
||||
inline Self &unite(const Self &other) { impl_.unite(other.impl_); return *this; }
|
||||
|
||||
// STL compatibility
|
||||
typedef typename Impl::difference_type difference_type;
|
||||
typedef typename Impl::size_type size_type;
|
||||
inline bool empty() const { return impl_.empty(); }
|
||||
|
||||
};
|
||||
88
Telegram/lib_base/base/overload.h
Normal file
88
Telegram/lib_base/base/overload.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
// This implementation was taken from range-v3 library.
|
||||
// It was modified so that more than one of passed function objects
|
||||
// could be called with an argument list and the first one that
|
||||
// matches gets used instead of a compile-time ambiguity error.
|
||||
//
|
||||
// This allows to write "default" visitor handlers with [](auto&&) syntax.
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bool is_callable_v = rpl::details::is_callable_plain_v<Args...>;
|
||||
|
||||
template <typename ...Args>
|
||||
struct overloaded;
|
||||
|
||||
template <>
|
||||
struct overloaded<> {
|
||||
};
|
||||
|
||||
template <typename First, typename ...Rest>
|
||||
struct overloaded<First, Rest...>
|
||||
: private First
|
||||
, private overloaded<Rest...> {
|
||||
|
||||
private:
|
||||
using Others = overloaded<Rest...>;
|
||||
|
||||
public:
|
||||
overloaded() = default;
|
||||
|
||||
constexpr overloaded(First first, Rest... rest)
|
||||
: First(std::move(first))
|
||||
, Others(std::move(rest)...) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype(std::declval<First&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<First&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const
|
||||
-> decltype(std::declval<const First&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<const First&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<!is_callable_v<First&, Args&&...>>>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype(std::declval<Others&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<Others&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<!is_callable_v<const First&, Args&&...>>>
|
||||
auto operator()(Args&&... args) const
|
||||
-> decltype(std::declval<const Others&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<const Others&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Function>
|
||||
Function overload(Function &&function) {
|
||||
return std::forward<Function>(function);
|
||||
}
|
||||
|
||||
template <typename ...Functions, typename = std::enable_if_t<(sizeof...(Functions) > 1)>>
|
||||
auto overload(Functions ...functions) {
|
||||
return details::overloaded<Functions...>(std::move(functions)...);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
100
Telegram/lib_base/base/parse_helper.cpp
Normal file
100
Telegram/lib_base/base/parse_helper.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/parse_helper.h"
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// inspired by https://github.com/sindresorhus/strip-json-comments
|
||||
QByteArray stripComments(const QByteArray &content) {
|
||||
enum class InsideComment {
|
||||
None,
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
};
|
||||
auto insideComment = InsideComment::None;
|
||||
auto insideString = false;
|
||||
|
||||
QByteArray result;
|
||||
auto begin = content.cbegin(), end = content.cend(), offset = begin;
|
||||
auto feedContent = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(offset, ch - offset);
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
auto feedComment = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(' ');
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
for (auto ch = offset; ch != end;) {
|
||||
auto currentChar = *ch;
|
||||
auto nextChar = (ch + 1 == end) ? 0 : *(ch + 1);
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '"') {
|
||||
auto escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\');
|
||||
if (!escaped) {
|
||||
insideString = !insideString;
|
||||
}
|
||||
}
|
||||
if (insideString) {
|
||||
++ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') {
|
||||
feedContent(ch);
|
||||
insideComment = InsideComment::SingleLine;
|
||||
ch += 2;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') {
|
||||
feedContent(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::MultiLine;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') {
|
||||
ch += 2;
|
||||
feedComment(ch);
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
feedContent(ch);
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
feedContent(ch);
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::MultiLine) {
|
||||
// unexpected end of content
|
||||
}
|
||||
if (insideComment == InsideComment::None && end > offset) {
|
||||
if (result.isEmpty()) {
|
||||
return content;
|
||||
} else {
|
||||
result.append(offset, end - offset);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
||||
41
Telegram/lib_base/base/parse_helper.h
Normal file
41
Telegram/lib_base/base/parse_helper.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// Strip all C-style comments.
|
||||
QByteArray stripComments(const QByteArray &content);
|
||||
|
||||
inline bool skipWhitespaces(const char *&from, const char *end) {
|
||||
Assert(from <= end);
|
||||
while (from != end && (
|
||||
(*from == ' ') ||
|
||||
(*from == '\n') ||
|
||||
(*from == '\t') ||
|
||||
(*from == '\r'))) {
|
||||
++from;
|
||||
}
|
||||
return (from != end);
|
||||
}
|
||||
|
||||
inline QLatin1String readName(const char *&from, const char *end) {
|
||||
Assert(from <= end);
|
||||
auto start = from;
|
||||
while (from != end && (
|
||||
(*from >= 'a' && *from <= 'z') ||
|
||||
(*from >= 'A' && *from <= 'Z') ||
|
||||
(*from >= '0' && *from <= '9') ||
|
||||
(*from == '_'))) {
|
||||
++from;
|
||||
}
|
||||
return QLatin1String(start, from - start);
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
class QString;
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
||||
std::optional<uint64> SetCustomAppIcon(QImage image);
|
||||
std::optional<uint64> SetCustomAppIcon(const QString &path);
|
||||
std::optional<uint64> CurrentCustomAppIconDigest();
|
||||
bool ClearCustomAppIcon();
|
||||
|
||||
#else // Q_OS_MAC
|
||||
|
||||
inline std::optional<uint64> SetCustomAppIcon(QImage image) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline std::optional<uint64> SetCustomAppIcon(const QString &path) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline std::optional<uint64> CurrentCustomAppIconDigest() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline bool ClearCustomAppIcon() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/base_platform_file_utilities.h"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
class QString;
|
||||
class QFile;
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
void ShowInFolder(const QString &filepath);
|
||||
[[nodiscard]] QString FileNameFromUserString(QString name);
|
||||
|
||||
bool DeleteDirectory(QString path);
|
||||
void RemoveQuarantine(const QString &path);
|
||||
|
||||
[[nodiscard]] QString CurrentExecutablePath(int argc, char *argv[]);
|
||||
[[nodiscard]] QString BundledResourcesPath();
|
||||
|
||||
bool RenameWithOverwrite(const QString &from, const QString &to);
|
||||
void FlushFileData(QFile &file);
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/global_shortcuts_generic.h"
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
namespace base::Platform::GlobalShortcuts {
|
||||
|
||||
[[nodiscard]] bool Available();
|
||||
[[nodiscard]] bool Allowed();
|
||||
|
||||
void Start(Fn<void(GlobalShortcutKeyGeneric descriptor, bool down)> process);
|
||||
void Stop();
|
||||
|
||||
[[nodiscard]] QString KeyName(GlobalShortcutKeyGeneric descriptor);
|
||||
|
||||
[[nodiscard]] bool IsToggleFullScreenKey(not_null<QKeyEvent*> e);
|
||||
|
||||
} // namespace base::Platform::GlobalShortcuts
|
||||
14
Telegram/lib_base/base/platform/base_platform_haptic.h
Normal file
14
Telegram/lib_base/base/platform/base_platform_haptic.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
void Haptic();
|
||||
[[nodiscard]] bool IsSwipeBackEnabled();
|
||||
|
||||
} // namespace base::Platform
|
||||
63
Telegram/lib_base/base/platform/base_platform_info.cpp
Normal file
63
Telegram/lib_base/base/platform/base_platform_info.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/base_platform_info.h"
|
||||
|
||||
#include "base/algorithm.h"
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxDeviceModelLength = 15;
|
||||
constexpr auto kMaxGoodDeviceModelLength = 32;
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsDeviceModelOk(const QString &model) {
|
||||
return !model.isEmpty() && (model.size() <= kMaxDeviceModelLength);
|
||||
}
|
||||
|
||||
QString SimplifyDeviceModel(QString model) {
|
||||
return CleanAndSimplify(model.replace(QChar('_'), QString()));
|
||||
}
|
||||
|
||||
QString SimplifyGoodDeviceModel(QString model, std::vector<QString> remove) {
|
||||
const auto limit = kMaxGoodDeviceModelLength;
|
||||
auto result = QString();
|
||||
for (const auto &word : model.split(QChar(' '))) {
|
||||
if (ranges::contains(remove, word.toLower())) {
|
||||
continue;
|
||||
} else if (result.isEmpty()) {
|
||||
result = word;
|
||||
} else if (result.size() + word.size() + 1 > limit) {
|
||||
return result;
|
||||
} else {
|
||||
result += ' ' + word;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ProductNameToDeviceModel(const QString &productName) {
|
||||
if (productName.startsWith("HP ")) {
|
||||
// Some special cases for good strings, like HP laptops.
|
||||
return SimplifyGoodDeviceModel(
|
||||
productName,
|
||||
{ "notebook", "desktop", "mobile", "workstation", "pc" });
|
||||
} else if (IsDeviceModelOk(productName)) {
|
||||
return productName;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString FinalizeDeviceModel(QString model) {
|
||||
using namespace ::Platform;
|
||||
|
||||
model = std::move(model).trimmed();
|
||||
return !model.isEmpty() ? model : IsMac() ? u"Mac"_q : u"Desktop"_q;
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
84
Telegram/lib_base/base/platform/base_platform_info.h
Normal file
84
Telegram/lib_base/base/platform/base_platform_info.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class QJsonObject;
|
||||
class QString;
|
||||
class QDate;
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
[[nodiscard]] bool IsDeviceModelOk(const QString &model);
|
||||
|
||||
[[nodiscard]] QString SimplifyDeviceModel(QString model);
|
||||
[[nodiscard]] QString SimplifyGoodDeviceModel(
|
||||
QString model,
|
||||
std::vector<QString> remove);
|
||||
[[nodiscard]] QString ProductNameToDeviceModel(const QString &productName);
|
||||
[[nodiscard]] QString FinalizeDeviceModel(QString model);
|
||||
|
||||
} // namespace base::Platform
|
||||
|
||||
namespace Platform {
|
||||
|
||||
enum class OutdateReason {
|
||||
IsOld,
|
||||
Is32Bit,
|
||||
};
|
||||
|
||||
[[nodiscard]] QString DeviceModelPretty();
|
||||
[[nodiscard]] QString SystemVersionPretty();
|
||||
[[nodiscard]] QString SystemCountry();
|
||||
[[nodiscard]] QString SystemLanguage();
|
||||
[[nodiscard]] QDate WhenSystemBecomesOutdated();
|
||||
[[nodiscard]] OutdateReason WhySystemBecomesOutdated();
|
||||
[[nodiscard]] int AutoUpdateVersion();
|
||||
[[nodiscard]] QString AutoUpdateKey();
|
||||
|
||||
[[nodiscard]] constexpr bool IsWindows();
|
||||
[[nodiscard]] constexpr bool IsWindows32Bit();
|
||||
[[nodiscard]] constexpr bool IsWindows64Bit();
|
||||
[[nodiscard]] constexpr bool IsWindowsARM64();
|
||||
[[nodiscard]] constexpr bool IsWindowsStoreBuild();
|
||||
[[nodiscard]] bool IsWindows7OrGreater();
|
||||
[[nodiscard]] bool IsWindows8OrGreater();
|
||||
[[nodiscard]] bool IsWindows8Point1OrGreater();
|
||||
[[nodiscard]] bool IsWindows10OrGreater();
|
||||
[[nodiscard]] bool IsWindows11OrGreater();
|
||||
|
||||
[[nodiscard]] constexpr bool IsMac();
|
||||
[[nodiscard]] constexpr bool IsMacStoreBuild();
|
||||
[[nodiscard]] bool IsMac10_12OrGreater();
|
||||
[[nodiscard]] bool IsMac10_13OrGreater();
|
||||
[[nodiscard]] bool IsMac10_14OrGreater();
|
||||
[[nodiscard]] bool IsMac10_15OrGreater();
|
||||
[[nodiscard]] bool IsMac11_0OrGreater();
|
||||
[[nodiscard]] bool IsMac26_0OrGreater();
|
||||
|
||||
[[nodiscard]] constexpr bool IsLinux();
|
||||
[[nodiscard]] bool IsX11();
|
||||
[[nodiscard]] bool IsWayland();
|
||||
[[nodiscard]] bool IsXwayland();
|
||||
|
||||
[[nodiscard]] QString GetLibcName();
|
||||
[[nodiscard]] QString GetLibcVersion();
|
||||
[[nodiscard]] QString GetWindowManager();
|
||||
|
||||
void Start(QJsonObject settings);
|
||||
void Finish();
|
||||
|
||||
} // namespace Platform
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "base/platform/win/base_info_win.h"
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
#include "base/platform/mac/base_info_mac.h"
|
||||
#else // Q_OS_WIN || Q_OS_MAC
|
||||
#include "base/platform/linux/base_info_linux.h"
|
||||
#endif // else for Q_OS_WIN || Q_OS_MAC
|
||||
16
Telegram/lib_base/base/platform/base_platform_last_input.h
Normal file
16
Telegram/lib_base/base/platform/base_platform_last_input.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
[[nodiscard]] std::optional<crl::time> LastUserInputTime();
|
||||
[[nodiscard]] inline bool LastUserInputTimeSupported() {
|
||||
return LastUserInputTime().has_value();
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,13 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
bool SwitchKeyboardLayoutToEnglish();
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,20 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
class NetworkReachability {
|
||||
public:
|
||||
virtual ~NetworkReachability() = default;
|
||||
|
||||
static std::unique_ptr<NetworkReachability> Create();
|
||||
|
||||
[[nodiscard]] virtual rpl::producer<bool> availableValue() const = 0;
|
||||
};
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/power_save_blocker.h"
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
inline constexpr int kPowerSaveBlockTypeCount = static_cast<int>(
|
||||
PowerSaveBlockType::kCount);
|
||||
|
||||
[[nodiscard]] inline constexpr int PowerSaveBlockTypeIndex(
|
||||
PowerSaveBlockType type) {
|
||||
Expects(static_cast<int>(type) >= 0);
|
||||
Expects(type < PowerSaveBlockType::kCount);
|
||||
|
||||
return static_cast<int>(type);
|
||||
}
|
||||
|
||||
// window may be null.
|
||||
void BlockPowerSave(
|
||||
PowerSaveBlockType type,
|
||||
const QString &description,
|
||||
QPointer<QWindow> window);
|
||||
void UnblockPowerSave(PowerSaveBlockType type, QPointer<QWindow> window);
|
||||
|
||||
} // namespace base::Platform
|
||||
18
Telegram/lib_base/base/platform/base_platform_process.h
Normal file
18
Telegram/lib_base/base/platform/base_platform_process.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
void ActivateProcessWindow(int64 pid, WId windowId);
|
||||
void ActivateThisProcessWindow(WId windowId);
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,82 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
class SystemMediaControls {
|
||||
public:
|
||||
enum class Command {
|
||||
PlayPause,
|
||||
Play,
|
||||
Pause,
|
||||
Next,
|
||||
Previous,
|
||||
Stop,
|
||||
Quit,
|
||||
Raise,
|
||||
LoopNone,
|
||||
LoopTrack,
|
||||
LoopPlaylist,
|
||||
Shuffle,
|
||||
None,
|
||||
};
|
||||
|
||||
enum class PlaybackStatus {
|
||||
Playing,
|
||||
Paused,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
enum class LoopStatus {
|
||||
None,
|
||||
Track,
|
||||
Playlist,
|
||||
};
|
||||
|
||||
SystemMediaControls();
|
||||
~SystemMediaControls();
|
||||
|
||||
bool init();
|
||||
|
||||
[[nodiscard]] bool seekingSupported() const;
|
||||
[[nodiscard]] bool volumeSupported() const;
|
||||
|
||||
void setApplicationName(const QString &name);
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
void setIsNextEnabled(bool value);
|
||||
void setIsPreviousEnabled(bool value);
|
||||
void setIsPlayPauseEnabled(bool value);
|
||||
void setIsStopEnabled(bool value);
|
||||
void setPlaybackStatus(PlaybackStatus status);
|
||||
void setLoopStatus(LoopStatus status);
|
||||
void setShuffle(bool value);
|
||||
void setTitle(const QString &title);
|
||||
void setArtist(const QString &artist);
|
||||
void setThumbnail(const QImage &thumbnail);
|
||||
void setDuration(int duration);
|
||||
void setPosition(int position);
|
||||
void setVolume(float64 volume);
|
||||
void clearThumbnail();
|
||||
void clearMetadata();
|
||||
void updateDisplay();
|
||||
|
||||
[[nodiscard]] rpl::producer<Command> commandRequests() const;
|
||||
[[nodiscard]] rpl::producer<float64> seekRequests() const;
|
||||
[[nodiscard]] rpl::producer<float64> volumeChangeRequests() const;
|
||||
[[nodiscard]] rpl::producer<> updatePositionRequests() const;
|
||||
|
||||
static bool Supported();
|
||||
|
||||
private:
|
||||
struct Private;
|
||||
|
||||
const std::unique_ptr<Private> _private;
|
||||
};
|
||||
|
||||
} // namespace base::Platform
|
||||
26
Telegram/lib_base/base/platform/base_platform_url_scheme.h
Normal file
26
Telegram/lib_base/base/platform/base_platform_url_scheme.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
struct UrlSchemeDescriptor {
|
||||
QString executable; // Full path.
|
||||
QString arguments; // Additional arguments.
|
||||
QString protocol; // 'myprotocol'
|
||||
QString protocolName; // "My Protocol Link"
|
||||
QString shortAppName; // "myapp"
|
||||
QString longAppName; // "MyApplication"
|
||||
QString displayAppName; // "My Application"
|
||||
QString displayAppDescription; // "My Nice Application"
|
||||
};
|
||||
|
||||
[[nodiscard]] bool CheckUrlScheme(const UrlSchemeDescriptor &descriptor);
|
||||
void RegisterUrlScheme(const UrlSchemeDescriptor &descriptor);
|
||||
void UnregisterUrlScheme(const UrlSchemeDescriptor &descriptor);
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,109 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_battery_saving_linux.h"
|
||||
|
||||
#include "base/battery_saving.h"
|
||||
#include "base/integration.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <gio/gio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
extern "C" {
|
||||
typedef struct _GPowerProfileMonitor GPowerProfileMonitor;
|
||||
} // extern "C"
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
class BatterySaving final : public AbstractBatterySaving {
|
||||
public:
|
||||
BatterySaving(Fn<void()> changedCallback);
|
||||
~BatterySaving();
|
||||
|
||||
std::optional<bool> enabled() const override;
|
||||
|
||||
private:
|
||||
GPowerProfileMonitor *_monitor = nullptr;
|
||||
gulong _handlerId = 0;
|
||||
Fn<void()> _changedCallback;
|
||||
|
||||
};
|
||||
|
||||
BatterySaving::BatterySaving(Fn<void()> changedCallback)
|
||||
: _changedCallback(std::move(changedCallback)) {
|
||||
// Detect battery
|
||||
if (QDir(u"/sys/class/power_supply"_q).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// glib 2.70+, we keep glib 2.56+ compatibility
|
||||
static const auto dup_default = [] {
|
||||
// reset dlerror after dlsym call
|
||||
const auto guard = gsl::finally([] { dlerror(); });
|
||||
return reinterpret_cast<GPowerProfileMonitor*(*)()>(
|
||||
dlsym(RTLD_DEFAULT, "g_power_profile_monitor_dup_default"));
|
||||
}();
|
||||
|
||||
if (!dup_default) {
|
||||
return;
|
||||
}
|
||||
|
||||
_monitor = dup_default();
|
||||
|
||||
if (_changedCallback) {
|
||||
_handlerId = g_signal_connect_swapped(
|
||||
_monitor,
|
||||
"notify::power-saver-enabled",
|
||||
G_CALLBACK(+[](BatterySaving *instance) {
|
||||
Integration::Instance().enterFromEventLoop([&] {
|
||||
instance->_changedCallback();
|
||||
});
|
||||
}), this);
|
||||
}
|
||||
}
|
||||
|
||||
BatterySaving::~BatterySaving() {
|
||||
if (_monitor) {
|
||||
if (_handlerId) {
|
||||
g_signal_handler_disconnect(_monitor, _handlerId);
|
||||
}
|
||||
|
||||
g_object_unref(_monitor);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<bool> BatterySaving::enabled() const {
|
||||
if (!_monitor) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// glib 2.70+, we keep glib 2.40+ compatibility
|
||||
static const auto get_power_saver_enabled = [] {
|
||||
// reset dlerror after dlsym call
|
||||
const auto guard = gsl::finally([] { dlerror(); });
|
||||
return reinterpret_cast<gboolean(*)(GPowerProfileMonitor*)>(
|
||||
dlsym(
|
||||
RTLD_DEFAULT,
|
||||
"g_power_profile_monitor_get_power_saver_enabled"));
|
||||
}();
|
||||
|
||||
if (!get_power_saver_enabled) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return get_power_saver_enabled(_monitor);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<AbstractBatterySaving> CreateBatterySaving(
|
||||
Fn<void()> changedCallback) {
|
||||
return std::make_unique<BatterySaving>(std::move(changedCallback));
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_file_utilities_linux.h"
|
||||
|
||||
#include "base/platform/base_platform_file_utilities.h"
|
||||
#include "base/platform/linux/base_linux_xdp_utilities.h"
|
||||
#include "base/platform/linux/base_linux_xdg_activation_token.h"
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
#include <xdpopenuri/xdpopenuri.hpp>
|
||||
#include <xdgfilemanager1/xdgfilemanager1.hpp>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
using namespace gi::repository;
|
||||
namespace GObject = gi::repository::GObject;
|
||||
|
||||
void PortalShowInFolder(const QString &filepath, Fn<void()> fail) {
|
||||
XdpOpenURI::OpenURIProxy::new_for_bus(
|
||||
Gio::BusType::SESSION_,
|
||||
Gio::DBusProxyFlags::NONE_,
|
||||
XDP::kService,
|
||||
XDP::kObjectPath,
|
||||
[=](GObject::Object, Gio::AsyncResult res) {
|
||||
auto interface = XdpOpenURI::OpenURI(
|
||||
XdpOpenURI::OpenURIProxy::new_for_bus_finish(res, nullptr));
|
||||
|
||||
if (!interface) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fd = open(
|
||||
QFile::encodeName(filepath).constData(),
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
|
||||
if (fd == -1) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
RunWithXdgActivationToken([=](
|
||||
const QString &activationToken) mutable {
|
||||
interface.call_open_directory(
|
||||
XDP::ParentWindowID(),
|
||||
GLib::Variant::new_handle(0),
|
||||
GLib::Variant::new_array({
|
||||
GLib::Variant::new_dict_entry(
|
||||
GLib::Variant::new_string("activation_token"),
|
||||
GLib::Variant::new_variant(
|
||||
GLib::Variant::new_string(
|
||||
activationToken.toStdString()))),
|
||||
}),
|
||||
Gio::UnixFDList::new_from_array(&fd, 1),
|
||||
{},
|
||||
[=](GObject::Object, Gio::AsyncResult res) mutable {
|
||||
if (!interface.call_open_directory_finish(res)) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void DBusShowInFolder(const QString &filepath, Fn<void()> fail) {
|
||||
XdgFileManager1::FileManager1Proxy::new_for_bus(
|
||||
Gio::BusType::SESSION_,
|
||||
Gio::DBusProxyFlags::NONE_,
|
||||
"org.freedesktop.FileManager1",
|
||||
"/org/freedesktop/FileManager1",
|
||||
[=](GObject::Object, Gio::AsyncResult res) {
|
||||
auto interface = XdgFileManager1::FileManager1(
|
||||
XdgFileManager1::FileManager1Proxy::new_for_bus_finish(
|
||||
res,
|
||||
nullptr));
|
||||
|
||||
if (!interface) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
RunWithXdgActivationToken([=](const QString &startupId) mutable {
|
||||
const auto callbackWrap = gi::unwrap(
|
||||
Gio::AsyncReadyCallback(
|
||||
[=](GObject::Object, Gio::AsyncResult res) mutable {
|
||||
if (!interface.call_show_items_finish(res)) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
),
|
||||
gi::scope_async);
|
||||
|
||||
xdg_file_manager1_file_manager1_call_show_items(
|
||||
interface.gobj_(),
|
||||
(std::array<const char*, 2>{
|
||||
GLib::filename_to_uri(
|
||||
filepath.toStdString(),
|
||||
nullptr
|
||||
).c_str(),
|
||||
nullptr,
|
||||
}).data(),
|
||||
startupId.toStdString().c_str(),
|
||||
nullptr,
|
||||
&callbackWrap->wrapper,
|
||||
callbackWrap);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowInFolder(const QString &filepath) {
|
||||
DBusShowInFolder(filepath, [=] {
|
||||
PortalShowInFolder(filepath, [=] {
|
||||
QDesktopServices::openUrl(
|
||||
QUrl::fromLocalFile(QFileInfo(filepath).absolutePath()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
QString CurrentExecutablePath(int argc, char *argv[]) {
|
||||
const auto exeLink = QFileInfo(u"/proc/%1/exe"_q.arg(getpid()));
|
||||
if (exeLink.exists() && exeLink.isSymLink()) {
|
||||
return exeLink.canonicalFilePath();
|
||||
}
|
||||
|
||||
// Fallback to the first command line argument.
|
||||
if (argc) {
|
||||
const auto argv0 = QFile::decodeName(argv[0]);
|
||||
if (!argv0.isEmpty() && !argv0.contains(QLatin1Char('/'))) {
|
||||
const auto argv0InPath = QStandardPaths::findExecutable(argv0);
|
||||
if (!argv0InPath.isEmpty()) {
|
||||
return argv0InPath;
|
||||
}
|
||||
}
|
||||
return QFileInfo(argv0).absoluteFilePath();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void RemoveQuarantine(const QString &path) {
|
||||
}
|
||||
|
||||
QString BundledResourcesPath() {
|
||||
Unexpected("BundledResourcesPath not implemented.");
|
||||
}
|
||||
|
||||
QString FileNameFromUserString(QString name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
// From http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
|
||||
bool DeleteDirectory(QString path) {
|
||||
if (path.endsWith('/')) {
|
||||
path.chop(1);
|
||||
}
|
||||
const auto pathRaw = QFile::encodeName(path);
|
||||
const auto d = opendir(pathRaw.constData());
|
||||
if (!d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (struct dirent *p = readdir(d)) {
|
||||
// Skip the names "." and ".." as we don't want to recurse on them.
|
||||
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto fname = path + '/' + p->d_name;
|
||||
const auto encoded = QFile::encodeName(fname);
|
||||
struct stat statbuf;
|
||||
if (!stat(encoded.constData(), &statbuf)) {
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
if (!DeleteDirectory(fname)) {
|
||||
closedir(d);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (unlink(encoded.constData())) {
|
||||
closedir(d);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return !rmdir(pathRaw.constData());
|
||||
}
|
||||
|
||||
bool RenameWithOverwrite(const QString &from, const QString &to) {
|
||||
const auto fromPath = QFile::encodeName(from);
|
||||
const auto toPath = QFile::encodeName(to);
|
||||
return (rename(fromPath.constData(), toPath.constData()) == 0);
|
||||
}
|
||||
|
||||
void FlushFileData(QFile &file) {
|
||||
file.flush();
|
||||
if (const auto descriptor = file.handle()) {
|
||||
fsync(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,7 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
@@ -0,0 +1,672 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_global_shortcuts_linux.h"
|
||||
|
||||
#include "base/const_string.h"
|
||||
#include "base/global_shortcuts_generic.h"
|
||||
#include "base/platform/base_platform_info.h" // IsX11
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
#include "base/platform/linux/base_linux_xcb_utilities.h" // CustomConnection, IsExtensionPresent
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
#include <xcb/record.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_keysyms.h> // xcb_key_symbols_*
|
||||
#include <xcb/xcbext.h> // xcb_poll_for_reply
|
||||
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
namespace base::Platform::GlobalShortcuts {
|
||||
namespace {
|
||||
|
||||
constexpr auto kShiftMouseButton = std::numeric_limits<uint64>::max() - 100;
|
||||
|
||||
Fn<void(GlobalShortcutKeyGeneric descriptor, bool down)> ProcessCallback;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
using XcbReply = xcb_record_enable_context_reply_t;
|
||||
|
||||
bool IsKeypad(xcb_keysym_t keysym) {
|
||||
return (xcb_is_keypad_key(keysym) || xcb_is_private_keypad_key(keysym));
|
||||
}
|
||||
|
||||
bool SkipMouseButton(xcb_button_t b) {
|
||||
return (b == 1) // Ignore the left button.
|
||||
|| (b > 3 && b < 8); // Ignore the wheel.
|
||||
}
|
||||
|
||||
class X11Manager final {
|
||||
public:
|
||||
X11Manager();
|
||||
~X11Manager();
|
||||
|
||||
[[nodiscard]] bool available() const;
|
||||
|
||||
private:
|
||||
void process(XcbReply *reply);
|
||||
xcb_keysym_t computeKeysym(xcb_keycode_t detail, uint16_t state);
|
||||
|
||||
XCB::CustomConnection _connection;
|
||||
std::unique_ptr<
|
||||
xcb_key_symbols_t,
|
||||
custom_delete<xcb_key_symbols_free>
|
||||
> _keySymbols;
|
||||
std::unique_ptr<QSocketNotifier> _notifier;
|
||||
xcb_record_context_t _context = XCB_NONE;
|
||||
xcb_record_enable_context_cookie_t _cookie = { XCB_NONE };
|
||||
|
||||
};
|
||||
|
||||
X11Manager::X11Manager()
|
||||
: _keySymbols(xcb_key_symbols_alloc(_connection)) {
|
||||
|
||||
if (xcb_connection_has_error(_connection)) {
|
||||
LOG((
|
||||
"Global Shortcuts Manager: Error to open local display!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!XCB::IsExtensionPresent(_connection, &xcb_record_id)) {
|
||||
LOG(("Global Shortcuts Manager: "
|
||||
"RECORD extension not supported on this X server!"));
|
||||
return;
|
||||
}
|
||||
|
||||
_context = xcb_generate_id(_connection);
|
||||
const xcb_record_client_spec_t clientSpec[] = {
|
||||
XCB_RECORD_CS_ALL_CLIENTS
|
||||
};
|
||||
|
||||
const xcb_record_range_t recordRange[] = {
|
||||
[] {
|
||||
xcb_record_range_t rr;
|
||||
memset(&rr, 0, sizeof(rr));
|
||||
|
||||
// XCB_KEY_PRESS = 2
|
||||
// XCB_KEY_RELEASE = 3
|
||||
// XCB_BUTTON_PRESS = 4
|
||||
// XCB_BUTTON_RELEASE = 5
|
||||
rr.device_events = { XCB_KEY_PRESS, XCB_BUTTON_RELEASE };
|
||||
return rr;
|
||||
}()
|
||||
};
|
||||
|
||||
const auto createCookie = xcb_record_create_context_checked(
|
||||
_connection,
|
||||
_context,
|
||||
0,
|
||||
sizeof(clientSpec) / sizeof(clientSpec[0]),
|
||||
sizeof(recordRange) / sizeof(recordRange[0]),
|
||||
clientSpec,
|
||||
recordRange);
|
||||
if (const auto error = xcb_request_check(_connection, createCookie)) {
|
||||
LOG((
|
||||
"Global Shortcuts Manager: Could not create a record context!"));
|
||||
_context = XCB_NONE;
|
||||
free(error);
|
||||
return;
|
||||
}
|
||||
|
||||
_cookie = xcb_record_enable_context(_connection, _context);
|
||||
xcb_flush(_connection);
|
||||
|
||||
_notifier = std::make_unique<QSocketNotifier>(
|
||||
xcb_get_file_descriptor(_connection),
|
||||
QSocketNotifier::Read);
|
||||
|
||||
QObject::connect(_notifier.get(), &QSocketNotifier::activated, [=] {
|
||||
while (const auto event = xcb_poll_for_event(_connection)) {
|
||||
free(event);
|
||||
}
|
||||
|
||||
void *reply = nullptr;
|
||||
xcb_generic_error_t *error = nullptr;
|
||||
while (_cookie.sequence
|
||||
&& xcb_poll_for_reply(
|
||||
_connection,
|
||||
_cookie.sequence,
|
||||
&reply,
|
||||
&error)) {
|
||||
// The xcb_poll_for_reply method may set both reply and error
|
||||
// to null if connection has error.
|
||||
if (xcb_connection_has_error(_connection)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
free(error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reply) {
|
||||
continue;
|
||||
}
|
||||
|
||||
process(reinterpret_cast<XcbReply*>(reply));
|
||||
free(reply);
|
||||
}
|
||||
});
|
||||
_notifier->setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
X11Manager::~X11Manager() {
|
||||
if (_cookie.sequence) {
|
||||
xcb_record_disable_context(_connection, _context);
|
||||
_cookie = { XCB_NONE };
|
||||
}
|
||||
|
||||
if (_context) {
|
||||
xcb_record_free_context(_connection, _context);
|
||||
_context = XCB_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void X11Manager::process(XcbReply *reply) {
|
||||
if (!ProcessCallback) {
|
||||
return;
|
||||
}
|
||||
// Seems like xcb_button_press_event_t and xcb_key_press_event_t structs
|
||||
// are the same, so we can safely cast both of them
|
||||
// to the xcb_key_press_event_t.
|
||||
const auto events = reinterpret_cast<xcb_key_press_event_t*>(
|
||||
xcb_record_enable_context_data(reply));
|
||||
|
||||
const auto countEvents = xcb_record_enable_context_data_length(reply) /
|
||||
sizeof(xcb_key_press_event_t);
|
||||
|
||||
for (auto e = events; e < (events + countEvents); e++) {
|
||||
const auto type = e->response_type;
|
||||
const auto buttonPress = (type == XCB_BUTTON_PRESS);
|
||||
const auto buttonRelease = (type == XCB_BUTTON_RELEASE);
|
||||
const auto keyPress = (type == XCB_KEY_PRESS);
|
||||
const auto keyRelease = (type == XCB_KEY_RELEASE);
|
||||
const auto isButton = (buttonPress || buttonRelease);
|
||||
|
||||
if (!(keyPress || keyRelease || isButton)) {
|
||||
continue;
|
||||
}
|
||||
const auto code = e->detail;
|
||||
if (isButton && SkipMouseButton(code)) {
|
||||
return;
|
||||
}
|
||||
const auto descriptor = isButton
|
||||
? (kShiftMouseButton + code)
|
||||
: GlobalShortcutKeyGeneric(computeKeysym(code, e->state));
|
||||
ProcessCallback(descriptor, keyPress || buttonPress);
|
||||
}
|
||||
}
|
||||
|
||||
xcb_keysym_t X11Manager::computeKeysym(xcb_keycode_t detail, uint16_t state) {
|
||||
// Perhaps XCB_MOD_MASK_1-5 are needed here.
|
||||
const auto keySym1 = xcb_key_symbols_get_keysym(_keySymbols.get(), detail, 1);
|
||||
if (IsKeypad(keySym1)) {
|
||||
return keySym1;
|
||||
}
|
||||
if (keySym1 >= Qt::Key_A && keySym1 <= Qt::Key_Z) {
|
||||
if (keySym1 != XCB_NO_SYMBOL) {
|
||||
return keySym1;
|
||||
}
|
||||
}
|
||||
|
||||
return xcb_key_symbols_get_keysym(_keySymbols.get(), detail, 0);
|
||||
}
|
||||
|
||||
bool X11Manager::available() const {
|
||||
return _cookie.sequence;
|
||||
}
|
||||
|
||||
std::unique_ptr<X11Manager> _x11Manager = nullptr;
|
||||
|
||||
void EnsureX11ShortcutManager() {
|
||||
if (!_x11Manager) {
|
||||
_x11Manager = std::make_unique<X11Manager>();
|
||||
}
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Available() {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
if (::Platform::IsX11()) {
|
||||
EnsureX11ShortcutManager();
|
||||
return _x11Manager->available();
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Allowed() {
|
||||
return Available();
|
||||
}
|
||||
|
||||
void Start(Fn<void(GlobalShortcutKeyGeneric descriptor, bool down)> process) {
|
||||
ProcessCallback = std::move(process);
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
EnsureX11ShortcutManager();
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
ProcessCallback = nullptr;
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
_x11Manager = nullptr;
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
}
|
||||
|
||||
QString KeyName(GlobalShortcutKeyGeneric descriptor) {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
// Telegram/ThirdParty/fcitx-qt5/platforminputcontext/qtkey.cpp
|
||||
static const auto KeyToString = flat_map<uint64, int>{
|
||||
{ XKB_KEY_KP_Space, Qt::Key_Space },
|
||||
{ XKB_KEY_KP_Tab, Qt::Key_Tab },
|
||||
{ XKB_KEY_KP_Enter, Qt::Key_Enter },
|
||||
{ XKB_KEY_KP_F1, Qt::Key_F1 },
|
||||
{ XKB_KEY_KP_F2, Qt::Key_F2 },
|
||||
{ XKB_KEY_KP_F3, Qt::Key_F3 },
|
||||
{ XKB_KEY_KP_F4, Qt::Key_F4 },
|
||||
{ XKB_KEY_KP_Home, Qt::Key_Home },
|
||||
{ XKB_KEY_KP_Left, Qt::Key_Left },
|
||||
{ XKB_KEY_KP_Up, Qt::Key_Up },
|
||||
{ XKB_KEY_KP_Right, Qt::Key_Right },
|
||||
{ XKB_KEY_KP_Down, Qt::Key_Down },
|
||||
{ XKB_KEY_KP_Page_Up, Qt::Key_PageUp },
|
||||
{ XKB_KEY_KP_Page_Down, Qt::Key_PageDown },
|
||||
{ XKB_KEY_KP_End, Qt::Key_End },
|
||||
{ XKB_KEY_KP_Begin, Qt::Key_Clear },
|
||||
{ XKB_KEY_KP_Insert, Qt::Key_Insert },
|
||||
{ XKB_KEY_KP_Delete, Qt::Key_Delete },
|
||||
{ XKB_KEY_KP_Equal, Qt::Key_Equal },
|
||||
{ XKB_KEY_KP_Multiply, Qt::Key_multiply },
|
||||
{ XKB_KEY_KP_Add, Qt::Key_Plus },
|
||||
{ XKB_KEY_KP_Separator, Qt::Key_Comma },
|
||||
{ XKB_KEY_KP_Subtract, Qt::Key_Minus },
|
||||
{ XKB_KEY_KP_Decimal, Qt::Key_Period },
|
||||
{ XKB_KEY_KP_Divide, Qt::Key_Slash },
|
||||
|
||||
{ XKB_KEY_KP_0, Qt::Key_0 },
|
||||
{ XKB_KEY_KP_1, Qt::Key_1 },
|
||||
{ XKB_KEY_KP_2, Qt::Key_2 },
|
||||
{ XKB_KEY_KP_3, Qt::Key_3 },
|
||||
{ XKB_KEY_KP_4, Qt::Key_4 },
|
||||
{ XKB_KEY_KP_5, Qt::Key_5 },
|
||||
{ XKB_KEY_KP_6, Qt::Key_6 },
|
||||
{ XKB_KEY_KP_7, Qt::Key_7 },
|
||||
{ XKB_KEY_KP_8, Qt::Key_8 },
|
||||
{ XKB_KEY_KP_9, Qt::Key_9 },
|
||||
|
||||
{ XKB_KEY_Escape, Qt::Key_Escape },
|
||||
{ XKB_KEY_Tab, Qt::Key_Tab },
|
||||
{ XKB_KEY_ISO_Left_Tab, Qt::Key_Tab },
|
||||
{ XKB_KEY_BackSpace, Qt::Key_Backspace },
|
||||
{ XKB_KEY_Return, Qt::Key_Return },
|
||||
{ XKB_KEY_KP_Enter, Qt::Key_Enter },
|
||||
{ XKB_KEY_Insert, Qt::Key_Insert },
|
||||
{ XKB_KEY_Delete, Qt::Key_Delete },
|
||||
{ XKB_KEY_Clear, Qt::Key_Delete },
|
||||
{ XKB_KEY_Pause, Qt::Key_Pause },
|
||||
{ XKB_KEY_Print, Qt::Key_Print },
|
||||
{ XKB_KEY_Sys_Req, Qt::Key_SysReq },
|
||||
{ XKB_KEY_SunSys_Req, Qt::Key_SysReq },
|
||||
{ 0x1007ff00, Qt::Key_SysReq },
|
||||
|
||||
{ XKB_KEY_Home, Qt::Key_Home },
|
||||
{ XKB_KEY_End, Qt::Key_End },
|
||||
{ XKB_KEY_Left, Qt::Key_Left },
|
||||
{ XKB_KEY_Up, Qt::Key_Up },
|
||||
{ XKB_KEY_Right, Qt::Key_Right },
|
||||
{ XKB_KEY_Down, Qt::Key_Down },
|
||||
{ XKB_KEY_Page_Up, Qt::Key_PageUp },
|
||||
{ XKB_KEY_Page_Down, Qt::Key_PageDown },
|
||||
{ XKB_KEY_Shift_L, Qt::Key_Shift },
|
||||
{ XKB_KEY_Shift_R, Qt::Key_Shift },
|
||||
{ XKB_KEY_Shift_Lock, Qt::Key_Shift },
|
||||
{ XKB_KEY_Control_L, Qt::Key_Control },
|
||||
{ XKB_KEY_Control_R, Qt::Key_Control },
|
||||
{ XKB_KEY_Meta_L, Qt::Key_Meta },
|
||||
{ XKB_KEY_Meta_R, Qt::Key_Meta },
|
||||
{ XKB_KEY_Alt_L, Qt::Key_Alt },
|
||||
{ XKB_KEY_Alt_R, Qt::Key_Alt },
|
||||
{ XKB_KEY_Caps_Lock, Qt::Key_CapsLock },
|
||||
{ XKB_KEY_Num_Lock, Qt::Key_NumLock },
|
||||
{ XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock },
|
||||
{ XKB_KEY_F1, Qt::Key_F1 },
|
||||
{ XKB_KEY_F2, Qt::Key_F2 },
|
||||
{ XKB_KEY_F3, Qt::Key_F3 },
|
||||
{ XKB_KEY_F4, Qt::Key_F4 },
|
||||
{ XKB_KEY_F5, Qt::Key_F5 },
|
||||
{ XKB_KEY_F6, Qt::Key_F6 },
|
||||
{ XKB_KEY_F7, Qt::Key_F7 },
|
||||
{ XKB_KEY_F8, Qt::Key_F8 },
|
||||
{ XKB_KEY_F9, Qt::Key_F9 },
|
||||
{ XKB_KEY_F10, Qt::Key_F10 },
|
||||
{ XKB_KEY_F11, Qt::Key_F11 },
|
||||
{ XKB_KEY_F12, Qt::Key_F12 },
|
||||
{ XKB_KEY_F13, Qt::Key_F13 },
|
||||
{ XKB_KEY_F14, Qt::Key_F14 },
|
||||
{ XKB_KEY_F15, Qt::Key_F15 },
|
||||
{ XKB_KEY_F16, Qt::Key_F16 },
|
||||
{ XKB_KEY_F17, Qt::Key_F17 },
|
||||
{ XKB_KEY_F18, Qt::Key_F18 },
|
||||
{ XKB_KEY_F19, Qt::Key_F19 },
|
||||
{ XKB_KEY_F20, Qt::Key_F20 },
|
||||
{ XKB_KEY_F21, Qt::Key_F21 },
|
||||
{ XKB_KEY_F22, Qt::Key_F22 },
|
||||
{ XKB_KEY_F23, Qt::Key_F23 },
|
||||
{ XKB_KEY_F24, Qt::Key_F24 },
|
||||
{ XKB_KEY_F25, Qt::Key_F25 },
|
||||
{ XKB_KEY_F26, Qt::Key_F26 },
|
||||
{ XKB_KEY_F27, Qt::Key_F27 },
|
||||
{ XKB_KEY_F28, Qt::Key_F28 },
|
||||
{ XKB_KEY_F29, Qt::Key_F29 },
|
||||
{ XKB_KEY_F30, Qt::Key_F30 },
|
||||
{ XKB_KEY_F31, Qt::Key_F31 },
|
||||
{ XKB_KEY_F32, Qt::Key_F32 },
|
||||
{ XKB_KEY_F33, Qt::Key_F33 },
|
||||
{ XKB_KEY_F34, Qt::Key_F34 },
|
||||
{ XKB_KEY_F35, Qt::Key_F35 },
|
||||
{ XKB_KEY_Super_L, Qt::Key_Super_L },
|
||||
{ XKB_KEY_Super_R, Qt::Key_Super_R },
|
||||
{ XKB_KEY_Menu, Qt::Key_Menu },
|
||||
{ XKB_KEY_Hyper_L, Qt::Key_Hyper_L },
|
||||
{ XKB_KEY_Hyper_R, Qt::Key_Hyper_R },
|
||||
{ XKB_KEY_Help, Qt::Key_Help },
|
||||
{ XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr },
|
||||
{ XKB_KEY_Multi_key, Qt::Key_Multi_key },
|
||||
{ XKB_KEY_Codeinput, Qt::Key_Codeinput },
|
||||
{ XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate },
|
||||
{ XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate },
|
||||
{ XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate },
|
||||
{ XKB_KEY_Mode_switch, Qt::Key_Mode_switch },
|
||||
{ XKB_KEY_script_switch, Qt::Key_Mode_switch },
|
||||
{ XKB_KEY_Kanji, Qt::Key_Kanji },
|
||||
{ XKB_KEY_Muhenkan, Qt::Key_Muhenkan },
|
||||
{ XKB_KEY_Henkan, Qt::Key_Henkan },
|
||||
{ XKB_KEY_Romaji, Qt::Key_Romaji },
|
||||
{ XKB_KEY_Hiragana, Qt::Key_Hiragana },
|
||||
{ XKB_KEY_Katakana, Qt::Key_Katakana },
|
||||
{ XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana },
|
||||
{ XKB_KEY_Zenkaku, Qt::Key_Zenkaku },
|
||||
{ XKB_KEY_Hankaku, Qt::Key_Hankaku },
|
||||
{ XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku },
|
||||
{ XKB_KEY_Touroku, Qt::Key_Touroku },
|
||||
{ XKB_KEY_Massyo, Qt::Key_Massyo },
|
||||
{ XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock },
|
||||
{ XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift },
|
||||
{ XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift },
|
||||
{ XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle },
|
||||
{ XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput },
|
||||
{ XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate },
|
||||
{ XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate },
|
||||
{ XKB_KEY_Hangul, Qt::Key_Hangul },
|
||||
{ XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start },
|
||||
{ XKB_KEY_Hangul_End, Qt::Key_Hangul_End },
|
||||
{ XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja },
|
||||
{ XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo },
|
||||
{ XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja },
|
||||
{ XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput },
|
||||
{ XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja },
|
||||
{ XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja },
|
||||
{ XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja },
|
||||
{ XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja },
|
||||
{ XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate },
|
||||
{ XKB_KEY_Hangul_MultipleCandidate, Qt::Key_MultipleCandidate },
|
||||
{ XKB_KEY_Hangul_PreviousCandidate, Qt::Key_PreviousCandidate },
|
||||
{ XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special },
|
||||
{ XKB_KEY_Hangul_switch, Qt::Key_Mode_switch },
|
||||
{ XKB_KEY_dead_grave, Qt::Key_Dead_Grave },
|
||||
{ XKB_KEY_dead_acute, Qt::Key_Dead_Acute },
|
||||
{ XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex },
|
||||
{ XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde },
|
||||
{ XKB_KEY_dead_macron, Qt::Key_Dead_Macron },
|
||||
{ XKB_KEY_dead_breve, Qt::Key_Dead_Breve },
|
||||
{ XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot },
|
||||
{ XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis },
|
||||
{ XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering },
|
||||
{ XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute },
|
||||
{ XKB_KEY_dead_caron, Qt::Key_Dead_Caron },
|
||||
{ XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla },
|
||||
{ XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek },
|
||||
{ XKB_KEY_dead_iota, Qt::Key_Dead_Iota },
|
||||
{ XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound },
|
||||
{ XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound },
|
||||
{ XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot },
|
||||
{ XKB_KEY_dead_hook, Qt::Key_Dead_Hook },
|
||||
{ XKB_KEY_dead_horn, Qt::Key_Dead_Horn },
|
||||
{ XKB_KEY_XF86Back, Qt::Key_Back },
|
||||
{ XKB_KEY_XF86Forward, Qt::Key_Forward },
|
||||
{ XKB_KEY_XF86Stop, Qt::Key_Stop },
|
||||
{ XKB_KEY_XF86Refresh, Qt::Key_Refresh },
|
||||
{ XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown },
|
||||
{ XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute },
|
||||
{ XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp },
|
||||
{ XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay },
|
||||
{ XKB_KEY_XF86AudioStop, Qt::Key_MediaStop },
|
||||
{ XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious },
|
||||
{ XKB_KEY_XF86AudioNext, Qt::Key_MediaNext },
|
||||
{ XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord },
|
||||
{ XKB_KEY_XF86AudioPause, Qt::Key_MediaPause },
|
||||
{ XKB_KEY_XF86HomePage, Qt::Key_HomePage },
|
||||
{ XKB_KEY_XF86Favorites, Qt::Key_Favorites },
|
||||
{ XKB_KEY_XF86Search, Qt::Key_Search },
|
||||
{ XKB_KEY_XF86Standby, Qt::Key_Standby },
|
||||
{ XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl },
|
||||
{ XKB_KEY_XF86Mail, Qt::Key_LaunchMail },
|
||||
{ XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia },
|
||||
{ XKB_KEY_XF86MyComputer, Qt::Key_Launch0 },
|
||||
{ XKB_KEY_XF86Calculator, Qt::Key_Launch1 },
|
||||
{ XKB_KEY_XF86Launch0, Qt::Key_Launch2 },
|
||||
{ XKB_KEY_XF86Launch1, Qt::Key_Launch3 },
|
||||
{ XKB_KEY_XF86Launch2, Qt::Key_Launch4 },
|
||||
{ XKB_KEY_XF86Launch3, Qt::Key_Launch5 },
|
||||
{ XKB_KEY_XF86Launch4, Qt::Key_Launch6 },
|
||||
{ XKB_KEY_XF86Launch5, Qt::Key_Launch7 },
|
||||
{ XKB_KEY_XF86Launch6, Qt::Key_Launch8 },
|
||||
{ XKB_KEY_XF86Launch7, Qt::Key_Launch9 },
|
||||
{ XKB_KEY_XF86Launch8, Qt::Key_LaunchA },
|
||||
{ XKB_KEY_XF86Launch9, Qt::Key_LaunchB },
|
||||
{ XKB_KEY_XF86LaunchA, Qt::Key_LaunchC },
|
||||
{ XKB_KEY_XF86LaunchB, Qt::Key_LaunchD },
|
||||
{ XKB_KEY_XF86LaunchC, Qt::Key_LaunchE },
|
||||
{ XKB_KEY_XF86LaunchD, Qt::Key_LaunchF },
|
||||
{ XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp },
|
||||
{ XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown },
|
||||
{ XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff },
|
||||
{ XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp },
|
||||
{ XKB_KEY_XF86PowerOff, Qt::Key_PowerOff },
|
||||
{ XKB_KEY_XF86WakeUp, Qt::Key_WakeUp },
|
||||
{ XKB_KEY_XF86Eject, Qt::Key_Eject },
|
||||
{ XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver },
|
||||
{ XKB_KEY_XF86WWW, Qt::Key_WWW },
|
||||
{ XKB_KEY_XF86Memo, Qt::Key_Memo },
|
||||
{ XKB_KEY_XF86LightBulb, Qt::Key_LightBulb },
|
||||
{ XKB_KEY_XF86Shop, Qt::Key_Shop },
|
||||
{ XKB_KEY_XF86History, Qt::Key_History },
|
||||
{ XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite },
|
||||
{ XKB_KEY_XF86HotLinks, Qt::Key_HotLinks },
|
||||
{ XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust },
|
||||
{ XKB_KEY_XF86Finance, Qt::Key_Finance },
|
||||
{ XKB_KEY_XF86Community, Qt::Key_Community },
|
||||
{ XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind },
|
||||
{ XKB_KEY_XF86BackForward, Qt::Key_BackForward },
|
||||
{ XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft },
|
||||
{ XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight },
|
||||
{ XKB_KEY_XF86Book, Qt::Key_Book },
|
||||
{ XKB_KEY_XF86CD, Qt::Key_CD },
|
||||
{ XKB_KEY_XF86Calculater, Qt::Key_Calculator },
|
||||
{ XKB_KEY_XF86ToDoList, Qt::Key_ToDoList },
|
||||
{ XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab },
|
||||
{ XKB_KEY_XF86Close, Qt::Key_Close },
|
||||
{ XKB_KEY_XF86Copy, Qt::Key_Copy },
|
||||
{ XKB_KEY_XF86Cut, Qt::Key_Cut },
|
||||
{ XKB_KEY_XF86Display, Qt::Key_Display },
|
||||
{ XKB_KEY_XF86DOS, Qt::Key_DOS },
|
||||
{ XKB_KEY_XF86Documents, Qt::Key_Documents },
|
||||
{ XKB_KEY_XF86Excel, Qt::Key_Excel },
|
||||
{ XKB_KEY_XF86Explorer, Qt::Key_Explorer },
|
||||
{ XKB_KEY_XF86Game, Qt::Key_Game },
|
||||
{ XKB_KEY_XF86Go, Qt::Key_Go },
|
||||
{ XKB_KEY_XF86iTouch, Qt::Key_iTouch },
|
||||
{ XKB_KEY_XF86LogOff, Qt::Key_LogOff },
|
||||
{ XKB_KEY_XF86Market, Qt::Key_Market },
|
||||
{ XKB_KEY_XF86Meeting, Qt::Key_Meeting },
|
||||
{ XKB_KEY_XF86MenuKB, Qt::Key_MenuKB },
|
||||
{ XKB_KEY_XF86MenuPB, Qt::Key_MenuPB },
|
||||
{ XKB_KEY_XF86MySites, Qt::Key_MySites },
|
||||
{ XKB_KEY_XF86News, Qt::Key_News },
|
||||
{ XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome },
|
||||
{ XKB_KEY_XF86Option, Qt::Key_Option },
|
||||
{ XKB_KEY_XF86Paste, Qt::Key_Paste },
|
||||
{ XKB_KEY_XF86Phone, Qt::Key_Phone },
|
||||
{ XKB_KEY_XF86Calendar, Qt::Key_Calendar },
|
||||
{ XKB_KEY_XF86Reply, Qt::Key_Reply },
|
||||
{ XKB_KEY_XF86Reload, Qt::Key_Reload },
|
||||
{ XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows },
|
||||
{ XKB_KEY_XF86RotationPB, Qt::Key_RotationPB },
|
||||
{ XKB_KEY_XF86RotationKB, Qt::Key_RotationKB },
|
||||
{ XKB_KEY_XF86Save, Qt::Key_Save },
|
||||
{ XKB_KEY_XF86Send, Qt::Key_Send },
|
||||
{ XKB_KEY_XF86Spell, Qt::Key_Spell },
|
||||
{ XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen },
|
||||
{ XKB_KEY_XF86Support, Qt::Key_Support },
|
||||
{ XKB_KEY_XF86TaskPane, Qt::Key_TaskPane },
|
||||
{ XKB_KEY_XF86Terminal, Qt::Key_Terminal },
|
||||
{ XKB_KEY_XF86Tools, Qt::Key_Tools },
|
||||
{ XKB_KEY_XF86Travel, Qt::Key_Travel },
|
||||
{ XKB_KEY_XF86Video, Qt::Key_Video },
|
||||
{ XKB_KEY_XF86Word, Qt::Key_Word },
|
||||
{ XKB_KEY_XF86Xfer, Qt::Key_Xfer },
|
||||
{ XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn },
|
||||
{ XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut },
|
||||
{ XKB_KEY_XF86Away, Qt::Key_Away },
|
||||
{ XKB_KEY_XF86Messenger, Qt::Key_Messenger },
|
||||
{ XKB_KEY_XF86WebCam, Qt::Key_WebCam },
|
||||
{ XKB_KEY_XF86MailForward, Qt::Key_MailForward },
|
||||
{ XKB_KEY_XF86Pictures, Qt::Key_Pictures },
|
||||
{ XKB_KEY_XF86Music, Qt::Key_Music },
|
||||
{ XKB_KEY_XF86Battery, Qt::Key_Battery },
|
||||
{ XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth },
|
||||
{ XKB_KEY_XF86WLAN, Qt::Key_WLAN },
|
||||
{ XKB_KEY_XF86UWB, Qt::Key_UWB },
|
||||
{ XKB_KEY_XF86AudioForward, Qt::Key_AudioForward },
|
||||
{ XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat },
|
||||
{ XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay },
|
||||
{ XKB_KEY_XF86Subtitle, Qt::Key_Subtitle },
|
||||
{ XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack },
|
||||
{ XKB_KEY_XF86Time, Qt::Key_Time },
|
||||
{ XKB_KEY_XF86Hibernate, Qt::Key_Hibernate },
|
||||
{ XKB_KEY_XF86View, Qt::Key_View },
|
||||
{ XKB_KEY_XF86TopMenu, Qt::Key_TopMenu },
|
||||
{ XKB_KEY_XF86PowerDown, Qt::Key_PowerDown },
|
||||
{ XKB_KEY_XF86Suspend, Qt::Key_Suspend },
|
||||
{ XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust },
|
||||
|
||||
{ XKB_KEY_XF86LaunchE, Qt::Key_LaunchG },
|
||||
{ XKB_KEY_XF86LaunchF, Qt::Key_LaunchH },
|
||||
|
||||
{ XKB_KEY_XF86Select, Qt::Key_Select },
|
||||
{ XKB_KEY_Cancel, Qt::Key_Cancel },
|
||||
{ XKB_KEY_Execute, Qt::Key_Execute },
|
||||
{ XKB_KEY_XF86Sleep, Qt::Key_Sleep },
|
||||
};
|
||||
|
||||
// Mouse.
|
||||
// Taken from QXcbConnection::translateMouseButton.
|
||||
static const auto XcbButtonToQt = flat_map<uint64, Qt::MouseButton>{
|
||||
// { 1, Qt::LeftButton }, // Ignore the left button.
|
||||
{ 2, Qt::MiddleButton },
|
||||
{ 3, Qt::RightButton },
|
||||
// Button values 4-7 were already handled as Wheel events.
|
||||
{ 8, Qt::BackButton },
|
||||
{ 9, Qt::ForwardButton },
|
||||
{ 10, Qt::ExtraButton3 },
|
||||
{ 11, Qt::ExtraButton4 },
|
||||
{ 12, Qt::ExtraButton5 },
|
||||
{ 13, Qt::ExtraButton6 },
|
||||
{ 14, Qt::ExtraButton7 },
|
||||
{ 15, Qt::ExtraButton8 },
|
||||
{ 16, Qt::ExtraButton9 },
|
||||
{ 17, Qt::ExtraButton10 },
|
||||
{ 18, Qt::ExtraButton11 },
|
||||
{ 19, Qt::ExtraButton12 },
|
||||
{ 20, Qt::ExtraButton13 },
|
||||
{ 21, Qt::ExtraButton14 },
|
||||
{ 22, Qt::ExtraButton15 },
|
||||
{ 23, Qt::ExtraButton16 },
|
||||
{ 24, Qt::ExtraButton17 },
|
||||
{ 25, Qt::ExtraButton18 },
|
||||
{ 26, Qt::ExtraButton19 },
|
||||
{ 27, Qt::ExtraButton20 },
|
||||
{ 28, Qt::ExtraButton21 },
|
||||
{ 29, Qt::ExtraButton22 },
|
||||
{ 30, Qt::ExtraButton23 },
|
||||
{ 31, Qt::ExtraButton24 },
|
||||
};
|
||||
if (descriptor > kShiftMouseButton) {
|
||||
const auto button = descriptor - kShiftMouseButton;
|
||||
if (XcbButtonToQt.contains(button)) {
|
||||
return QString("Mouse %1").arg(button);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
// Modifiers.
|
||||
static const auto ModifierToString = flat_map<uint64, const_string>{
|
||||
{ XKB_KEY_Shift_L, "Shift" },
|
||||
{ XKB_KEY_Shift_R, "Right Shift" },
|
||||
{ XKB_KEY_Control_L, "Ctrl" },
|
||||
{ XKB_KEY_Control_R, "Right Ctrl" },
|
||||
{ XKB_KEY_Meta_L, "Meta" },
|
||||
{ XKB_KEY_Meta_R, "Right Meta" },
|
||||
{ XKB_KEY_Alt_L, "Alt" },
|
||||
{ XKB_KEY_Alt_R, "Right Alt" },
|
||||
{ XKB_KEY_Super_L, "Super" },
|
||||
{ XKB_KEY_Super_R, "Right Super" },
|
||||
};
|
||||
const auto modIt = ModifierToString.find(descriptor);
|
||||
if (modIt != end(ModifierToString)) {
|
||||
return modIt->second.utf16();
|
||||
}
|
||||
//
|
||||
|
||||
const auto fromSequence = [](int k) {
|
||||
return QKeySequence(k).toString(QKeySequence::NativeText);
|
||||
};
|
||||
|
||||
// The conversion is not necessary,
|
||||
// if the value in the range Qt::Key_Space - Qt::Key_QuoteLeft.
|
||||
if (descriptor >= Qt::Key_Space && descriptor <= Qt::Key_QuoteLeft) {
|
||||
return fromSequence(descriptor);
|
||||
}
|
||||
const auto prefix = IsKeypad(descriptor) ? "Num " : QString();
|
||||
|
||||
const auto keyIt = KeyToString.find(descriptor);
|
||||
return (keyIt != end(KeyToString))
|
||||
? prefix + fromSequence(keyIt->second)
|
||||
: QString("\\x%1").arg(descriptor, 0, 16);
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsToggleFullScreenKey(not_null<QKeyEvent*> e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace base::Platform::GlobalShortcuts
|
||||
@@ -0,0 +1,9 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/global_shortcuts.h"
|
||||
53
Telegram/lib_base/base/platform/linux/base_haptic_linux.cpp
Normal file
53
Telegram/lib_base/base/platform/linux/base_haptic_linux.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_haptic_linux.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
#include <sigxcpufeedback/sigxcpufeedback.hpp>
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
using namespace gi::repository;
|
||||
namespace GObject = gi::repository::GObject;
|
||||
|
||||
} // namespace
|
||||
|
||||
void Haptic() {
|
||||
SigxcpuFeedback::HapticProxy::new_for_bus(
|
||||
Gio::BusType::SESSION_,
|
||||
Gio::DBusProxyFlags::NONE_,
|
||||
"org.sigxcpu.Feedback",
|
||||
"/org/sigxcpu/Feedback",
|
||||
[=](GObject::Object, Gio::AsyncResult res) {
|
||||
auto interface = SigxcpuFeedback::Haptic(
|
||||
SigxcpuFeedback::HapticProxy::new_for_bus_finish(
|
||||
res,
|
||||
nullptr));
|
||||
|
||||
if (!interface) {
|
||||
return;
|
||||
}
|
||||
|
||||
interface.call_vibrate(
|
||||
QGuiApplication::desktopFileName().toStdString(),
|
||||
GLib::Variant::new_array({
|
||||
GLib::Variant::new_tuple({
|
||||
GLib::Variant::new_double(0.2),
|
||||
GLib::Variant::new_uint32(5),
|
||||
}),
|
||||
}),
|
||||
nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
bool IsSwipeBackEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,9 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/platform/base_platform_haptic.h"
|
||||
386
Telegram/lib_base/base/platform/linux/base_info_linux.cpp
Normal file
386
Telegram/lib_base/base/platform/linux/base_info_linux.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_info_linux.h"
|
||||
|
||||
#include "base/algorithm.h"
|
||||
#include "base/platform/linux/base_linux_library.h"
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
#include "base/platform/linux/base_linux_xcb_utilities.h"
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QLocale>
|
||||
#include <QtCore/QVersionNumber>
|
||||
#include <QtCore/QDate>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <gnu/libc-version.h>
|
||||
#endif // __GLIBC__
|
||||
|
||||
extern "C" {
|
||||
struct wl_display;
|
||||
} // extern "C"
|
||||
|
||||
namespace Platform {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QStringList GetDesktopEnvironment() {
|
||||
const auto list = qEnvironmentVariable("XDG_CURRENT_DESKTOP").split(':');
|
||||
return list | ranges::views::transform([](const auto &item) {
|
||||
return item.simplified();
|
||||
}) | ranges::to<QStringList>;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString ChassisTypeToString(uint type) {
|
||||
switch (type) {
|
||||
case 0x3: /* Desktop */
|
||||
case 0x4: /* Low Profile Desktop */
|
||||
case 0x6: /* Mini Tower */
|
||||
case 0x7: /* Tower */
|
||||
case 0xD: /* All in one (i.e. PC built into monitor) */
|
||||
return "Desktop";
|
||||
case 0x8: /* Portable */
|
||||
case 0x9: /* Laptop */
|
||||
case 0xA: /* Notebook */
|
||||
case 0xE: /* Sub Notebook */
|
||||
return "Laptop";
|
||||
case 0xB: /* Hand Held */
|
||||
return "Handset";
|
||||
case 0x11: /* Main Server Chassis */
|
||||
case 0x1C: /* Blade */
|
||||
case 0x1D: /* Blade Enclosure */
|
||||
return "Server";
|
||||
case 0x1E: /* Tablet */
|
||||
return "Tablet";
|
||||
case 0x1F: /* Convertible */
|
||||
case 0x20: /* Detachable */
|
||||
return "Convertible";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsGlibcLess228() {
|
||||
static const auto result = [] {
|
||||
const auto libcName = GetLibcName();
|
||||
const auto libcVersion = GetLibcVersion();
|
||||
return (libcName == qstr("glibc"))
|
||||
&& !libcVersion.isEmpty()
|
||||
&& (QVersionNumber::fromString(libcVersion)
|
||||
< QVersionNumber(2, 28));
|
||||
}();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString DeviceModelPretty() {
|
||||
using namespace base::Platform;
|
||||
static const auto result = FinalizeDeviceModel([&] {
|
||||
const auto value = [](const char *key) {
|
||||
auto file = QFile(u"/sys/class/dmi/id/"_q + key);
|
||||
return (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
? SimplifyDeviceModel(QString(file.readAll()))
|
||||
: QString();
|
||||
};
|
||||
const auto productName = value("product_name");
|
||||
if (const auto model = ProductNameToDeviceModel(productName)
|
||||
; !model.isEmpty()) {
|
||||
return model;
|
||||
}
|
||||
|
||||
const auto productFamily = value("product_family");
|
||||
const auto boardName = value("board_name");
|
||||
const auto familyName = SimplifyDeviceModel(
|
||||
productFamily + ' ' + boardName);
|
||||
|
||||
if (IsDeviceModelOk(familyName)) {
|
||||
return familyName;
|
||||
} else if (IsDeviceModelOk(boardName)) {
|
||||
return boardName;
|
||||
} else if (IsDeviceModelOk(productFamily)) {
|
||||
return productFamily;
|
||||
}
|
||||
|
||||
const auto virtualization = []() -> QString {
|
||||
QProcess process;
|
||||
process.start("systemd-detect-virt", QStringList());
|
||||
process.waitForFinished();
|
||||
return process.readAll().simplified().toUpper();
|
||||
}();
|
||||
|
||||
if (!virtualization.isEmpty() && virtualization != qstr("NONE")) {
|
||||
return virtualization;
|
||||
}
|
||||
|
||||
const auto chassisType = ChassisTypeToString(
|
||||
value("chassis_type").toUInt());
|
||||
if (!chassisType.isEmpty()) {
|
||||
return chassisType;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString SystemVersionPretty() {
|
||||
static const auto result = [&] {
|
||||
QStringList resultList{};
|
||||
|
||||
struct utsname u;
|
||||
if (uname(&u) == 0) {
|
||||
resultList << u.sysname;
|
||||
#ifndef Q_OS_LINUX
|
||||
resultList << u.release;
|
||||
#endif // !Q_OS_LINUX
|
||||
} else {
|
||||
resultList << "Unknown";
|
||||
}
|
||||
|
||||
if (const auto desktopEnvironment = GetDesktopEnvironment();
|
||||
!desktopEnvironment.isEmpty()) {
|
||||
resultList << desktopEnvironment;
|
||||
} else if (const auto windowManager = GetWindowManager();
|
||||
!windowManager.isEmpty()) {
|
||||
resultList << windowManager;
|
||||
}
|
||||
|
||||
if (IsWayland()) {
|
||||
resultList << "Wayland";
|
||||
} else if (IsXwayland()) {
|
||||
resultList << "Xwayland";
|
||||
} else if (IsX11()) {
|
||||
resultList << "X11";
|
||||
}
|
||||
|
||||
const auto libcName = GetLibcName();
|
||||
const auto libcVersion = GetLibcVersion();
|
||||
if (!libcVersion.isEmpty()) {
|
||||
if (!libcName.isEmpty()) {
|
||||
resultList << libcName;
|
||||
} else {
|
||||
resultList << "libc";
|
||||
}
|
||||
resultList << libcVersion;
|
||||
}
|
||||
|
||||
return resultList.join(' ');
|
||||
}();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString SystemCountry() {
|
||||
return QLocale::system().name().split('_').last();
|
||||
}
|
||||
|
||||
QString SystemLanguage() {
|
||||
const auto system = QLocale::system();
|
||||
const auto languages = system.uiLanguages();
|
||||
return languages.isEmpty()
|
||||
? system.name().split('_').first()
|
||||
: languages.front();
|
||||
}
|
||||
|
||||
QDate WhenSystemBecomesOutdated() {
|
||||
if (IsGlibcLess228()) {
|
||||
return QDate(2023, 7, 1); // Older than CentOS 8.
|
||||
}
|
||||
return QDate();
|
||||
}
|
||||
|
||||
int AutoUpdateVersion() {
|
||||
if (IsGlibcLess228()) {
|
||||
return 2;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
QString AutoUpdateKey() {
|
||||
return "linux";
|
||||
}
|
||||
|
||||
QString GetLibcName() {
|
||||
#ifdef __GLIBC__
|
||||
return "glibc";
|
||||
#endif // __GLIBC__
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString GetLibcVersion() {
|
||||
#ifdef __GLIBC__
|
||||
static const auto result = [&] {
|
||||
const auto version = QString::fromLatin1(gnu_get_libc_version());
|
||||
return QVersionNumber::fromString(version).isNull() ? QString() : version;
|
||||
}();
|
||||
return result;
|
||||
#endif // __GLIBC__
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString GetWindowManager() {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
const base::Platform::XCB::Connection connection;
|
||||
if (!connection || xcb_connection_has_error(connection)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto root = base::Platform::XCB::GetRootWindow(connection);
|
||||
if (!root) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto nameAtom = base::Platform::XCB::GetAtom(
|
||||
connection,
|
||||
"_NET_WM_NAME");
|
||||
|
||||
const auto utf8Atom = base::Platform::XCB::GetAtom(
|
||||
connection,
|
||||
"UTF8_STRING");
|
||||
|
||||
const auto supportingWindow = base::Platform::XCB::GetSupportingWMCheck(
|
||||
connection,
|
||||
root);
|
||||
|
||||
if (!nameAtom || !utf8Atom || !supportingWindow) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto cookie = xcb_get_property(
|
||||
connection,
|
||||
false,
|
||||
supportingWindow,
|
||||
nameAtom,
|
||||
utf8Atom,
|
||||
0,
|
||||
1024);
|
||||
|
||||
const auto reply = base::Platform::XCB::MakeReplyPointer(
|
||||
xcb_get_property_reply(
|
||||
connection,
|
||||
cookie,
|
||||
nullptr));
|
||||
|
||||
if (!reply) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return (reply->format == 8 && reply->type == utf8Atom)
|
||||
? QString::fromUtf8(
|
||||
reinterpret_cast<const char*>(
|
||||
xcb_get_property_value(reply.get())),
|
||||
xcb_get_property_value_length(reply.get())).simplified()
|
||||
: QString();
|
||||
#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
return QString();
|
||||
#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
}
|
||||
|
||||
bool IsX11() {
|
||||
if (!qApp) {
|
||||
static const auto result = []() -> bool {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
const base::Platform::XCB::Connection connection;
|
||||
return connection && !xcb_connection_has_error(connection);
|
||||
#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
return qEnvironmentVariableIsSet("DISPLAY");
|
||||
#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
}();
|
||||
return result;
|
||||
}
|
||||
static const bool result =
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
|
||||
#if defined QT_FEATURE_xcb && QT_CONFIG(xcb)
|
||||
qApp->nativeInterface<QNativeInterface::QX11Application>()
|
||||
#else // xcb
|
||||
false
|
||||
#endif // !xcb
|
||||
#else // Qt >= 6.2.0
|
||||
QGuiApplication::platformName() == "xcb"
|
||||
#endif // Qt < 6.2.0
|
||||
;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsWayland() {
|
||||
if (!qApp) {
|
||||
static const auto result = []() -> bool {
|
||||
struct wl_display *(*wl_display_connect)(const char *name);
|
||||
void (*wl_display_disconnect)(struct wl_display *display);
|
||||
if (const auto lib = base::Platform::LoadLibrary(
|
||||
"libwayland-client.so.0",
|
||||
RTLD_NODELETE); lib
|
||||
&& LOAD_LIBRARY_SYMBOL(lib, wl_display_connect)
|
||||
&& LOAD_LIBRARY_SYMBOL(lib, wl_display_disconnect)) {
|
||||
const auto display = wl_display_connect(nullptr);
|
||||
if (display) {
|
||||
wl_display_disconnect(display);
|
||||
}
|
||||
return display;
|
||||
}
|
||||
return qEnvironmentVariableIsSet("WAYLAND_DISPLAY");
|
||||
}();
|
||||
return result;
|
||||
}
|
||||
static const bool result =
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||
#if defined QT_FEATURE_wayland && QT_CONFIG(wayland)
|
||||
qApp->nativeInterface<QNativeInterface::QWaylandApplication>()
|
||||
#else // wayland
|
||||
false
|
||||
#endif // !wayland
|
||||
#else // Qt >= 6.7.0
|
||||
QGuiApplication::platformName().startsWith("wayland")
|
||||
#endif // Qt < 6.7.0
|
||||
;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsXwayland() {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
static const auto result = []() -> bool {
|
||||
const base::Platform::XCB::Connection connection;
|
||||
if (!connection || xcb_connection_has_error(connection)) {
|
||||
return false;
|
||||
}
|
||||
constexpr auto kXWAYLAND = "XWAYLAND";
|
||||
const auto reply = base::Platform::XCB::MakeReplyPointer(
|
||||
xcb_query_extension_reply(
|
||||
connection,
|
||||
xcb_query_extension(
|
||||
connection,
|
||||
strlen(kXWAYLAND),
|
||||
kXWAYLAND),
|
||||
nullptr));
|
||||
if (!reply) {
|
||||
return false;
|
||||
}
|
||||
return reply->present;
|
||||
}();
|
||||
return result;
|
||||
#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
return IsX11() && qEnvironmentVariableIsSet("WAYLAND_DISPLAY");
|
||||
#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
}
|
||||
|
||||
void Start(QJsonObject options) {
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
40
Telegram/lib_base/base/platform/linux/base_info_linux.h
Normal file
40
Telegram/lib_base/base/platform/linux/base_info_linux.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/platform/base_platform_info.h"
|
||||
|
||||
namespace Platform {
|
||||
|
||||
inline OutdateReason WhySystemBecomesOutdated() {
|
||||
return OutdateReason::IsOld;
|
||||
}
|
||||
|
||||
inline constexpr bool IsLinux() {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline constexpr bool IsWindows() { return false; }
|
||||
inline constexpr bool IsWindows32Bit() { return false; }
|
||||
inline constexpr bool IsWindows64Bit() { return false; }
|
||||
inline constexpr bool IsWindowsARM64() { return false; }
|
||||
inline constexpr bool IsWindowsStoreBuild() { return false; }
|
||||
inline bool IsWindows7OrGreater() { return false; }
|
||||
inline bool IsWindows8OrGreater() { return false; }
|
||||
inline bool IsWindows8Point1OrGreater() { return false; }
|
||||
inline bool IsWindows10OrGreater() { return false; }
|
||||
inline bool IsWindows11OrGreater() { return false; }
|
||||
inline constexpr bool IsMac() { return false; }
|
||||
inline constexpr bool IsMacStoreBuild() { return false; }
|
||||
inline bool IsMac10_12OrGreater() { return false; }
|
||||
inline bool IsMac10_13OrGreater() { return false; }
|
||||
inline bool IsMac10_14OrGreater() { return false; }
|
||||
inline bool IsMac10_15OrGreater() { return false; }
|
||||
inline bool IsMac11_0OrGreater() { return false; }
|
||||
inline bool IsMac26_0OrGreater() { return false; }
|
||||
|
||||
} // namespace Platform
|
||||
104
Telegram/lib_base/base/platform/linux/base_last_input_linux.cpp
Normal file
104
Telegram/lib_base/base/platform/linux/base_last_input_linux.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_last_input_linux.h"
|
||||
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
#include "base/platform/linux/base_linux_xcb_utilities.h"
|
||||
|
||||
#include <xcb/screensaver.h>
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
#include <mutteridlemonitor/mutteridlemonitor.hpp>
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
using namespace gi::repository;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
std::optional<crl::time> XCBLastUserInputTime() {
|
||||
const XCB::Connection connection;
|
||||
if (!connection || xcb_connection_has_error(connection)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!XCB::IsExtensionPresent(connection, &xcb_screensaver_id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto root = XCB::GetRootWindow(connection);
|
||||
if (!root) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto cookie = xcb_screensaver_query_info(
|
||||
connection,
|
||||
root);
|
||||
|
||||
const auto reply = XCB::MakeReplyPointer(
|
||||
xcb_screensaver_query_info_reply(
|
||||
connection,
|
||||
cookie,
|
||||
nullptr));
|
||||
|
||||
if (!reply) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (crl::now() - static_cast<crl::time>(reply->ms_since_user_input));
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
std::optional<crl::time> MutterDBusLastUserInputTime() {
|
||||
static auto NotSupported = false;
|
||||
if (NotSupported) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto interface = MutterIdleMonitor::IdleMonitor(
|
||||
MutterIdleMonitor::IdleMonitorProxy::new_for_bus_sync(
|
||||
Gio::BusType::SESSION_,
|
||||
Gio::DBusProxyFlags::DO_NOT_AUTO_START_AT_CONSTRUCTION_,
|
||||
"org.gnome.Mutter.IdleMonitor",
|
||||
"/org/gnome/Mutter/IdleMonitor/Core",
|
||||
nullptr));
|
||||
|
||||
if (!interface) {
|
||||
NotSupported = true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto result = interface.call_get_idletime_sync();
|
||||
if (!result) {
|
||||
NotSupported = true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (crl::now() - static_cast<crl::time>(std::get<1>(*result)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<crl::time> LastUserInputTime() {
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
const auto xcbResult = XCBLastUserInputTime();
|
||||
if (xcbResult.has_value()) {
|
||||
return xcbResult;
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
const auto mutterResult = MutterDBusLastUserInputTime();
|
||||
if (mutterResult.has_value()) {
|
||||
return mutterResult;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_layout_switch_linux.h"
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
bool SwitchKeyboardLayoutToEnglish() {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "base/platform/linux/base_linux_allocation_tracer.h"
|
||||
|
||||
#include "base/debug_log.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void SetMallocLogger(void (*logger)(size_t, void *));
|
||||
void SetVallocLogger(void (*logger)(size_t, void *));
|
||||
void SetPVallocLogger(void (*logger)(size_t, void *));
|
||||
void SetReallocLogger(void (*logger)(void *, size_t, void *));
|
||||
void SetFreeLogger(void (*logger)(void *));
|
||||
void SetMemAlignLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetAlignedAllocLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetPosixMemAlignLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetCallocLogger(void (*logger)(size_t, size_t, void *));
|
||||
|
||||
namespace base::Platform {
|
||||
namespace {
|
||||
|
||||
#ifdef DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
|
||||
constexpr auto kBufferSize = 1024 * 1024;
|
||||
|
||||
char *Buffer/* = nullptr*/;
|
||||
FILE *File/* = 0*/;
|
||||
char *Data/* = nullptr*/;
|
||||
std::mutex Mutex;
|
||||
|
||||
void WriteBlock() {
|
||||
if (Data > Buffer) {
|
||||
fwrite(Buffer, Data - Buffer, 1, File);
|
||||
fflush(File);
|
||||
fdatasync(fileno(File));
|
||||
Data = Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
void AppendEntry(char (&bytes)[Size]) {
|
||||
std::unique_lock<std::mutex> lock(Mutex);
|
||||
if (!File) {
|
||||
return;
|
||||
} else if (Data + Size > Buffer + kBufferSize) {
|
||||
WriteBlock();
|
||||
}
|
||||
*reinterpret_cast<std::uint32_t*>(bytes + 1) = uint32_t(time(nullptr));
|
||||
memcpy(Data, bytes, Size);
|
||||
Data += Size;
|
||||
}
|
||||
|
||||
void MallocLogger(size_t size, void *result) {
|
||||
char entry[5 + sizeof(std::uint64_t) * 2];
|
||||
entry[0] = 1;
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5)
|
||||
= static_cast<std::uint64_t>(size);
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5 + sizeof(std::uint64_t))
|
||||
= reinterpret_cast<std::uint64_t>(result);
|
||||
AppendEntry(entry);
|
||||
}
|
||||
|
||||
void VallocLogger(size_t size, void *result) {
|
||||
MallocLogger(size, result);
|
||||
}
|
||||
|
||||
void PVallocLogger(size_t size, void *result) {
|
||||
MallocLogger(size, result);
|
||||
}
|
||||
|
||||
void CallocLogger(size_t num, size_t size, void *result) {
|
||||
MallocLogger(num * size, result);
|
||||
}
|
||||
|
||||
void ReallocLogger(void *ptr, size_t size, void *result) {
|
||||
if (!ptr) {
|
||||
return MallocLogger(size, result);
|
||||
}
|
||||
char entry[5 + sizeof(std::uint64_t) * 3];
|
||||
entry[0] = 2;
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5)
|
||||
= reinterpret_cast<std::uint64_t>(ptr);
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5 + sizeof(std::uint64_t))
|
||||
= static_cast<std::uint64_t>(size);
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5 + sizeof(std::uint64_t) * 2)
|
||||
= reinterpret_cast<std::uint64_t>(result);
|
||||
AppendEntry(entry);
|
||||
}
|
||||
|
||||
void MemAlignLogger(size_t alignment, size_t size, void *result) {
|
||||
MallocLogger(size, result);
|
||||
}
|
||||
|
||||
void AlignedAllocLogger(size_t alignment, size_t size, void *result) {
|
||||
MallocLogger(size, result);
|
||||
}
|
||||
|
||||
void PosixMemAlignLogger(size_t alignment, size_t size, void *result) {
|
||||
MallocLogger(size, result);
|
||||
}
|
||||
|
||||
void FreeLogger(void *ptr) {
|
||||
if (ptr) {
|
||||
char entry[5 + sizeof(std::uint64_t)];
|
||||
entry[0] = 3;
|
||||
*reinterpret_cast<std::uint64_t*>(entry + 5)
|
||||
= reinterpret_cast<std::uint64_t>(ptr);
|
||||
AppendEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void InstallLoggers() {
|
||||
SetMallocLogger(MallocLogger);
|
||||
SetVallocLogger(VallocLogger);
|
||||
SetPVallocLogger(PVallocLogger);
|
||||
SetCallocLogger(CallocLogger);
|
||||
SetReallocLogger(ReallocLogger);
|
||||
SetMemAlignLogger(MemAlignLogger);
|
||||
SetAlignedAllocLogger(AlignedAllocLogger);
|
||||
SetPosixMemAlignLogger(PosixMemAlignLogger);
|
||||
SetFreeLogger(FreeLogger);
|
||||
}
|
||||
|
||||
void RemoveLoggers() {
|
||||
SetMallocLogger(nullptr);
|
||||
SetVallocLogger(nullptr);
|
||||
SetPVallocLogger(nullptr);
|
||||
SetCallocLogger(nullptr);
|
||||
SetReallocLogger(nullptr);
|
||||
SetMemAlignLogger(nullptr);
|
||||
SetAlignedAllocLogger(nullptr);
|
||||
SetPosixMemAlignLogger(nullptr);
|
||||
SetFreeLogger(nullptr);
|
||||
}
|
||||
|
||||
#endif // DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
|
||||
} // namespace
|
||||
|
||||
void SetAllocationTracerPath(const QString &path) {
|
||||
#ifdef DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
Expects(!Buffer);
|
||||
|
||||
Data = Buffer = new char[kBufferSize];
|
||||
if (!Buffer) {
|
||||
return;
|
||||
}
|
||||
File = fopen(path.toStdString().c_str(), "wb");
|
||||
if (!File) {
|
||||
return;
|
||||
}
|
||||
InstallLoggers();
|
||||
#endif // DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
}
|
||||
|
||||
void FinishAllocationTracer() {
|
||||
#ifdef DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
if (File) {
|
||||
RemoveLoggers();
|
||||
|
||||
std::unique_lock<std::mutex> lock(Mutex);
|
||||
WriteBlock();
|
||||
fclose(File);
|
||||
File = nullptr;
|
||||
}
|
||||
#endif // DESKTOP_APP_USE_ALLOCATION_TRACER
|
||||
}
|
||||
|
||||
} // namespace base::Platform
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace base::Platform {
|
||||
|
||||
void SetAllocationTracerPath(const QString &path);
|
||||
void FinishAllocationTracer();
|
||||
|
||||
} // namespace base::Platform
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user