diff --git a/CMakeLists.txt b/CMakeLists.txt index 249452d6..9eb86ac5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) project(TDLib VERSION 1.1.3 LANGUAGES CXX C) +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore") + set(UWP 1) +endif() + option(TD_ENABLE_JNI "Use \"ON\" to enable JNI-compatible TDLib API.") if (NOT DEFINED CMAKE_MODULE_PATH) @@ -145,6 +149,11 @@ elseif (INTEL) endif() if (WIN32) + if (CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1") + string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + endif() + add_definitions(-D_SCL_SECURE_NO_WARNINGS) + add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DNOMINMAX -DUNICODE -D_UNICODE) endif() if (CYGWIN) @@ -275,6 +284,12 @@ set(TL_C_SCHEME_SOURCE ${TL_C_AUTO} ) +set_source_files_properties(${TL_WINDOWS_AUTO} PROPERTIES GENERATED TRUE) +set(TL_WINDOWS_SCHEME_SOURCE + ${TL_TD_WINDOWS_AUTO} + td/tl/tl_win_object.h +) + set(TDLIB_SOURCE td/mtproto/AuthData.cpp td/mtproto/crypto.cpp @@ -583,6 +598,30 @@ if (TD_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android target_link_libraries(tdclient PUBLIC ${JAVA_JVM_LIBRARY}) endif() +if (TD_ENABLE_DOTNET) + add_library(TdWindows SHARED + td/telegram/ClientWindows.cpp + td/telegram/LogWindows.cpp + ${TL_WINDOWS_SCHEME_SOURCE} + ) + target_link_libraries(TdWindows PRIVATE tdclient tdutils) + target_include_directories(TdWindows PUBLIC + $ + ) + if (NOT CMAKE_CROSSCOMPILING) + add_dependencies(TdWindows generate_td_windows_api) + endif() + if (UWP) + set_target_properties(TdWindows PROPERTIES VS_WINRT_COMPONENT "true") + target_compile_options(TdWindows PUBLIC "/ZW") + else() + set_target_properties(TdWindows PROPERTIES COMPILE_FLAGS "/GR /clr") + target_compile_options(TdWindows PUBLIC "/EHa") + endif() + + target_compile_options(TdWindows PUBLIC "/bigobj") +endif() + # tdc - TDLib interface in pure c. add_library(tdc STATIC ${TL_C_SCHEME_SOURCE} td/telegram/td_c_client.cpp td/telegram/td_c_client.h) target_include_directories(tdc PUBLIC diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index 7a693ec9..765afad5 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -27,7 +27,8 @@ set(TL_TD_JSON_AUTO PARENT_SCOPE ) -set(TL_TD_API_TLO ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tlo PARENT_SCOPE) +set(TL_TD_API_TLO ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tlo) +set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) set(TL_C_AUTO ${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_tdc_api.cpp @@ -36,6 +37,12 @@ set(TL_C_AUTO PARENT_SCOPE ) +set(TL_TD_WINDOWS_AUTO + ${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/TdWindowsApi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/TdWindowsApi.h + PARENT_SCOPE +) + set(TL_GENERATE_COMMON_SOURCE generate_common.cpp @@ -76,6 +83,7 @@ set(TL_GENERATE_JSON_SOURCE tl_json_converter.h ) + if (NOT CMAKE_CROSSCOMPILING) find_program(PHP_EXECUTABLE php) @@ -89,8 +97,7 @@ if (NOT CMAKE_CROSSCOMPILING) target_link_libraries(generate_common PRIVATE tdtl) add_custom_target(tl_generate_common WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${GENERATE_COMMON_CMD} - COMMENT "Generate common tl source files" + COMMAND ${GENERATE_COMMON_CMD} COMMENT "Generate common tl source files" DEPENDS generate_common scheme/mtproto_api.tlo scheme/telegram_api.tlo scheme/secret_api.tlo scheme/td_api.tlo ) if (TD_ENABLE_JNI) @@ -123,4 +130,13 @@ if (NOT CMAKE_CROSSCOMPILING) install(FILES JavadocTlDocumentationGenerator.php TlDocumentationGenerator.php DESTINATION bin/td/generate) install(FILES scheme/td_api.tlo scheme/td_api.tl DESTINATION bin/td/generate/scheme) endif() + + add_executable(generate_td_windows_api_exe generate_td_windows_api.cpp tl_writer_win.h) + target_link_libraries(generate_td_windows_api_exe PRIVATE tdtl) + add_custom_target(generate_td_windows_api + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND generate_td_windows_api_exe ${TL_TD_API_TLO} + COMMENT "Generate tl-files" + DEPENDS generate_td_windows_api_exe ${TL_TD_API_TLO} + ) endif() diff --git a/td/generate/generate_td_windows_api.cpp b/td/generate/generate_td_windows_api.cpp new file mode 100644 index 00000000..59e7f3d6 --- /dev/null +++ b/td/generate/generate_td_windows_api.cpp @@ -0,0 +1,25 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/tl/tl_config.h" +#include "td/tl/tl_generate.h" + +#include "tl_writer_win.h" + +int main(int argc, char *argv[]) { + if (argc < 2) { + return 1; + } + + td::tl::tl_config config_td = td::tl::read_tl_config_from_file(argv[1]); + + td::tl::write_tl_to_file(config_td, "auto/td/telegram/TdWindowsApi.cpp", + td::tl::TlWriterWinCommon("TdApi", false, "#include \"td/telegram/TdWindowsApi.h\"\n")); + td::tl::write_tl_to_file(config_td, "auto/td/telegram/TdWindowsApi.h", + td::tl::TlWriterWinCommon("TdApi", true, "#include \"td/telegram/td_api.h\"\n")); + + return 0; +} diff --git a/td/generate/tl_writer_win.h b/td/generate/tl_writer_win.h new file mode 100644 index 00000000..7483c905 --- /dev/null +++ b/td/generate/tl_writer_win.h @@ -0,0 +1,549 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/tl/tl_writer.h" + +#include +#include +#include +#include + +namespace td { +namespace tl { + +class TlWriterWinCommon : public TL_writer { + public: + bool is_header_; + std::string prefix_; + TlWriterWinCommon(const std::string &name, bool is_header, const std::string &prefix = "") + : TL_writer(name), is_header_(is_header), prefix_(prefix) { + } + int get_max_arity(void) const override { + return 0; + } + + bool is_built_in_simple_type(const std::string &name) const override { + return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "String" || name == "Bytes"; + } + bool is_built_in_complex_type(const std::string &name) const override { + return name == "Vector"; + } + bool is_type_bare(const tl_type *t) const override { + return t->simple_constructors <= 1 || (is_built_in_simple_type(t->name) && t->name != "Bool") || + is_built_in_complex_type(t->name); + } + + std::vector get_parsers(void) const override { + return {"FromUnmanaged"}; + } + int get_parser_type(const tl_combinator *t, const std::string &name) const override { + return 0; + } + Mode get_parser_mode(int type) const override { + return All; // Server; + } + std::vector get_storers(void) const override { + return {"ToUnmanaged", "ToString"}; + } + std::vector get_additional_functions(void) const override { + return {"ToUnmanaged", "FromUnmanaged"}; + } + int get_storer_type(const tl_combinator *t, const std::string &name) const override { + return name == "ToString"; + } + Mode get_storer_mode(int type) const override { + return type <= 1 ? All : Server; + } + + std::string gen_base_tl_class_name(void) const override { + return "BaseObject"; + } + std::string gen_base_type_class_name(int arity) const override { + assert(arity == 0); + return "Object"; + } + std::string gen_base_function_class_name(void) const override { + return "Function"; + } + + static std::string to_camelCase(const std::string &name) { + return to_cCamelCase(name, false); + } + static std::string to_CamelCase(const std::string &name) { + return to_cCamelCase(name, true); + } + static std::string to_cCamelCase(const std::string &name, bool flag) { + bool next_to_upper = flag; + std::string result; + for (int i = 0; i < (int)name.size(); i++) { + if (!is_alnum(name[i])) { + next_to_upper = true; + continue; + } + if (next_to_upper) { + result += (char)to_upper(name[i]); + next_to_upper = false; + } else { + result += name[i]; + } + } + return result; + } + + std::string gen_native_field_name(std::string name) const { + for (int i = 0; i < (int)name.size(); i++) { + if (!is_alnum(name[i])) { + name[i] = '_'; + } + } + assert(name.size() > 0); + assert(name[name.size() - 1] != '_'); + return name + "_"; + } + + std::string gen_native_class_name(std::string name) const { + if (name == "Object") { + assert(0); + } + if (name == "#") { + return "int32_t"; + } + for (int i = 0; i < (int)name.size(); i++) { + if (!is_alnum(name[i])) { + name[i] = '_'; + } + } + return name; + } + + std::string gen_class_name(std::string name) const override { + if (name == "Object" || name == "#") { + assert(0); + } + return to_CamelCase(name); + } + std::string gen_field_name(std::string name) const override { + assert(name.size() > 0); + assert(is_alnum(name.back())); + return to_CamelCase(name); + } + + std::string gen_type_name(const tl_tree_type *tree_type) const override { + const tl_type *t = tree_type->type; + const std::string &name = t->name; + + if (name == "#") { + assert(0); + } + if (name == "Bool") { + return "bool"; + } + if (name == "Int32") { + return "int32"; + } + if (name == "Int53" || name == "Int64") { + return "int64"; + } + if (name == "Double") { + return "float64"; + } + if (name == "String") { + return "String^"; + } + if (name == "Bytes") { + return "Array^"; + } + + if (name == "Vector") { + assert(t->arity == 1); + assert((int)tree_type->children.size() == 1); + assert(tree_type->children[0]->get_type() == NODE_TYPE_TYPE); + const tl_tree_type *child = static_cast(tree_type->children[0]); + + return "Array<" + gen_type_name(child) + ">^"; + } + + assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name)); + + for (int i = 0; i < (int)tree_type->children.size(); i++) { + assert(tree_type->children[i]->get_type() == NODE_TYPE_NAT_CONST); + } + + return gen_main_class_name(t) + "^"; + } + std::string gen_output_begin(void) const override { + return prefix_ + + "#include \"td/utils/port/CxCli.h\"\n" + "#include \"td/tl/tl_win_object.h\"\n\n" + "namespace TdWindows {\n"; + } + std::string gen_output_end() const override { + return "}\n"; + } + + std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override { + if (!is_header_) { + return ""; + } + std::stringstream ss; + ss << (is_proxy ? "interface" : "ref") << " class " << class_name << ";\n"; + return ss.str(); + } + + std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, + bool is_proxy) const override { + if (!is_header_) { + return ""; + } + + std::stringstream ss; + ss << "\npublic " << (is_proxy ? "interface" : "ref") << " class " << class_name << (is_proxy ? "" : " sealed") + << (class_name == gen_base_tl_class_name() ? "" : " : " + base_class_name) << " {\n" + << " public:\n"; + return ss.str(); + } + std::string gen_class_end(void) const override { + return ""; + } + + std::string gen_field_definition(const std::string &class_name, const std::string &type_name, + const std::string &field_name) const override { + if (!is_header_) { + return ""; + } + auto fixed_field_name = field_name; + if (field_name == class_name) { + fixed_field_name += "Data"; + } + if (type_name.substr(0, field_name.size()) == field_name) { + auto fixed_type_name = "::TdWindows::" + type_name; + std::stringstream ss; + ss << "private:\n"; + ss << " " << fixed_type_name << " " << fixed_field_name << "Private;\n"; + ss << "public:\n"; + ss << " property " << fixed_type_name << " " << fixed_field_name << " {\n"; + ss << " " << fixed_type_name << " get() {\n"; + ss << " return " << fixed_field_name << "Private;\n"; + ss << " }\n"; + ss << " void set(" << fixed_type_name << " newValue) {\n"; + ss << " " << fixed_field_name << "Private = newValue;\n"; + ss << " }\n"; + ss << " }\n"; + return ss.str(); + } + return " property " + type_name + " " + fixed_field_name + ";\n"; + } + + std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity, + std::vector &vars, int storer_type) const override { + if (storer_type < 0) { + return ""; + } + std::stringstream ss; + ss << "\n"; + if (storer_type) { + ss << (is_header_ ? " virtual " : "") << "String^ " << (is_header_ ? "" : gen_class_name(class_name) + "::") + << "ToString()" << (is_header_ ? " override;" : " {\n return ::TdWindows::ToString(this);\n}") << "\n"; + } else { + ss << (is_header_ ? " virtual " : "") << "NativeObject^ " + << (is_header_ ? "" : gen_class_name(class_name) + "::") << "ToUnmanaged()"; + if (is_header_) { + ss << ";\n"; + } else { + ss << "{\n return REF_NEW NativeObject(::TdWindows::ToUnmanaged(this).release());\n}\n"; + } + } + return ss.str(); + } + std::string gen_store_function_end(const std::vector &vars, int storer_type) const override { + return ""; + } + + std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override { + std::stringstream ss; + ss << "\n"; + ss << (is_header_ ? " " : gen_class_name(class_name) + "::") << gen_class_name(class_name) << "("; + return ss.str(); + } + std::string gen_constructor_parameter(int field_num, const std::string &class_name, const arg &a, bool is_default) const override { + if (is_default) { + return ""; + } + std::stringstream ss; + ss << (field_num == 0 ? "" : ", "); + auto field_type = gen_field_type(a); + if (field_type.substr(0, 5) == "Array") { + ss << "CXCONST "; + } else if (field_type.substr(0, 6) != "String" && to_upper(field_type[0]) == field_type[0]) { + field_type = "::TdWindows::" + field_type; + } + ss << field_type << " " << to_camelCase(a.name); + return ss.str(); + } + std::string gen_constructor_field_init(int field_num, const std::string &class_name, const arg &a, bool is_default) const override { + if (is_default || is_header_) { + return ""; + } + + std::stringstream ss; + if (field_num == 0) { + ss << ") {\n"; + } + auto field_name = gen_field_name(a.name); + if (field_name == class_name) { + field_name += "Data"; + } + ss << " " << field_name << " = " << to_camelCase(a.name) << ";\n"; + + return ss.str(); + } + std::string gen_constructor_end(const tl_combinator *t, int fields_num, bool is_default) const override { + if (is_header_) { + return ");\n"; + } + std::stringstream ss; + if (fields_num == 0) { + ss << ") {\n"; + } + ss << "}\n"; + return ss.str(); + } + std::string gen_additional_function(const std::string &function_name, const tl_combinator *t, + bool is_function) const override { + std::stringstream ss; + if (is_header_ && function_name == "ToUnmanaged") { + ss << "};\n"; + } + ss << "\n"; + if (function_name == "ToUnmanaged") { + gen_to_unmanaged(ss, t); + } else { + gen_from_unmanaged(ss, t); + } + return ss.str(); + } + void gen_to_unmanaged(std::stringstream &ss, const tl_combinator *t) const { + auto native_class_name = gen_native_class_name(t->name); + auto class_name = gen_class_name(t->name); + ss << "td::td_api::object_ptr ToUnmanaged(" << class_name + << "^ from)"; + if (is_header_) { + ss << ";\n"; + return; + } + ss << "{\n" + << " if (!from) {\n" + << " return nullptr;\n" + << " }\n" + << " return td::td_api::make_object("; + bool is_first = true; + for (auto &it : t->args) { + if (is_first) { + is_first = false; + } else { + ss << ", "; + } + auto field_name = gen_field_name(it.name); + if (field_name == class_name) { + field_name += "Data"; + } + ss << "ToUnmanaged(from->" << field_name << ")"; + } + ss << ");\n}\n"; + } + + void gen_from_unmanaged(std::stringstream &ss, const tl_combinator *t) const { + auto native_class_name = gen_native_class_name(t->name); + auto class_name = gen_class_name(t->name); + ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << "& from)"; + if (is_header_) { + ss << ";\n"; + return; + } + ss << "{\n" + << " return REF_NEW " << class_name << "("; + bool is_first = true; + for (auto &it : t->args) { + if (is_first) { + is_first = false; + } else { + ss << ", "; + } + ss << (gen_field_type(it) == "Array^" ? "Bytes" : "") << "FromUnmanaged(from." << gen_native_field_name(it.name) << ")"; + } + ss << ");\n}\n"; + } + + std::string gen_array_type_name(const tl_tree_array *arr, const std::string &field_name) const override { + assert(0); + return std::string(); + } + std::string gen_var_type_name(void) const override { + assert(0); + return std::string(); + } + + std::string gen_int_const(const tl_tree *tree_c, const std::vector &vars) const override { + assert(0); + return std::string(); + } + + std::string gen_var_name(const var_description &desc) const override { + assert(0); + return ""; + } + std::string gen_parameter_name(int index) const override { + assert(0); + return ""; + } + + std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override { + return ""; + } + + std::string gen_vars(const tl_combinator *t, const tl_tree_type *result_type, + std::vector &vars) const override { + assert(vars.empty()); + return ""; + } + std::string gen_function_vars(const tl_combinator *t, std::vector &vars) const override { + assert(vars.empty()); + return ""; + } + std::string gen_uni(const tl_tree_type *result_type, std::vector &vars, + bool check_negative) const override { + assert(result_type->children.empty()); + return ""; + } + std::string gen_constructor_id_store(int32_t id, int storer_type) const override { + return ""; + } + std::string gen_field_fetch(int field_num, const arg &a, std::vector &vars, bool flat, + int parser_type) const override { + return ""; + // std::stringstream ss; + // ss << gen_field_name(a.name) << " = from_unmanaged(from->" << + // gen_native_field_name(a.name) << ");\n"; return ss.str(); + } + std::string gen_field_store(const arg &a, std::vector &vars, bool flat, + int storer_type) const override { + return ""; + // std::stringstream ss; + // ss << "to_unmanaged(" << gen_field_name(a.name) << ")"; + // return ss.str(); + } + std::string gen_type_fetch(const std::string &field_name, const tl_tree_type *tree_type, + const std::vector &vars, int parser_type) const override { + assert(vars.empty()); + return ""; + } + + std::string gen_type_store(const std::string &field_name, const tl_tree_type *tree_type, + const std::vector &vars, int storer_type) const override { + return ""; + } + std::string gen_var_type_fetch(const arg &a) const override { + assert(0); + return ""; + } + + std::string gen_get_id(const std::string &class_name, int32_t id, bool is_proxy) const override { + return ""; + } + + std::string gen_function_result_type(const tl_tree *result) const override { + return ""; + } + + std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity, + std::vector &vars, int parser_type) const override { + return ""; + } + std::string gen_fetch_function_end(int field_num, const std::vector &vars, + int parser_type) const override { + return ""; + } + + std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, + const tl_tree *result) const override { + return ""; + } + std::string gen_fetch_function_result_end(void) const override { + return ""; + } + std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name, + bool is_proxy) const override { + return ""; + } + std::string gen_fetch_function_result_any_end(bool is_proxy) const override { + return ""; + } + + std::string gen_fetch_switch_begin(void) const override { + return ""; + } + std::string gen_fetch_switch_case(const tl_combinator *t, int arity) const override { + return ""; + } + std::string gen_fetch_switch_end(void) const override { + return ""; + } + + std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl_type *type, + const std::string &name, int arity, bool is_function) const override { + std::stringstream ss; + if (is_header_ && function_name == "ToUnmanaged") { + ss << "};\n"; + } + if (type == nullptr) { + return ss.str(); + } + auto native_class_name = gen_native_class_name(type->name); + auto class_name = gen_class_name(type->name); + if (function_name == "ToUnmanaged") { + ss << "td::td_api::object_ptr ToUnmanaged(" << class_name + << "^ from)"; + if (is_header_) { + ss << ";\n"; + return ss.str(); + } + ss << "{\n" + << " if (!from) {\n" + << " return nullptr;\n" + << " }\n" + << " return td::td_api::move_object_as(from->ToUnmanaged()->get_object_ptr());\n}\n"; + } else { + ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << "& from)"; + if (is_header_) { + ss << ";\n"; + return ss.str(); + } + ss << "{\n"; + ss << " CallFromUnmanaged<" << class_name << "^> res;\n"; + ss << " downcast_call(from, res);\n"; + ss << " return CallFromUnmanagedRes<" << class_name << "^>::res;\n"; + ss << "}\n"; + } + return ss.str(); + } + std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type, + const std::string &class_name, int arity) const override { + return ""; + } + std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type, + const tl_combinator *t, int arity, bool is_function) const override { + return ""; + } + std::string gen_additional_proxy_function_end(const std::string &function_name, const tl_type *type, + bool is_function) const override { + return ""; + } +}; + +} // namespace tl +} // namespace td diff --git a/td/telegram/ClientWindows.cpp b/td/telegram/ClientWindows.cpp new file mode 100644 index 00000000..d4b18106 --- /dev/null +++ b/td/telegram/ClientWindows.cpp @@ -0,0 +1,95 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/TdWindowsApi.h" + +#include "td/telegram/Client.h" + +#include "td/utils/port/CxCli.h" + +namespace TdWindows { +using namespace CxCli; + +public interface class ClientResultHandler { + void OnResult(BaseObject^ object); +}; + +public ref class Client sealed { +public: + void Send(Function^ function, ClientResultHandler^ handler) { + if (function == nullptr) { + throw REF_NEW NullReferenceException("Function can't be null"); + } + + std::uint64_t queryId = Increment(currentId); + if (handler != nullptr) { + handlers[queryId] = handler; + } + td::Client::Request request; + request.id = queryId; + request.function = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); + client->send(std::move(request)); + } + + BaseObject^ Execute(Function^ function) { + if (function == nullptr) { + throw REF_NEW NullReferenceException("Function can't be null"); + } + + td::Client::Request request; + request.id = 0; + request.function = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); + return FromUnmanaged(*client->execute(std::move(request)).object); + } + + void SetUpdatesHandler(ClientResultHandler^ handler) { + handlers[0] = handler; + } + + void Run() { + while (true) { + auto response = client->receive(10.0); + if (response.object != nullptr) { + ProcessResult(response.id, FromUnmanaged(*response.object)); + + if (response.object->get_id() == td::td_api::updateAuthorizationState::ID && + static_cast(*response.object).authorization_state_->get_id() == + td::td_api::authorizationStateClosed::ID) { + break; + } + } + } + } + + static Client^ Create(ClientResultHandler^ updatesHandler) { + return REF_NEW Client(updatesHandler); + } + +private: + Client(ClientResultHandler^ updatesHandler) { + client = new td::Client(); + handlers[0] = updatesHandler; + } + + ~Client() { + delete client; + } + + std::int64_t currentId = 0; + ConcurrentDictionary handlers; + td::Client *client = nullptr; + + void ProcessResult(std::uint64_t id, BaseObject^ object) { + ClientResultHandler^ handler; + // update handler stays forever + if (id == 0 ? handlers.TryGetValue(id, handler) : handlers.TryRemove(id, handler)) { + // TODO try/catch + handler->OnResult(object); + } + } +}; + +} // namespace TdWindows diff --git a/td/telegram/LogWindows.cpp b/td/telegram/LogWindows.cpp new file mode 100644 index 00000000..b79138b3 --- /dev/null +++ b/td/telegram/LogWindows.cpp @@ -0,0 +1,27 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/Log.h" + +#include "td/utils/port/CxCli.h" + +namespace TdWindows { +using namespace CxCli; +public ref class Log sealed { +public: + static void SetFilePath(String^ filePath) { + ::td::Log::set_file_path(string_to_unmanaged(filePath)); + } + + static void SetMaxFileSize(std::int64_t maxFileSize) { + ::td::Log::set_max_file_size(maxFileSize); + } + + static void SetVerbosityLevel(int verbosityLevel) { + ::td::Log::set_verbosity_level(verbosityLevel); + } +}; +} // namespace TdWindows diff --git a/td/tl/tl_win_object.h b/td/tl/tl_win_object.h new file mode 100644 index 00000000..49a3d166 --- /dev/null +++ b/td/tl/tl_win_object.h @@ -0,0 +1,176 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/utils/misc.h" +#include "td/utils/port/CxCli.h" +#include "td/utils/tl_storers.h" + +#include "td/telegram/td_api.h" +#include "td/telegram/td_api.hpp" + +namespace TdWindows { +using namespace CxCli; + +public ref class NativeObject sealed { +public: + virtual ~NativeObject() { + if (data != nullptr) { + get_object_ptr(); + } + } +internal: + explicit NativeObject(td::td_api::BaseObject *fromData) { + data = static_cast(fromData); + } + td::td_api::object_ptr get_object_ptr() { + auto res = static_cast(data); + data = nullptr; + return td::td_api::object_ptr(res); + } + void *data; +}; + +public interface class BaseObject { +public: + virtual NativeObject^ ToUnmanaged(); +}; + +//from unmanaged +inline bool FromUnmanaged(bool val) { + return val; +} + +inline int32 FromUnmanaged(int32 val) { + return val; +} + +inline int64 FromUnmanaged(int64 val) { + return val; +} + +inline float64 FromUnmanaged(float64 val) { + return val; +} + +inline String^ FromUnmanaged(const std::string &from) { + return string_from_unmanaged(from); +} + +inline auto CLRCALL BytesFromUnmanaged(const std::string &from) { + Array^ res = REF_NEW Vector(td::narrow_cast(from.size())); + ArrayIndexType i = 0; + for (auto b : from) { + ArraySet(res, i++, b); + } + return res; +} + +template +auto CLRCALL FromUnmanaged(std::vector &vec) { + using ToT = decltype(FromUnmanaged(vec[0])); + Array^ res = REF_NEW Vector(td::narrow_cast(vec.size())); + ArrayIndexType i = 0; + for (auto &from : vec) { + ArraySet(res, i++, FromUnmanaged(from)); + } + return res; +} + +template +auto CLRCALL FromUnmanaged(td::td_api::object_ptr &from) -> decltype(FromUnmanaged(*from.get())) { + if (!from) { + return nullptr; + } + return FromUnmanaged(*from.get()); +} + +template +ref class CallFromUnmanagedRes { +public: + static property ResT res; +}; + +template +struct CallFromUnmanaged { + template + void operator()(T &val) const { + CallFromUnmanagedRes::res = FromUnmanaged(val); + } +}; + +inline BaseObject^ FromUnmanaged(td::td_api::Function &from) { + CallFromUnmanaged res; + downcast_call(from, res); + return CallFromUnmanagedRes::res; +} + +inline BaseObject^ FromUnmanaged(td::td_api::Object &from) { + CallFromUnmanaged res; + downcast_call(from, res); + return CallFromUnmanagedRes::res; +} + +// to unmanaged +inline bool ToUnmanaged(bool val) { + return val; +} +inline int32 ToUnmanaged(int32 val) { + return val; +} + +inline int64 ToUnmanaged(int64 val) { + return val; +} + +inline float64 ToUnmanaged(float64 val) { + return val; +} + +inline std::string ToUnmanaged(String ^from) { + return string_to_unmanaged(from); +} + +inline std::string ToUnmanaged(Array^ from) { + if (!from) { + return std::string(); + } + + ArrayIndexType size = ArraySize(from); + std::string res(size, '\0'); + for (ArrayIndexType i = 0; i < size; i++) { + res[i] = static_cast(ArrayGet(from, i)); + } + return res; +} + +template +auto ToUnmanaged(Array^ from) { + std::vector res; + if (from && ArraySize(from)) { + ArrayIndexType size = ArraySize(from); + res.reserve(size); + for (ArrayIndexType i = 0; i < size; i++) { + res.push_back(ToUnmanaged(ArrayGet(from, i))); + } + } + return res; +} + +inline NativeObject^ ToUnmanaged(BaseObject^ from) { + if (!from) { + return REF_NEW NativeObject(nullptr); + } + + return from->ToUnmanaged(); +} + +inline String^ ToString(BaseObject^ from) { + return string_from_unmanaged(td::td_api::to_string(ToUnmanaged(from)->get_object_ptr())); +} + +} // namespace TdWindows diff --git a/tdutils/td/utils/port/CxCli.h b/tdutils/td/utils/port/CxCli.h index 778f5cab..4fcf6396 100644 --- a/tdutils/td/utils/port/CxCli.h +++ b/tdutils/td/utils/port/CxCli.h @@ -7,6 +7,7 @@ #pragma once #include "td/utils/common.h" +#include "td/utils/port/config.h" #undef small #if TD_WINRT