Merge branch 'main' into GTK-systray

This commit is contained in:
Gary Elshaw 2024-01-07 14:58:40 +13:00 committed by GitHub
commit 8a07c316ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
965 changed files with 84416 additions and 60947 deletions

View File

@ -57,7 +57,7 @@ jobs:
get_changes any-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web third-party
get_changes our-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web
get_changes daemon CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission daemon
get_changes dist dist
get_changes dist dist release
get_changes docs docs
get_changes gtk CMakeLists.txt cmake third-party libtransmission gtk
get_changes mac CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission macosx Transmission.xcodeproj
@ -227,8 +227,8 @@ jobs:
run: |
if grep 'warning:' makelog; then exit 1; fi
macos-11:
runs-on: macos-11
macos-12:
runs-on: macos-12
needs: [ what-to-make ]
if: ${{ needs.what-to-make.outputs.make-cli == 'true' || needs.what-to-make.outputs.make-daemon == 'true' || needs.what-to-make.outputs.make-gtk == 'true' || needs.what-to-make.outputs.make-mac == 'true' || needs.what-to-make.outputs.make-qt == 'true' || needs.what-to-make.outputs.make-tests == 'true' || needs.what-to-make.outputs.make-utils == 'true' }}
steps:
@ -245,7 +245,7 @@ jobs:
run: brew install gtkmm3
- name: Get Dependencies (Qt)
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
run: brew install qt@5
run: brew install qt
- name: Get Source
uses: actions/checkout@v3
with:
@ -260,7 +260,7 @@ jobs:
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=pfx \
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt@5 \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt \
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
-DENABLE_GTK=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \
@ -336,7 +336,6 @@ jobs:
-G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=pfx \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt@5 \
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
-DENABLE_GTK=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \
@ -500,10 +499,10 @@ jobs:
name: source-tarball
path: obj/transmission*.tar.*
macos-11-from-tarball:
macos-12-from-tarball:
needs: [ make-source-tarball, what-to-make ]
if: ${{ needs.what-to-make.outputs.make-cli == 'true' || needs.what-to-make.outputs.make-daemon == 'true' || needs.what-to-make.outputs.make-gtk == 'true' || needs.what-to-make.outputs.make-mac == 'true' || needs.what-to-make.outputs.make-qt == 'true' || needs.what-to-make.outputs.make-tests == 'true' || needs.what-to-make.outputs.make-utils == 'true' }}
runs-on: macos-11
runs-on: macos-12
steps:
- name: Show Configuration
run: |
@ -518,7 +517,7 @@ jobs:
run: brew install gtkmm3
- name: Get Dependencies (Qt)
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
run: brew install qt@5
run: brew install qt
- name: Get Source
uses: actions/download-artifact@v3
with:
@ -534,7 +533,7 @@ jobs:
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=pfx \
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt@5 \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt \
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
-DENABLE_GTK=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \
@ -771,7 +770,6 @@ jobs:
-G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=pfx \
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt@5 \
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
-DENABLE_GTK=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \

View File

@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@ -76,10 +76,17 @@ jobs:
xargs -L1 ninja -C _build
- name: Initialize CodeQL
if: ${{ matrix.language == 'javascript' }}
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config-js.yml
- name: Initialize CodeQL
if: ${{ matrix.language == 'cpp' }}
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
- name: Build Project
if: ${{ matrix.language == 'cpp' }}

46
.github/workflows/update-copyright.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Update copyright year(s)
on:
schedule:
- cron: '0 3 1 1 *' # 03:00 AM on January 1
workflow_dispatch: # Allows you to run this workflow manually from the Actions tab
jobs:
update-copyright-years:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: FantasticFiasco/action-update-license-year@v3
with:
prTitle: 'chore: update copyright years'
commitTitle: 'chore: update copyright year in COPYING'
token: ${{ secrets.GITHUB_TOKEN }}
path: COPYING
transform: (?<=Copyright )(?<from>\d{4})?-?(\d{4})?
- uses: FantasticFiasco/action-update-license-year@v3
with:
commitTitle: 'chore: update copyright year in user-facing files (1)'
token: ${{ secrets.GITHUB_TOKEN }}
path: |
gtk/Application.cc
qt/LicenseDialog.ui
transform: (?<=Copyright )(?<from>\d{4})?-?(\d{4})
- uses: FantasticFiasco/action-update-license-year@v3
with:
commitTitle: 'chore: update copyright year in user-facing files (2)'
token: ${{ secrets.GITHUB_TOKEN }}
path: |
macosx/Info.plist
macosx/Info.plist.in
macosx/*/InfoPlist.strings
macosx/QuickLookPlugin/Info.plist.in
macosx/QuickLookPlugin/QuickLookPlugin-Info.plist
transform: (?<=Copyright © )(?<from>\d{4})?-?(\d{4})
- uses: FantasticFiasco/action-update-license-year@v3
with:
commitTitle: 'chore: update copyright year in cmake/transmission.rc.in'
token: ${{ secrets.GITHUB_TOKEN }}
path: cmake/Transmission.rc.in
transform: (?<from>\d{4})?-?(\d{4})(?= Transmission Project)

View File

@ -62,6 +62,7 @@ jobs:
- name: Get dependencies
run: |
set -e # abort if any command fails
sudo apt-get update
sudo apt-get install -y npm
- name: Check for style diffs
id: check-for-diffs
@ -110,9 +111,12 @@ jobs:
echo
echo Please undo your changes to these files:
git diff --exit-code --name-only --merge-base "origin/$GITHUB_BASE_REF" -- \
web/public_html/transmission-app.css \
web/public_html/transmission-app.css.LEGAL.txt \
web/public_html/transmission-app.css.map \
web/public_html/transmission-app.js \
web/public_html/transmission-app.js.map \
web/public_html/transmission-app.js.LICENSE.txt
web/public_html/transmission-app.js.LEGAL.txt \
web/public_html/transmission-app.js.map
update-generated-files:
runs-on: ubuntu-latest

6
.gitignore vendored
View File

@ -24,5 +24,11 @@ node_modules/
/third-party/suffixes_dafsa.h
/web/public_html/transmission-app.js.map
# clangd compile commands
compile_commands.json
# CLion IDE build directory
/cmake-build-*/
# CMake user presets
CMakeUserPresets.json

7
.gitmodules vendored
View File

@ -50,3 +50,10 @@
[submodule "third-party/small"]
path = third-party/small
url = https://github.com/transmission/small.git
[submodule "third-party/rapidjson"]
path = third-party/rapidjson
url = https://github.com/transmission/rapidjson.git
fetchRecurseSubmodules = false
[submodule "third-party/rpavlik-cmake-modules"]
path = third-party/rpavlik-cmake-modules
url = https://github.com/transmission/rpavlik-cmake-modules.git

View File

@ -12,72 +12,84 @@ file_filter = macosx/<lang>.lproj/Localizable.strings
source_file = macosx/en.lproj/Localizable.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-AddMagnetWindow]
file_filter = macosx/<lang>.lproj/AddMagnetWindow.strings
source_file = macosx/en.lproj/AddMagnetWindow.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-AddWindow]
file_filter = macosx/<lang>.lproj/AddWindow.strings
source_file = macosx/en.lproj/AddWindow.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-Creator]
file_filter = macosx/<lang>.lproj/Creator.strings
source_file = macosx/en.lproj/Creator.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-GlobalOptionsPopover]
file_filter = macosx/<lang>.lproj/GlobalOptionsPopover.strings
source_file = macosx/en.lproj/GlobalOptionsPopover.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-GroupRules]
file_filter = macosx/<lang>.lproj/GroupRules.strings
source_file = macosx/en.lproj/GroupRules.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-InfoActivityView]
file_filter = macosx/<lang>.lproj/InfoActivityView.strings
source_file = macosx/en.lproj/InfoActivityView.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-InfoGeneralView]
file_filter = macosx/<lang>.lproj/InfoGeneralView.strings
source_file = macosx/en.lproj/InfoGeneralView.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-InfoOptionsView]
file_filter = macosx/<lang>.lproj/InfoOptionsView.strings
source_file = macosx/en.lproj/InfoOptionsView.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-MainMenu]
file_filter = macosx/<lang>.lproj/MainMenu.strings
source_file = macosx/en.lproj/MainMenu.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-PrefsWindow]
file_filter = macosx/<lang>.lproj/PrefsWindow.strings
source_file = macosx/en.lproj/PrefsWindow.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:mac-ql]
file_filter = macosx/QuickLookPlugin/<lang>.lproj/Localizable.strings
source_file = macosx/QuickLookPlugin/en.lproj/Localizable.strings
source_lang = en
type = STRINGS
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
[o:transmissionbt:p:transmissionbt:r:qt]
file_filter = qt/translations/transmission_<lang>.ts

View File

@ -20,7 +20,13 @@ endif()
project(transmission)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(TR_THIRD_PARTY_DIR_NAME third-party)
set(TR_THIRD_PARTY_SOURCE_DIR ${PROJECT_SOURCE_DIR}/${TR_THIRD_PARTY_DIR_NAME})
set(TR_THIRD_PARTY_BINARY_DIR ${PROJECT_BINARY_DIR}/${TR_THIRD_PARTY_DIR_NAME})
list(APPEND CMAKE_MODULE_PATH
"${PROJECT_SOURCE_DIR}/cmake"
"${TR_THIRD_PARTY_SOURCE_DIR}/rpavlik-cmake-modules")
set(CMAKE_MACOSX_RPATH ON)
@ -40,7 +46,7 @@ set(GIOMM_MINIMUM 2.26.0)
set(GLIBMM_MINIMUM 2.60.0)
set(GTKMM3_MINIMUM 3.24.0)
set(GTKMM4_MINIMUM 4.11.1)
set(OPENSSL_MINIMUM 0.9.7)
set(OPENSSL_MINIMUM 1.1.0)
set(MBEDTLS_MINIMUM 1.3)
set(NPM_MINIMUM 8.1.307) # Node.js 14
set(PSL_MINIMUM 0.21.1)
@ -142,9 +148,16 @@ else()
endif()
string(APPEND TR_PEER_ID_PREFIX "-")
set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION")
set(TR_VCS_REVISION_FILE "${PROJECT_SOURCE_DIR}/REVISION")
if(EXISTS ${CMAKE_SOURCE_DIR}/.git)
## Compiler standard version
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
find_package(Git)
endif()
@ -153,11 +166,9 @@ if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{GIT_COMMIT}" STREQUAL "")
elseif(NOT "$ENV{TEAMCITY_PROJECT_NAME}" STREQUAL "" AND NOT "$ENV{BUILD_VCS_NUMBER}" STREQUAL "")
set(TR_VCS_REVISION "$ENV{BUILD_VCS_NUMBER}")
elseif(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE TR_VCS_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE)
include(GetGitRevisionDescription)
get_git_head_revision(TR_REFSPEC TR_VCS_REVISION)
unset(TR_REFSPEC)
endif()
if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}")
@ -204,9 +215,11 @@ if(WIN32)
endforeach()
endif()
set(CMAKE_FOLDER "third-party")
set(CMAKE_FOLDER "${TR_THIRD_PARTY_DIR_NAME}")
find_package(FastFloat)
find_package(Fmt)
find_package(RapidJSON)
find_package(Small)
find_package(UtfCpp)
find_package(WideInteger)
@ -215,11 +228,6 @@ find_package(Threads)
find_package(PkgConfig QUIET)
find_package(CURL ${CURL_MINIMUM} REQUIRED)
if(NOT TARGET CURL::libcurl)
add_library(CURL::libcurl INTERFACE IMPORTED)
target_link_libraries(CURL::libcurl INTERFACE ${CURL_LIBRARIES})
target_include_directories(CURL::libcurl INTERFACE ${CURL_INCLUDE_DIRS})
endif()
if(ENABLE_DEPRECATED STREQUAL "AUTO")
if(DEFINED ENV{CI})
@ -456,8 +464,6 @@ if(ENABLE_MAC)
tr_fixup_auto_option(ENABLE_MAC MAC_FOUND MAC_IS_REQUIRED)
endif()
set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/third-party)
if(WIN32 AND NOT MINGW)
set(DEFLATE_LIB_NAME deflatestatic)
else()
@ -507,8 +513,7 @@ target_compile_definitions(miniupnpc::libminiupnpc
SYSTEM_MINIUPNP
$<$<VERSION_LESS:${MINIUPNPC_VERSION},1.7>:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}>) # API version macro was only added in 1.7
add_subdirectory(third-party/jsonsl)
add_subdirectory(third-party/wildmat)
add_subdirectory(${TR_THIRD_PARTY_SOURCE_DIR}/wildmat)
tr_add_external_auto_library(DHT dht dht
TARGET dht::dht)
@ -530,7 +535,7 @@ tr_add_external_auto_library(B64 libb64 b64
CMAKE_ARGS
-DLIBB64_SHARED:BOOL=OFF)
set(TR_WEB_ASSETS ${CMAKE_SOURCE_DIR}/web/public_html)
set(TR_WEB_ASSETS ${PROJECT_SOURCE_DIR}/web/public_html)
if(NOT ${REBUILD_WEB} STREQUAL "OFF")
find_program(NPM npm)
if ("${NPM}" STREQUAL "NPM-NOTFOUND")
@ -599,13 +604,6 @@ endif()
unset(CMAKE_FOLDER)
## Compiler standard version
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
### Compiler Warnings
set(C_WARNING_FLAGS)
@ -830,7 +828,7 @@ if(NOT TR_STABLE_RELEASE AND NOT "${TR_VCS_REVISION}" STREQUAL "")
string(APPEND CPACK_SOURCE_PACKAGE_FILE_NAME "+r${TR_VCS_REVISION}")
endif()
list(APPEND CPACK_SOURCE_IGNORE_FILES
"${CMAKE_BINARY_DIR}"
"${PROJECT_BINARY_DIR}"
"[.]git"
"node_modules")
@ -839,17 +837,17 @@ list(APPEND CPACK_SOURCE_IGNORE_FILES
if(GIT_FOUND)
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse --show-toplevel
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE TR_GIT_ROOT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(TR_GIT_ROOT AND IS_DIRECTORY "${TR_GIT_ROOT}/.git")
configure_file("${CMAKE_SOURCE_DIR}/extras/pre-commit" "${TR_GIT_ROOT}/.git/hooks/pre-commit" COPYONLY)
configure_file("${PROJECT_SOURCE_DIR}/extras/pre-commit" "${TR_GIT_ROOT}/.git/hooks/pre-commit" COPYONLY)
add_custom_target(check-format
COMMAND "${CMAKE_SOURCE_DIR}/code_style.sh" --check
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
COMMAND "${PROJECT_SOURCE_DIR}/code_style.sh" --check
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
add_custom_target(format
COMMAND "${CMAKE_SOURCE_DIR}/code_style.sh"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
COMMAND "${PROJECT_SOURCE_DIR}/code_style.sh"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
set_property(
TARGET check-format format
PROPERTY FOLDER "utility")

View File

@ -1,4 +1,4 @@
Copyright 2005-2023. All code is copyrighted by the respective authors.
Copyright 2005-2024. All code is copyrighted by the respective authors.
Transmission can be redistributed and/or modified under the terms of
the GNU GPLv2 (http://www.gnu.org/licenses/license-list.html#GPLv2),

View File

@ -31,15 +31,16 @@ For a more detailed description, and dependencies, visit [How to Build Transmiss
### Building a Transmission release from the command line
$ tar xf transmission-3.00.tar.xz
$ cd transmission-3.00
$ mkdir build
$ cd build
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
$ make
$ sudo make install
```bash
$ tar xf transmission-4.0.4.tar.xz
$ cd transmission-4.0.4
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
$ cd build
$ cmake --build .
$ sudo cmake --install .
```
### Building Transmission from the nightly builds
@ -49,29 +50,28 @@ If you're new to building programs from source code, this is typically easier th
### Building Transmission from Git (first time)
$ git clone https://github.com/transmission/transmission Transmission
$ cd Transmission
$ git submodule update --init --recursive
$ mkdir build
$ cd build
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
$ make
$ sudo make install
```bash
$ git clone --recurse-submodules https://github.com/transmission/transmission Transmission
$ cd Transmission
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
$ cd build
$ cmake --build .
$ sudo cmake --install .
```
### Building Transmission from Git (updating)
$ cd Transmission/build
$ make clean
$ git submodule foreach --recursive git clean -xfd
$ git pull --rebase --prune
$ git submodule update --init --recursive
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
$ make
$ sudo make install
```bash
$ cd Transmission/build
$ cmake --build . -t clean
$ git submodule foreach --recursive git clean -xfd
$ git pull --rebase --prune
$ git submodule update --init --recursive
$ cmake --build .
$ sudo cmake --install .
```
## Contributing

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// This file Copyright © 2006-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
@ -19,57 +19,36 @@
#include <libtransmission/file.h>
#include <libtransmission/tr-getopt.h>
#include <libtransmission/utils.h> // _()
#include <libtransmission/values.h>
#include <libtransmission/variant.h>
#include <libtransmission/version.h>
#include <libtransmission/web-utils.h>
#include <libtransmission/web.h> // tr_sessionFetch()
using namespace std::chrono_literals;
using namespace libtransmission::Values;
/***
****
***/
static auto constexpr MemK = size_t{ 1024 };
static char constexpr MemKStr[] = "KiB";
static char constexpr MemMStr[] = "MiB";
static char constexpr MemGStr[] = "GiB";
static char constexpr MemTStr[] = "TiB";
static auto constexpr DiskK = size_t{ 1000 };
static char constexpr DiskKStr[] = "kB";
static char constexpr DiskMStr[] = "MB";
static char constexpr DiskGStr[] = "GB";
static char constexpr DiskTStr[] = "TB";
static auto constexpr SpeedK = size_t{ 1000 };
#define SPEED_K_STR "kB/s"
static char constexpr SpeedKStr[] = SPEED_K_STR;
static char constexpr SpeedMStr[] = "MB/s";
static char constexpr SpeedGStr[] = "GB/s";
static char constexpr SpeedTStr[] = "TB/s";
/***
****
***/
namespace
{
auto constexpr LineWidth = int{ 80 };
static auto constexpr LineWidth = int{ 80 };
static char constexpr MyConfigName[] = "transmission";
static char constexpr MyReadableName[] = "transmission-cli";
static char constexpr Usage
char constexpr MyConfigName[] = "transmission";
char constexpr MyReadableName[] = "transmission-cli";
char constexpr Usage
[] = "A fast and easy BitTorrent client\n"
"\n"
"Usage: transmission-cli [options] <file|url|magnet>";
static bool showVersion = false;
static bool verify = false;
static sig_atomic_t gotsig = false;
static sig_atomic_t manualUpdate = false;
bool showVersion = false;
bool verify = false;
sig_atomic_t gotsig = false;
sig_atomic_t manualUpdate = false;
static char const* torrentPath = nullptr;
char const* torrentPath = nullptr;
static auto constexpr Options = std::array<tr_option, 20>{
auto constexpr Options = std::array<tr_option, 20>{
{ { 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'd', "downlimit", "Set max download speed in " SPEED_K_STR, "d", true, "<speed>" },
@ -99,11 +78,11 @@ static auto constexpr Options = std::array<tr_option, 20>{
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static int parseCommandLine(tr_variant*, int argc, char const** argv);
int parseCommandLine(tr_variant*, int argc, char const** argv);
static void sigHandler(int signal);
void sigHandler(int signal);
static std::string tr_strlratio(double ratio)
[[nodiscard]] std::string tr_strlratio(double ratio)
{
if (static_cast<int>(ratio) == TR_RATIO_NA)
{
@ -117,27 +96,27 @@ static std::string tr_strlratio(double ratio)
if (ratio < 10.0)
{
return fmt::format(FMT_STRING("{:.2f}"), ratio);
return fmt::format("{:.2f}", ratio);
}
if (ratio < 100.0)
{
return fmt::format(FMT_STRING("{:.1f}"), ratio);
return fmt::format("{:.1f}", ratio);
}
return fmt::format(FMT_STRING("{:.0f}"), ratio);
return fmt::format("{:.0f}", ratio);
}
static bool waitingOnWeb;
bool waitingOnWeb;
static void onTorrentFileDownloaded(tr_web::FetchResponse const& response)
void onTorrentFileDownloaded(tr_web::FetchResponse const& response)
{
auto* ctor = static_cast<tr_ctor*>(response.user_data);
tr_ctorSetMetainfo(ctor, std::data(response.body), std::size(response.body), nullptr);
waitingOnWeb = false;
}
static std::string getStatusStr(tr_stat const* st)
[[nodiscard]] std::string getStatusStr(tr_stat const* st)
{
if (st->activity == TR_STATUS_CHECK_WAIT)
{
@ -147,7 +126,7 @@ static std::string getStatusStr(tr_stat const* st)
if (st->activity == TR_STATUS_CHECK)
{
return fmt::format(
FMT_STRING("Verifying local files ({:.2f}%, {:.2f}% valid)"),
"Verifying local files ({:.2f}%, {:.2f}% valid)",
tr_truncd(100 * st->recheckProgress, 2),
tr_truncd(100 * st->percentDone, 2));
}
@ -155,30 +134,30 @@ static std::string getStatusStr(tr_stat const* st)
if (st->activity == TR_STATUS_DOWNLOAD)
{
return fmt::format(
FMT_STRING("Progress: {:.1f}%, dl from {:d} of {:d} peers ({:s}), ul to {:d} ({:s}) [{:s}]"),
"Progress: {:.1f}%, dl from {:d} of {:d} peers ({:s}), ul to {:d} ({:s}) [{:s}]",
tr_truncd(100 * st->percentDone, 1),
st->peersSendingToUs,
st->peersConnected,
tr_formatter_speed_KBps(st->pieceDownloadSpeed_KBps),
Speed{ st->pieceDownloadSpeed_KBps, Speed::Units::KByps }.to_string(),
st->peersGettingFromUs,
tr_formatter_speed_KBps(st->pieceUploadSpeed_KBps),
Speed{ st->pieceUploadSpeed_KBps, Speed::Units::KByps }.to_string(),
tr_strlratio(st->ratio));
}
if (st->activity == TR_STATUS_SEED)
{
return fmt::format(
FMT_STRING("Seeding, uploading to {:d} of {:d} peer(s), {:s} [{:s}]"),
"Seeding, uploading to {:d} of {:d} peer(s), {:s} [{:s}]",
st->peersGettingFromUs,
st->peersConnected,
tr_formatter_speed_KBps(st->pieceUploadSpeed_KBps),
Speed{ st->pieceUploadSpeed_KBps, Speed::Units::KByps }.to_string(),
tr_strlratio(st->ratio));
}
return "";
}
static std::string getConfigDir(int argc, char const** argv)
[[nodiscard]] std::string getConfigDir(int argc, char const** argv)
{
int c;
char const* my_optarg;
@ -198,16 +177,139 @@ static std::string getConfigDir(int argc, char const** argv)
return tr_getDefaultConfigDir(MyConfigName);
}
// ---
int parseCommandLine(tr_variant* d, int argc, char const** argv)
{
int c;
char const* my_optarg;
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
{
switch (c)
{
case 'b':
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, true);
break;
case 'B':
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, false);
break;
case 'd':
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, atoi(my_optarg));
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, true);
break;
case 'D':
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
break;
case 'f':
tr_variantDictAddStr(d, TR_KEY_script_torrent_done_filename, my_optarg);
tr_variantDictAddBool(d, TR_KEY_script_torrent_done_enabled, true);
break;
case 'g': /* handled above */
break;
case 'm':
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, true);
break;
case 'M':
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, false);
break;
case 'p':
tr_variantDictAddInt(d, TR_KEY_peer_port, atoi(my_optarg));
break;
case 't':
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
break;
case 'u':
tr_variantDictAddInt(d, TR_KEY_speed_limit_up, atoi(my_optarg));
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, true);
break;
case 'U':
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, false);
break;
case 'v':
verify = true;
break;
case 'V':
showVersion = true;
break;
case 'w':
tr_variantDictAddStr(d, TR_KEY_download_dir, my_optarg);
break;
case 910:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
break;
case 911:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
break;
case 912:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_CLEAR_PREFERRED);
break;
case 500:
tr_variantDictAddBool(d, TR_KEY_sequentialDownload, true);
break;
case TR_OPT_UNK:
if (torrentPath == nullptr)
{
torrentPath = my_optarg;
}
break;
default:
return 1;
}
}
return 0;
}
void sigHandler(int signal)
{
switch (signal)
{
case SIGINT:
gotsig = true;
break;
#ifndef _WIN32
case SIGHUP:
manualUpdate = true;
break;
#endif
default:
break;
}
}
} // namespace
int tr_main(int argc, char* argv[])
{
auto const init_mgr = tr_lib_init();
tr_locale_set_global("");
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);
tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr);
printf("%s %s\n", MyReadableName, LONG_VERSION_STRING);
/* user needs to pass in at least one argument */
@ -219,7 +321,7 @@ int tr_main(int argc, char* argv[])
/* load the defaults from config file + libtransmission defaults */
auto const config_dir = getConfigDir(argc, (char const**)argv);
auto settings = tr_sessionLoadSettings(config_dir.c_str(), MyConfigName);
auto settings = tr_sessionLoadSettings(nullptr, config_dir.c_str(), MyConfigName);
/* the command line overrides defaults */
if (parseCommandLine(&settings, argc, (char const**)argv) != 0)
@ -245,12 +347,14 @@ int tr_main(int argc, char* argv[])
if (!tr_sys_path_exists(sz_download_dir))
{
tr_error* error = nullptr;
if (!tr_sys_dir_create(sz_download_dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error))
if (auto error = tr_error{}; !tr_sys_dir_create(sz_download_dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error) && error)
{
fprintf(stderr, "Unable to create download directory \"%s\": %s\n", sz_download_dir.c_str(), error->message);
tr_error_free(error);
auto const errmsg = fmt::format(
"Couldn't create '{path}': {error} ({error_code})",
fmt::arg("path", sz_download_dir),
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code()));
fmt::print(stderr, "{:s}\n", errmsg);
return EXIT_FAILURE;
}
}
@ -360,132 +464,3 @@ int tr_main(int argc, char* argv[])
tr_sessionClose(h);
return EXIT_SUCCESS;
}
/***
****
****
***/
static int parseCommandLine(tr_variant* d, int argc, char const** argv)
{
int c;
char const* my_optarg;
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
{
switch (c)
{
case 'b':
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, true);
break;
case 'B':
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, false);
break;
case 'd':
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, atoi(my_optarg));
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, true);
break;
case 'D':
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
break;
case 'f':
tr_variantDictAddStr(d, TR_KEY_script_torrent_done_filename, my_optarg);
tr_variantDictAddBool(d, TR_KEY_script_torrent_done_enabled, true);
break;
case 'g': /* handled above */
break;
case 'm':
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, true);
break;
case 'M':
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, false);
break;
case 'p':
tr_variantDictAddInt(d, TR_KEY_peer_port, atoi(my_optarg));
break;
case 't':
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
break;
case 'u':
tr_variantDictAddInt(d, TR_KEY_speed_limit_up, atoi(my_optarg));
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, true);
break;
case 'U':
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, false);
break;
case 'v':
verify = true;
break;
case 'V':
showVersion = true;
break;
case 'w':
tr_variantDictAddStr(d, TR_KEY_download_dir, my_optarg);
break;
case 910:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
break;
case 911:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
break;
case 912:
tr_variantDictAddInt(d, TR_KEY_encryption, TR_CLEAR_PREFERRED);
break;
case 500:
tr_variantDictAddBool(d, TR_KEY_sequentialDownload, true);
break;
case TR_OPT_UNK:
if (torrentPath == nullptr)
{
torrentPath = my_optarg;
}
break;
default:
return 1;
}
}
return 0;
}
static void sigHandler(int signal)
{
switch (signal)
{
case SIGINT:
gotsig = true;
break;
#ifndef _WIN32
case SIGHUP:
manualUpdate = true;
break;
#endif
default:
break;
}
}

View File

@ -2,4 +2,4 @@ add_library(FastFloat::fast_float INTERFACE IMPORTED)
target_include_directories(FastFloat::fast_float
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../third-party/fast_float/include)
${TR_THIRD_PARTY_SOURCE_DIR}/fast_float/include)

View File

@ -2,7 +2,7 @@ add_library(fmt::fmt-header-only INTERFACE IMPORTED)
target_include_directories(fmt::fmt-header-only
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../third-party/fmt/include)
${TR_THIRD_PARTY_SOURCE_DIR}/fmt/include)
target_compile_definitions(fmt::fmt-header-only
INTERFACE

View File

@ -0,0 +1,9 @@
add_library(RapidJSON INTERFACE IMPORTED)
target_include_directories(RapidJSON
INTERFACE
${TR_THIRD_PARTY_SOURCE_DIR}/rapidjson/include)
target_compile_definitions(RapidJSON
INTERFACE
RAPIDJSON_HAS_STDSTRING=1)

View File

@ -2,10 +2,8 @@ add_library(small::small INTERFACE IMPORTED)
target_include_directories(small::small
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../third-party/small/include)
${TR_THIRD_PARTY_SOURCE_DIR}/small/include)
target_compile_definitions(fmt::fmt-header-only
target_compile_definitions(small::small
INTERFACE
SMALL_DISABLE_EXCEPTIONS=1)

View File

@ -2,4 +2,4 @@ add_library(utf8::cpp INTERFACE IMPORTED)
target_include_directories(utf8::cpp
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../third-party/utfcpp/source)
${TR_THIRD_PARTY_SOURCE_DIR}/utfcpp/source)

View File

@ -2,4 +2,4 @@ add_library(WideInteger::WideInteger INTERFACE IMPORTED)
target_include_directories(WideInteger::WideInteger
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../third-party/wide-integer)
${TR_THIRD_PARTY_SOURCE_DIR}/wide-integer)

View File

@ -88,10 +88,10 @@ macro(tr_eval SCRIPT)
string(SHA1 _TR_EVAL_TMP_FILE "${_TR_EVAL_SCRIPT}")
string(SUBSTRING "${_TR_EVAL_TMP_FILE}" 0 10 _TR_EVAL_TMP_FILE)
set(_TR_EVAL_TMP_FILE "${CMAKE_BINARY_DIR}/.tr-cache/tr_eval.${_TR_EVAL_TMP_FILE}.cmake")
set(_TR_EVAL_TMP_FILE "${PROJECT_BINARY_DIR}/.tr-cache/tr_eval.${_TR_EVAL_TMP_FILE}.cmake")
if(NOT EXISTS "${_TR_EVAL_TMP_FILE}")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/.tr-cache")
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/.tr-cache")
file(WRITE "${_TR_EVAL_TMP_FILE}" "${_TR_EVAL_SCRIPT}")
endif()
@ -145,10 +145,10 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME)
set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3} CACHE INTERNAL "")
endif()
endforeach()
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/${DIRNAME}" "${CMAKE_BINARY_DIR}/third-party/${DIRNAME}.bld")
add_subdirectory("${TR_THIRD_PARTY_SOURCE_DIR}/${DIRNAME}" "${TR_THIRD_PARTY_BINARY_DIR}/${DIRNAME}.bld")
else()
set(${ID}_UPSTREAM_TARGET ${LIBNAME})
set(${ID}_PREFIX "${CMAKE_BINARY_DIR}/third-party/${DIRNAME}.bld/pfx")
set(${ID}_PREFIX "${TR_THIRD_PARTY_BINARY_DIR}/${DIRNAME}.bld/pfx")
set(${ID}_INCLUDE_DIR "${${ID}_PREFIX}/include"
CACHE INTERNAL "")
@ -169,8 +169,8 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME)
ExternalProject_Add(
${${ID}_UPSTREAM_TARGET}
PREFIX "${CMAKE_BINARY_DIR}/third-party/${DIRNAME}.bld"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/${DIRNAME}"
PREFIX "${TR_THIRD_PARTY_BINARY_DIR}/${DIRNAME}.bld"
SOURCE_DIR "${TR_THIRD_PARTY_SOURCE_DIR}/${DIRNAME}"
INSTALL_DIR "${${ID}_PREFIX}"
CMAKE_ARGS
-Wno-dev # We don't want to be warned over unused variables
@ -188,7 +188,7 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME)
${_TAEAL_ARG_CMAKE_ARGS}
BUILD_BYPRODUCTS "${${ID}_LIBRARY}")
set_property(TARGET ${${ID}_UPSTREAM_TARGET} PROPERTY FOLDER "third-party")
set_property(TARGET ${${ID}_UPSTREAM_TARGET} PROPERTY FOLDER "${TR_THIRD_PARTY_DIR_NAME}")
# Imported target (below) requires include directories to be present at configuration time
file(MAKE_DIRECTORY ${${ID}_INCLUDE_DIRS})
@ -275,7 +275,7 @@ function(tr_win32_app_info TGT DESCR INTNAME ORIGFNAME)
set(TR_MAIN_ICON "${ARGN}")
endif()
configure_file("${CMAKE_SOURCE_DIR}/cmake/Transmission.rc.in" "${INTNAME}-app-info.rc")
configure_file("${PROJECT_SOURCE_DIR}/cmake/Transmission.rc.in" "${INTNAME}-app-info.rc")
target_sources(${TGT}
PRIVATE
@ -437,7 +437,7 @@ function(tr_gettext_msgfmt TGT OUTPUT_FILE INPUT_FILE)
add_custom_command(
OUTPUT ${OUTPUT_FILE}
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${MODE_ARG} -d ${CMAKE_SOURCE_DIR}/po --template ${INPUT_FILE} -o ${OUTPUT_FILE}
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${MODE_ARG} -d ${PROJECT_SOURCE_DIR}/po --template ${INPUT_FILE} -o ${OUTPUT_FILE}
DEPENDS ${INPUT_FILE}
VERBATIM)
@ -471,7 +471,7 @@ function(tr_wrap_idl TGT INPUT_FILE OUTPUT_FILE_BASE)
DEPENDS ${INPUT_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
configure_file("${CMAKE_SOURCE_DIR}/cmake/Transmission.tlb.rc.in" ${OUTPUT_FILE_BASE}.tlb.rc)
configure_file("${PROJECT_SOURCE_DIR}/cmake/Transmission.tlb.rc.in" ${OUTPUT_FILE_BASE}.tlb.rc)
target_sources(${TGT}
PRIVATE

View File

@ -32,7 +32,7 @@ BEGIN
VALUE "FileDescription", "${TR_FILE_DESCRIPTION}"
VALUE "FileVersion", LONG_VERSION_STRING
VALUE "InternalName", "${TR_INTERNAL_NAME}"
VALUE "LegalCopyright", "2005-2022 Transmission Project"
VALUE "LegalCopyright", "2005-2024 Transmission Project"
VALUE "OriginalFilename", "${TR_ORIGINAL_FILENAME}"
VALUE "ProductName", "Transmission"
VALUE "ProductVersion", LONG_VERSION_STRING

View File

@ -52,4 +52,10 @@ foreach(P daemon)
FILES ${TR_NAME}-${P}.1
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()
if (WITH_SYSTEMD)
install(
FILES ${TR_NAME}-${P}.service
DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/system)
endif()
endforeach()

View File

@ -1,4 +1,4 @@
// This file Copyright © 2015-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -24,9 +24,9 @@
#include "daemon.h"
static void set_system_error(tr_error** error, int code, std::string_view message)
static void set_system_error(tr_error& error, int code, std::string_view message)
{
tr_error_set(error, code, fmt::format(FMT_STRING("{:s}: {:s} ({:d}"), message, tr_strerror(code), code));
error.set(code, fmt::format("{:s}: {:s} ({:d}", message, tr_strerror(code), code));
}
#ifdef HAVE_SYS_SIGNALFD_H
@ -131,7 +131,7 @@ void tr_daemon::cleanup_signals(struct event* sig_ev) const
#endif /* HAVE_SYS_SIGNALFD_H */
}
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error** error)
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error& error)
{
*exit_code = 1;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2015-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -23,27 +23,29 @@
#define SERVICE_CONTROL_PRESHUTDOWN 0x0000000F
#endif
static LPCWSTR const service_name = L"TransmissionDaemon";
namespace
{
LPCWSTR constexpr service_name = L"TransmissionDaemon";
// If we can get rid of this global variable...
static tr_daemon* daemon;
// ...these becomes a good candidates for being converted to 'class tr_daemon' members.
static SERVICE_STATUS_HANDLE status_handle = nullptr;
static DWORD current_state = SERVICE_STOPPED;
static HANDLE service_thread = nullptr;
static HANDLE service_stop_thread = nullptr;
SERVICE_STATUS_HANDLE status_handle = nullptr;
DWORD current_state = SERVICE_STOPPED;
HANDLE service_thread = nullptr;
HANDLE service_stop_thread = nullptr;
static void set_system_error(tr_error** error, DWORD code, char const* message)
void set_system_error(tr_error& error, DWORD code, char const* message)
{
auto const system_message = tr_win32_format_message(code);
tr_error_set(error, code, fmt::format(FMT_STRING("{:s} ({:#08x}): {:s})"), message, code, system_message));
error.set(code, fmt::format("{:s} ({:#08x}): {:s})", message, code, system_message));
}
static void do_log_system_error(char const* file, int line, tr_log_level level, DWORD code, char const* message)
void do_log_system_error(char const* file, int line, tr_log_level level, DWORD code, char const* message)
{
auto const system_message = tr_win32_format_message(code);
tr_logAddMessage(file, line, level, "tr_daemon", fmt::format("[tr_daemon] {} ({:#x}): {}", message, code, system_message));
tr_logAddMessage(file, line, level, fmt::format("{} ({:#x}): {}", message, code, system_message), "tr_daemon");
}
#define log_system_error(level, code, message) \
@ -57,13 +59,13 @@ static void do_log_system_error(char const* file, int line, tr_log_level level,
} \
} while (0)
static BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
{
daemon->stop();
return TRUE;
}
static void update_service_status(
void update_service_status(
DWORD new_state,
DWORD win32_exit_code,
DWORD service_specific_exit_code,
@ -91,7 +93,7 @@ static void update_service_status(
}
}
static unsigned int __stdcall service_stop_thread_main(void* param)
unsigned int __stdcall service_stop_thread_main(void* param)
{
daemon->stop();
@ -107,7 +109,7 @@ static unsigned int __stdcall service_stop_thread_main(void* param)
return 0;
}
static void stop_service(void)
void stop_service(void)
{
if (service_stop_thread != nullptr)
{
@ -128,7 +130,7 @@ static void stop_service(void)
}
}
static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/, LPVOID /*event_data*/, LPVOID /*context*/)
DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/, LPVOID /*event_data*/, LPVOID /*context*/)
{
switch (control_code)
{
@ -150,12 +152,12 @@ static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/
return ERROR_CALL_NOT_IMPLEMENTED;
}
static unsigned int __stdcall service_thread_main(void* /*context*/)
unsigned int __stdcall service_thread_main(void* /*context*/)
{
return daemon->start(false);
}
static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
{
status_handle = RegisterServiceCtrlHandlerExW(service_name, &handle_service_ctrl, nullptr);
@ -199,6 +201,7 @@ static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
update_service_status(SERVICE_STOPPED, NO_ERROR, exit_code, 0, 0);
}
} // namespace
bool tr_daemon::setup_signals([[maybe_unused]] struct event*& sig_ev)
{
@ -209,7 +212,7 @@ void tr_daemon::cleanup_signals([[maybe_unused]] struct event* sig_ev) const
{
}
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error** error)
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error& error)
{
daemon = this;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -6,7 +6,6 @@
#include <array>
#include <cerrno>
#include <cstdio> /* printf */
#include <cstdlib> /* atoi */
#include <iostream>
#include <iterator> /* std::back_inserter */
#include <memory>
@ -33,6 +32,7 @@
#include <libtransmission/error.h>
#include <libtransmission/file.h>
#include <libtransmission/log.h>
#include <libtransmission/quark.h>
#include <libtransmission/timer-ev.h>
#include <libtransmission/tr-getopt.h>
#include <libtransmission/tr-strbuf.h>
@ -62,39 +62,21 @@ struct tr_torrent;
using namespace std::literals;
using libtransmission::Watchdir;
static char constexpr MyName[] = "transmission-daemon";
static char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
" https://transmissionbt.com/\n"
"A fast and easy BitTorrent client\n"
"\n"
"transmission-daemon is a headless Transmission session that can be\n"
"controlled via transmission-qt, transmission-remote, or its web interface.\n"
"\n"
"Usage: transmission-daemon [options]";
namespace
{
char constexpr MyName[] = "transmission-daemon";
char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
" https://transmissionbt.com/\n"
"A fast and easy BitTorrent client\n"
"\n"
"transmission-daemon is a headless Transmission session that can be\n"
"controlled via transmission-qt, transmission-remote, or its web interface.\n"
"\n"
"Usage: transmission-daemon [options]";
static auto constexpr MemK = size_t{ 1024 };
static char constexpr MemKStr[] = "KiB";
static char constexpr MemMStr[] = "MiB";
static char constexpr MemGStr[] = "GiB";
static char constexpr MemTStr[] = "TiB";
// --- Config File
static auto constexpr DiskK = size_t{ 1000 };
static char constexpr DiskKStr[] = "kB";
static char constexpr DiskMStr[] = "MB";
static char constexpr DiskGStr[] = "GB";
static char constexpr DiskTStr[] = "TB";
static auto constexpr SpeedK = size_t{ 1000 };
static char constexpr SpeedKStr[] = "kB/s";
static char constexpr SpeedMStr[] = "MB/s";
static char constexpr SpeedGStr[] = "GB/s";
static char constexpr SpeedTStr[] = "TB/s";
/***
**** Config File
***/
static auto constexpr Options = std::array<tr_option, 45>{
auto constexpr Options = std::array<tr_option, 45>{
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
@ -162,35 +144,7 @@ static auto constexpr Options = std::array<tr_option, 45>{
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
bool tr_daemon::reopen_log_file(char const* filename)
{
tr_error* error = nullptr;
tr_sys_file_t const old_log_file = logfile_;
tr_sys_file_t const new_log_file = tr_sys_file_open(
filename,
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_APPEND,
0666,
&error);
if (new_log_file == TR_BAD_SYS_FILE)
{
fprintf(stderr, "Couldn't (re)open log file \"%s\": %s\n", filename, error->message);
tr_error_free(error);
return false;
}
logfile_ = new_log_file;
logfile_flush_ = tr_sys_file_flush_possible(logfile_);
if (old_log_file != TR_BAD_SYS_FILE)
{
tr_sys_file_close(old_log_file);
}
return true;
}
static std::string getConfigDir(int argc, char const* const* argv)
[[nodiscard]] std::string getConfigDir(int argc, char const* const* argv)
{
int c;
char const* optstr;
@ -209,7 +163,7 @@ static std::string getConfigDir(int argc, char const* const* argv)
return tr_getDefaultConfigDir(MyName);
}
static auto onFileAdded(tr_session const* session, std::string_view dirname, std::string_view basename)
auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view basename)
{
auto const lowercase = tr_strlower(basename);
auto const is_torrent = tr_strv_ends_with(lowercase, ".torrent"sv);
@ -235,15 +189,14 @@ static auto onFileAdded(tr_session const* session, std::string_view dirname, std
else // is_magnet
{
auto content = std::vector<char>{};
tr_error* error = nullptr;
auto error = tr_error{};
if (!tr_file_read(filename, content, &error))
{
tr_logAddWarn(fmt::format(
_("Couldn't read '{path}': {error} ({error_code})"),
fmt::arg("path", basename),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
tr_error_free(error);
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code())));
retry = true;
}
else
@ -274,18 +227,15 @@ static auto onFileAdded(tr_session const* session, std::string_view dirname, std
if (test && trash)
{
tr_error* error = nullptr;
tr_logAddInfo(fmt::format(_("Removing torrent file '{path}'"), fmt::arg("path", basename)));
if (!tr_sys_path_remove(filename, &error))
if (auto error = tr_error{}; !tr_sys_path_remove(filename, &error))
{
tr_logAddError(fmt::format(
_("Couldn't remove '{path}': {error} ({error_code})"),
fmt::arg("path", basename),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
tr_error_free(error);
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code())));
}
}
else
@ -298,7 +248,7 @@ static auto onFileAdded(tr_session const* session, std::string_view dirname, std
return Watchdir::Action::Done;
}
static char const* levelName(tr_log_level level)
[[nodiscard]] constexpr char const* levelName(tr_log_level level)
{
switch (level)
{
@ -317,7 +267,7 @@ static char const* levelName(tr_log_level level)
}
}
static void printMessage(
void printMessage(
tr_sys_file_t file,
tr_log_level level,
std::string_view name,
@ -379,7 +329,7 @@ static void printMessage(
#endif
}
static void pumpLogMessages(tr_sys_file_t file, bool flush)
void pumpLogMessages(tr_sys_file_t file, bool flush)
{
tr_log_message* list = tr_logGetQueue();
@ -396,7 +346,62 @@ static void pumpLogMessages(tr_sys_file_t file, bool flush)
tr_logFreeQueue(list);
}
void tr_daemon::report_status(void)
void periodic_update(evutil_socket_t /*fd*/, short /*what*/, void* arg)
{
static_cast<tr_daemon*>(arg)->periodic_update();
}
tr_rpc_callback_status on_rpc_callback(tr_session* /*session*/, tr_rpc_callback_type type, tr_torrent* /*tor*/, void* arg)
{
if (type == TR_RPC_SESSION_CLOSE)
{
static_cast<tr_daemon*>(arg)->stop();
}
return TR_RPC_OK;
}
tr_variant load_settings(char const* config_dir)
{
auto app_defaults = tr_variant::make_map();
tr_variantDictAddBool(&app_defaults, TR_KEY_rpc_enabled, true);
return tr_sessionLoadSettings(&app_defaults, config_dir, MyName);
}
} // namespace
bool tr_daemon::reopen_log_file(char const* filename)
{
auto error = tr_error{};
tr_sys_file_t const old_log_file = logfile_;
tr_sys_file_t const new_log_file = tr_sys_file_open(
filename,
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_APPEND,
0666,
&error);
if (new_log_file == TR_BAD_SYS_FILE)
{
auto const errmsg = fmt::format(
"Couldn't open '{path}': {error} ({error_code})",
fmt::arg("path", filename),
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code()));
fmt::print(stderr, "{:s}\n", errmsg);
return false;
}
logfile_ = new_log_file;
logfile_flush_ = tr_sys_file_flush_possible(logfile_);
if (old_log_file != TR_BAD_SYS_FILE)
{
tr_sys_file_close(old_log_file);
}
return true;
}
void tr_daemon::report_status()
{
double const up = tr_sessionGetRawSpeed_KBps(my_session_, TR_UP);
double const dn = tr_sessionGetRawSpeed_KBps(my_session_, TR_DOWN);
@ -411,30 +416,12 @@ void tr_daemon::report_status(void)
}
}
void tr_daemon::periodic_update(void)
void tr_daemon::periodic_update()
{
pumpLogMessages(logfile_, logfile_flush_);
report_status();
}
static void periodic_update(evutil_socket_t /*fd*/, short /*what*/, void* arg)
{
static_cast<tr_daemon*>(arg)->periodic_update();
}
static tr_rpc_callback_status on_rpc_callback(
tr_session* /*session*/,
tr_rpc_callback_type type,
tr_torrent* /*tor*/,
void* arg)
{
if (type == TR_RPC_SESSION_CLOSE)
{
static_cast<tr_daemon*>(arg)->stop();
}
return TR_RPC_OK;
}
bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_settings, bool* foreground, int* exit_code)
{
int c;
@ -518,7 +505,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
break;
case 'p':
tr_variantDictAddInt(&settings_, TR_KEY_rpc_port, atoi(optstr));
if (auto const rpc_port = tr_num_parse<uint16_t>(optstr); rpc_port)
{
tr_variantDictAddInt(&settings_, TR_KEY_rpc_port, *rpc_port);
}
break;
case 't':
@ -542,7 +532,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
break;
case 'P':
tr_variantDictAddInt(&settings_, TR_KEY_peer_port, atoi(optstr));
if (auto const peer_port = tr_num_parse<uint16_t>(optstr); peer_port)
{
tr_variantDictAddInt(&settings_, TR_KEY_peer_port, *peer_port);
}
break;
case 'm':
@ -554,11 +547,17 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
break;
case 'L':
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_global, atoi(optstr));
if (auto const peer_limit_global = tr_num_parse<int64_t>(optstr); peer_limit_global && *peer_limit_global >= 0)
{
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_global, *peer_limit_global);
}
break;
case 'l':
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_per_torrent, atoi(optstr));
if (auto const peer_limit_tor = tr_num_parse<int64_t>(optstr); peer_limit_tor && *peer_limit_tor >= 0)
{
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_per_torrent, *peer_limit_tor);
}
break;
case 800:
@ -590,7 +589,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
break;
case 953:
tr_variantDictAddReal(&settings_, TR_KEY_ratio_limit, atof(optstr));
if (auto const ratio_limit = tr_num_parse<double>(optstr); optstr)
{
tr_variantDictAddReal(&settings_, TR_KEY_ratio_limit, *ratio_limit);
}
tr_variantDictAddBool(&settings_, TR_KEY_ratio_limit_enabled, true);
break;
@ -660,7 +662,7 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
return true;
}
void tr_daemon::reconfigure(void)
void tr_daemon::reconfigure()
{
if (my_session_ == nullptr)
{
@ -680,35 +682,24 @@ void tr_daemon::reconfigure(void)
configDir = tr_sessionGetConfigDir(my_session_);
tr_logAddInfo(fmt::format(_("Reloading settings from '{path}'"), fmt::arg("path", configDir)));
auto newsettings = tr_variant::make_map();
tr_variantDictAddBool(&newsettings, TR_KEY_rpc_enabled, true);
newsettings.merge(tr_sessionLoadSettings(configDir, MyName));
tr_sessionSet(my_session_, newsettings);
tr_sessionSet(my_session_, load_settings(configDir));
tr_sessionReloadBlocklists(my_session_);
}
}
void tr_daemon::stop(void)
void tr_daemon::stop()
{
event_base_loopexit(ev_base_, nullptr);
}
int tr_daemon::start([[maybe_unused]] bool foreground)
{
bool boolVal;
bool pidfile_created = false;
tr_session* session = nullptr;
struct event* status_ev = nullptr;
struct event* sig_ev = nullptr;
auto watchdir = std::unique_ptr<Watchdir>{};
char const* const cdir = this->config_dir_.c_str();
sd_notifyf(0, "MAINPID=%d\n", (int)getpid());
/* setup event state */
ev_base_ = event_base_new();
event* sig_ev = nullptr;
if (ev_base_ == nullptr || !setup_signals(sig_ev))
{
auto const error_code = errno;
@ -722,10 +713,8 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
}
/* start the session */
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);
tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr);
session = tr_sessionInit(cdir, true, settings_);
auto const* const cdir = this->config_dir_.c_str();
auto* session = tr_sessionInit(cdir, true, settings_);
tr_sessionSetRPCCallback(session, on_rpc_callback, this);
tr_logAddInfo(fmt::format(_("Loading settings from '{path}'"), fmt::arg("path", cdir)));
tr_sessionSaveSettings(session, cdir, settings_);
@ -733,9 +722,10 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
auto sv = std::string_view{};
(void)tr_variantDictFindStrView(&settings_, key_pidfile_, &sv);
auto const sz_pid_filename = std::string{ sv };
auto pidfile_created = false;
if (!std::empty(sz_pid_filename))
{
tr_error* error = nullptr;
auto error = tr_error{};
tr_sys_file_t fp = tr_sys_file_open(
sz_pid_filename.c_str(),
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE,
@ -755,13 +745,12 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
tr_logAddError(fmt::format(
_("Couldn't save '{path}': {error} ({error_code})"),
fmt::arg("path", sz_pid_filename),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
tr_error_free(error);
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code())));
}
}
if (tr_variantDictFindBool(&settings_, TR_KEY_rpc_authentication_required, &boolVal) && boolVal)
if (auto tmp_bool = false; tr_variantDictFindBool(&settings_, TR_KEY_rpc_authentication_required, &tmp_bool) && tmp_bool)
{
tr_logAddInfo(_("Requiring authentication"));
}
@ -775,7 +764,8 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
}
/* maybe add a watchdir */
if (tr_variantDictFindBool(&settings_, TR_KEY_watch_dir_enabled, &boolVal) && boolVal)
auto watchdir = std::unique_ptr<Watchdir>{};
if (auto tmp_bool = false; tr_variantDictFindBool(&settings_, TR_KEY_watch_dir_enabled, &tmp_bool) && tmp_bool)
{
auto force_generic = bool{ false };
(void)tr_variantDictFindBool(&settings_, key_watch_dir_force_generic_, &force_generic);
@ -820,6 +810,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
#endif
/* Create new timer event to report daemon status */
event* status_ev;
{
constexpr auto one_sec = timeval{ 1, 0 }; // 1 second
status_ev = event_new(ev_base_, -1, EV_PERSIST, &::periodic_update, this);
@ -906,9 +897,7 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
config_dir_ = getConfigDir(argc, argv);
/* load settings from defaults + config file */
settings_ = tr_variant::make_map();
tr_variantDictAddBool(&settings_, TR_KEY_rpc_enabled, true);
settings_.merge(tr_sessionLoadSettings(config_dir_.c_str(), MyName));
settings_ = load_settings(config_dir_.c_str());
bool dumpSettings;
@ -917,7 +906,7 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
/* overwrite settings from the command line */
if (!parse_args(argc, argv, &dumpSettings, foreground, ret))
{
goto EXIT_EARLY;
return false;
}
if (*foreground && logfile_ == TR_BAD_SYS_FILE)
@ -929,20 +918,16 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
if (dumpSettings)
{
fmt::print("{:s}\n", tr_variant_serde::json().to_string(settings_));
goto EXIT_EARLY;
return false;
}
return true;
EXIT_EARLY:
return false;
}
void tr_daemon::handle_error(tr_error* error) const
void tr_daemon::handle_error(tr_error const& error) const
{
auto const errmsg = fmt::format(FMT_STRING("Couldn't daemonize: {:s} ({:d})"), error->message, error->code);
auto const errmsg = fmt::format("Couldn't daemonize: {:s} ({:d})", error.message(), error.code());
printMessage(logfile_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
tr_error_free(error);
}
int tr_main(int argc, char* argv[])
@ -951,16 +936,15 @@ int tr_main(int argc, char* argv[])
tr_locale_set_global("");
int ret;
tr_daemon daemon;
bool foreground;
tr_error* error = nullptr;
auto foreground = bool{};
auto ret = int{};
auto daemon = tr_daemon{};
if (!daemon.init(argc, argv, &foreground, &ret))
{
return ret;
}
if (!daemon.spawn(foreground, &ret, &error))
if (auto error = tr_error{}; !daemon.spawn(foreground, &ret, error))
{
daemon.handle_error(error);
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2015-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -33,9 +33,9 @@ public:
#endif /* signalfd API */
}
bool spawn(bool foreground, int* exit_code, tr_error** error);
bool spawn(bool foreground, int* exit_code, tr_error& error);
bool init(int argc, char const* const argv[], bool* foreground, int* ret);
void handle_error(tr_error*) const;
void handle_error(tr_error const&) const;
int start(bool foreground);
void periodic_update();
void reconfigure();

View File

@ -127,12 +127,14 @@ Disable uTP for peer connections.
Where to store downloaded data.
.It Fl e Fl -logfile
Where to store transmission's log messages.
.It Fl -log-level Ar level
Log level. Must be 'critical', 'error', 'warn', 'info', 'debug', or 'trace'.
.It Fl -log-error
Show error messages
Deprecated. Use --log-level=error
.It Fl -log-info
Show error and info messages
Deprecated. Use --log-level=info
.It Fl -log-debug
Show error, info, and debug messages
Deprecated. Use --log-level=debug
.It Fl x Fl -pid-file
Name of PID file
.El

View File

@ -8,10 +8,31 @@ User=transmission
Type=notify
ExecStart=/usr/bin/transmission-daemon -f --log-level=error
ExecReload=/bin/kill -s HUP $MAINPID
# Hardening
CapabilityBoundingSet=
DevicePolicy=closed
KeyringMode=private
LockPersonality=true
NoNewPrivileges=true
MemoryDenyWriteExecute=true
ProtectSystem=true
PrivateTmp=true
PrivateDevices=true
ProtectClock=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectKernelModules=true
ProtectSystem=true
ProtectHostname=true
ProtectKernelTunables=true
ProtectProc=invisible
RestrictNamespaces=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=true
SystemCallFilter=@system-service
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
[Install]
WantedBy=multi-user.target

View File

@ -58,7 +58,7 @@ else()
endif()
file(
COPY "${CMAKE_SOURCE_DIR}/qt/icons/transmission.ico"
COPY "${PROJECT_SOURCE_DIR}/qt/icons/transmission.ico"
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
set(WEBSRCDIR "${CMAKE_INSTALL_PREFIX}/share/transmission/public_html")

View File

@ -19,41 +19,41 @@ Transmission has an Xcode project file for building in Xcode.
- Open Transmission.xcodeproj
- Run the Transmission scheme
### Building the native app with ninja ###
### Building the native app with CMake ###
Build the app:
```console
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
ninja -C build transmission-mac
cmake --build build -t transmission-mac
open ./build/macosx/Transmission.app
```
### Building the GTK app with ninja ###
### Building the GTK app with CMake ###
Install GTK and build the app:
```console
brew install gtk4 gtkmm4
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_GTK=ON -DENABLE_MAC=OFF
ninja -C build transmission-gtk
cmake --build build -t transmission-gtk
./build/gtk/transmission-gtk
```
## On Unix ##
### Prerequisites ###
#### Debian 11 / Bullseye ####
#### Debian 11 and Newer ####
On Debian, you can build transmission with a few dependencies on top of a base installation.
For building transmission-daemon you will need basic dependencies
```console
```bash
$ sudo apt install git build-essential cmake libcurl4-openssl-dev libssl-dev python3
```
You likely want to install transmission as a native GUI application. There are two options, GTK and QT.
For GTK 3 client, two additional packages are required
```console
```bash
$ sudo apt install libgtkmm-3.0-dev gettext
```
For QT client, one additional package is needed on top of basic dependencies
```console
```bash
$ sudo apt install qttools5-dev
```
@ -62,7 +62,7 @@ Then you can begin [building.](#building-transmission-from-git-first-time)
#### Ubuntu ####
On Ubuntu, you can install the required development tools for GTK with this command:
```console
```bash
$ sudo apt-get install build-essential automake autoconf libtool pkg-config intltool libcurl4-openssl-dev libglib2.0-dev libevent-dev libminiupnpc-dev libgtk-3-dev libappindicator3-dev libssl-dev
```
@ -80,7 +80,7 @@ The packages you need are:
* openssl-devel
Or simply run the following command:
```console
```bash
$ yum install gcc gcc-c++ m4 make automake libtool gettext openssl-devel
```
@ -90,33 +90,31 @@ However, Transmission needs other packages unavailable in `yum`:
* [intltool](https://ftp.gnome.org/pub/gnome/sources/intltool/)
Before building Transmission, you need to set the pkgconfig environment setting:
```console
```bash
$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
```
### Building Transmission from Git (first time) ###
```console
$ git clone https://github.com/transmission/transmission Transmission
```bash
$ git clone --recurse-submodules https://github.com/transmission/transmission Transmission
$ cd Transmission
$ git submodule update --init --recursive
$ mkdir build
# Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary with debug information. (preferred)
# Use -DCMAKE_BUILD_TYPE=Release to build full optimized binary.
$ cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
$ cd build
$ # Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary.
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
$ make
$ sudo make install
$ cmake --build .
$ sudo cmake --install .
```
### Building Transmission from Git (updating) ###
```console
```bash
$ cd Transmission/build
$ make clean
$ cmake --build . -t clean
$ git submodule foreach --recursive git clean -xfd
$ git pull --rebase --prune
$ git submodule update --recursive
$ # Use -DCMAKE_BUILD_TYPE=RelWithDebInfo to build optimized binary.
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
$ make
$ sudo make install
$ git submodule update --init --recursive
$ cmake --build .
$ sudo cmake --install .
```
## On Windows ##
@ -138,17 +136,17 @@ You need the following installed:
Vcpkg will install x86 libraries by default. To install x64 add the `--triplet=x64-windows` flag at the end of the commands below.
Common dependencies:
```
```bat
vcpkg install curl zlib openssl
```
Additional dependencies for the Qt client:
```
```bat
vcpkg install qt5-tools qt5-winextras
```
### Get Transmission source
```
```bat
git clone https://github.com/transmission/transmission
cd transmission
git submodule update --init --recursive
@ -163,11 +161,11 @@ Each option can be set to `ON` or `OFF`, values shown below are the defaults.
* `-DENABLE_UTILS=ON` - build transmission-remote, transmission-create, transmission-edit and transmission-show cli tools
* `-DENABLE_CLI=OFF` - build the cli client
```
```bat
cmake -B build -DCMAKE_TOOLCHAIN_FILE="<path-to-vcpkg>\scripts\buildsystems\vcpkg.cmake" <flags-from-above> <other-cmake-configurations>
```
To build the project run:
```
```bat
cmake --build build
```

View File

@ -81,7 +81,6 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri
* **message-level:** Number (0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace; default = 2) Set verbosity of Transmission's log messages.
* **pex-enabled:** Boolean (default = true) Enable [Peer Exchange (PEX)](https://en.wikipedia.org/wiki/Peer_exchange).
* **pidfile:** String Path to file in which daemon PID will be stored (transmission-daemon only)
* **prefetch-enabled:** Boolean (default = true). When enabled, Transmission will hint to the OS which piece data it's about to read from disk in order to satisfy requests from peers. On Linux, this is done by passing `POSIX_FADV_WILLNEED` to [posix_fadvise()](https://www.kernel.org/doc/man-pages/online/pages/man2/posix_fadvise.2.html). On macOS, this is done by passing `F_RDADVISE` to [fcntl()](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html).
* **scrape-paused-torrents-enabled:** Boolean (default = true)
* **script-torrent-added-enabled:** Boolean (default = false) Run a script when a torrent is added to Transmission. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page
* **script-torrent-added-filename:** String (default = "") Path to script.

View File

@ -1,15 +1,38 @@
## Peer-ID
From version 0.80 onward, Transmission's peer-id is formatted in Azureus' style with one digit for the major version, two digits for the minor version and one character to denote a stable release (`0`), nightly build (`Z`) or prerelease beta (`X`). For example:
* `-TR1330-` &mdash; Official 1.33 release
* `-TR133Z-` &mdash; Nightly build between 1.33 and 1.34
* `-TR133X-` &mdash; Beta release of 1.34
Rationale: This differentiates between official and unofficial releases in a way which is easy for trackers to detect with simple string comparison. An official release (`-TR1330-`) is lexicographically smaller than its post-release unsupported versions (`-TR133Z-` and `-TR133X-`), which in turn are lexicographically smaller than the next official release (`-TR1340-`).
Transmission's peer-ids follow Azureus' style with code `TR`. Peer-id prefix is `-TR????-`, where `????` are four bytes encoding the client's version. Client version encoding changed over the client's history.
Before 0.80, versions of Transmission used two digits for the major version and two for the minor version. For example, `-TR0072-` was Transmission 0.72.
### Current scheme
Starting from version 3.00, peer-ids are formatted `-TRXYZR-` where `X`, `Y`, `Z` are [base62](https://en.wikipedia.org/wiki/Base62) major, minor, patch version numbers, and `R` is one of `0` (stable release), `B` (beta release), or `Z` (dev build). For example:
* `-TR40aZ-` &mdash; 4.0.36 Dev
* `-TR400B-` &mdash; 4.0.0 Beta
* `-TR4A00-` &mdash; 4.11.0
The suffix scheme was changed after 3.00 for consistency with other clients.
### 0.80 up to 3.00
From version 0.80 up to 3.00, Transmission's peer-ids were formatted `-TRXYYR-`, where `X` was one base10 digit for the major version and `YY` were two base10 digits for the minor version. `R` was a suffix denoting a stable release (`0`), nightly build (`Z`), or prerelease beta (`X`). For example:
* `-TR133Z-` &mdash; Nightly build between 1.33 and 1.34
* `-TR133X-` &mdash; 1.34 Beta
* `-TR1330-` &mdash; 1.33
Rationale at the time: this differentiates between official and unofficial releases in a way which is easy for trackers to detect with simple string comparison. An official release (`-TR1330-`) is lexicographically smaller than its post-release unsupported versions (`-TR133Z-` and `-TR133X-`), which in turn are lexicographically smaller than the next official release (`-TR1340-`).
### Before 0.80
Before 0.80, Transmission used two base10 digits for the major version and two base10 digits for the minor version. For example:
* `-TR0072-` &mdash; 0.72
* `-TR0006-` &mdash; 0.6
## User-Agent
Its User-Agent header follows a similar format, plus the VCS revision in parentheses:
* Transmission/1.30X (6416) &mdash; Beta release leading up to version 1.30
* Transmission/1.32 (6455) &mdash; Official 1.32 release
* Transmission/1.32+ (6499) &mdash; Nightly build between 1.32 and 1.33

View File

@ -31,6 +31,7 @@ Much of this documentation is out-of-date or could be improved. Pull requests ar
* [RPC protocol specification](rpc-spec.md)
* [Transmission's Peer ID and User-Agent headers](Peer-ID-and-User-Agent.md)
* [Peer status text explained](Peer-Status-Text.md)
* [Securing Transmission's RPC interface with an open-source reverse proxy (External Link)](https://www.pomerium.com/docs/guides/transmission)
# 3. Report a problem / request a feature #
* [Check](https://github.com/transmission/transmission/issues) whether the problem has already been reported.

View File

@ -39,8 +39,6 @@ Functionality of these scripts has been implemented in libtransmission and is th
## contrib/scripts
Tomas Carnecky (aka wereHamster) is maintaining a set of scripts in his [GitHub repository](https://github.com/wereHamster/transmission/tree/master/contrib/scripts/ ).
Falk Husemann (aka hxgn) is maintaining scripts in his [blog](https://falkhusemann.de/category/tcp_ip/transmission-tcp_ip/).
Oguz wrote [on his blog](https://oguzarduc.blogspot.com/2012/05/transmission-quit-script-in-php.html) a PHP script to stop Transmission after it finishes downloading and seeding.
Scripts which have not yet been ported and may not work with the latest version:
* https://pastebin.com/QzVxQDtM: Bash - (cron)script to keep a maximum number of torrents running; starting and pausing torrents as necessary

View File

@ -37,6 +37,8 @@ The file contains the following **per-torrent** properties:
<tr><td><tt>bitfield</tt></td><td></td></tr>
</table>
The file format is bencoding, as described in [bep_0003](https://www.bittorrent.org/beps/bep_0003.html).
## Constants
<table>
<tr><td>Maximum number of remembered peers</td><td><tt>MAX_REMEMBERED_PEERS</tt></td><td>200</td></tr>

View File

@ -112,13 +112,13 @@ username and password (respectively), separated by a colon.
## 3 Torrent requests
### 3.1 Torrent action requests
| Method name | libtransmission function
|:--|:--
| `torrent-start` | tr_torrentStart
| `torrent-start-now` | tr_torrentStartNow
| `torrent-stop` | tr_torrentStop
| `torrent-verify` | tr_torrentVerify
| `torrent-reannounce` | tr_torrentManualUpdate ("ask tracker for more peers")
| Method name | libtransmission function | Description
|:--|:--|:--
| `torrent-start` | tr_torrentStart | start torrent
| `torrent-start-now` | tr_torrentStartNow | start torrent disregarding queue position
| `torrent-stop` | tr_torrentStop | stop torrent
| `torrent-verify` | tr_torrentVerify | verify torrent
| `torrent-reannounce` | tr_torrentManualUpdate | re-announce to trackers now
Request arguments: `ids`, which specifies which torrents to use.
All torrents are used if the `ids` argument is omitted.
@ -259,8 +259,8 @@ The 'source' column here corresponds to the data structure there.
| `priorities`| array (see below)| n/a
| `primary-mime-type`| string| tr_torrent
| `queuePosition`| number| tr_stat
| `rateDownload (B/s)`| number| tr_stat
| `rateUpload (B/s)`| number| tr_stat
| `rateDownload` (B/s)| number| tr_stat
| `rateUpload` (B/s)| number| tr_stat
| `recheckProgress`| double| tr_stat
| `secondsDownloading`| number| tr_stat
| `secondsSeeding`| number| tr_stat
@ -642,11 +642,11 @@ A stats object contains:
| Key | Value Type | transmission.h source
|:--|:--|:--
| uploadedBytes | number | tr_session_stats
| downloadedBytes | number | tr_session_stats
| filesAdded | number | tr_session_stats
| sessionCount | number | tr_session_stats
| secondsActive | number | tr_session_stats
| `uploadedBytes` | number | tr_session_stats
| `downloadedBytes` | number | tr_session_stats
| `filesAdded` | number | tr_session_stats
| `sessionCount` | number | tr_session_stats
| `secondsActive` | number | tr_session_stats
### 4.3 Blocklist
Method name: `blocklist-update`
@ -661,9 +661,19 @@ from the outside world.
Method name: `port-test`
Request arguments: none
Request arguments: an optional argument `ipProtocol`.
`ipProtocol` is a string specifying the IP protocol version to be used for the port test.
Set to `ipv4` to check IPv4, or set to `ipv6` to check IPv6.
For backwards compatibility, it is allowed to omit this argument to get the behaviour before Transmission `4.1.0`,
which is to check whichever IP protocol the OS happened to use to connect to our port test service,
frankly not very useful.
Response arguments: a Boolean, `port-is-open`
Response arguments:
| Key | Value Type | Description
| :-- | :-- | :--
| `port-is-open` | boolean | true if port is open, false if port is closed
| `ipProtocol` | string | `ipv4` if the test was carried out on IPv4, `ipv6` if the test was carried out on IPv6, unset if an error occured
### 4.5 Session shutdown
This method tells the transmission session to shut down.
@ -1014,3 +1024,4 @@ Transmission 4.1.0 (`rpc-version-semver` 5.4.0, `rpc-version`: 18)
| `torrent-set` | new arg `sequentialDownload`
| `torrent-get` | new arg `files.beginPiece`
| `torrent-get` | new arg `files.endPiece`
| `port-test` | new arg `ipProtocol`

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -11,6 +11,7 @@
#include "Utils.h"
#include <libtransmission/transmission.h>
#include <libtransmission/quark.h>
#include <giomm/liststore.h>
#include <giomm/menuattributeiter.h>
@ -19,18 +20,22 @@
#include <glibmm/i18n.h>
#include <glibmm/variant.h>
#if GTKMM_CHECK_VERSION(4, 0, 0)
#include <gtkmm/shortcut.h>
#include <gtkmm/shortcutaction.h>
#include <gtkmm/shortcuttrigger.h>
#endif
#include <array>
#include <stack>
#include <string>
#include <string_view>
#include <unordered_map>
#if GTKMM_CHECK_VERSION(4, 0, 0)
#include <giomm/liststore.h>
#include <gtkmm/shortcut.h>
#include <gtkmm/shortcutaction.h>
#include <gtkmm/shortcuttrigger.h>
#include <stack>
#include <utility>
#endif
using namespace std::string_view_literals;
using VariantString = Glib::Variant<Glib::ustring>;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
@ -26,6 +26,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/log.h>
#include <libtransmission/quark.h>
#include <libtransmission/rpcimpl.h>
#include <libtransmission/utils.h>
#include <libtransmission/version.h>
@ -100,7 +101,7 @@ namespace
auto const AppIconName = "transmission"sv; // TODO(C++20): Use ""s
char const* const LICENSE =
"Copyright 2005-2023. All code is copyrighted by the respective authors.\n"
"Copyright 2005-2024. All code is copyrighted by the respective authors.\n"
"\n"
"Transmission can be redistributed and/or modified under the terms of the "
"GNU GPL, versions 2 or 3, or by any future license endorsed by Mnemosyne LLC."
@ -1012,7 +1013,7 @@ void Application::Impl::on_app_exit()
p->attach(*icon, 0, 0, 1, 2);
auto* top_label = Gtk::make_managed<Gtk::Label>();
top_label->set_markup(fmt::format(FMT_STRING("<b>{:s}</b>"), _("Closing Connections…")));
top_label->set_markup(fmt::format("<b>{:s}</b>", _("Closing Connections…")));
top_label->set_halign(TR_GTK_ALIGN(START));
top_label->set_valign(TR_GTK_ALIGN(CENTER));
p->attach(*top_label, 1, 0, 1, 1);
@ -1450,7 +1451,7 @@ bool Application::Impl::call_rpc_for_selected_torrents(std::string const& method
if (tr_variantListSize(ids) != 0)
{
tr_rpc_request_exec_json(session, &top, nullptr, nullptr);
tr_rpc_request_exec(session, top, {});
invoked = true;
}
@ -1472,7 +1473,7 @@ void Application::Impl::start_all_torrents()
tr_variantInitDict(&request, 1);
tr_variantDictAddStrView(&request, TR_KEY_method, "torrent-start"sv);
tr_rpc_request_exec_json(session, &request, nullptr, nullptr);
tr_rpc_request_exec(session, request, {});
}
void Application::Impl::pause_all_torrents()
@ -1482,7 +1483,7 @@ void Application::Impl::pause_all_torrents()
tr_variantInitDict(&request, 1);
tr_variantDictAddStrView(&request, TR_KEY_method, "torrent-stop"sv);
tr_rpc_request_exec_json(session, &request, nullptr, nullptr);
tr_rpc_request_exec(session, request, {});
}
void Application::Impl::copy_magnet_link_to_clipboard(Glib::RefPtr<Torrent> const& torrent) const

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -14,7 +14,7 @@
#include "Session.h"
#include "Utils.h"
#include <libtransmission/utils.h>
#include <libtransmission/values.h>
#include <libtransmission/web-utils.h>
#include <gdkmm/pixbuf.h>
@ -53,6 +53,7 @@
#include <array>
#include <cstddef>
#include <cstdlib> // abort()
#include <iterator>
#include <limits>
#include <memory>
#include <numeric>
@ -71,6 +72,8 @@
using namespace std::literals;
using namespace libtransmission::Values;
class DetailsDialog::Impl
{
public:
@ -444,7 +447,7 @@ void DetailsDialog::Impl::torrent_set_bool(tr_quark key, bool value)
tr_variantListAddInt(ids, id);
}
core_->exec(&top);
core_->exec(top);
}
void DetailsDialog::Impl::torrent_set_int(tr_quark key, int value)
@ -462,7 +465,7 @@ void DetailsDialog::Impl::torrent_set_int(tr_quark key, int value)
tr_variantListAddInt(ids, id);
}
core_->exec(&top);
core_->exec(top);
}
void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value)
@ -480,15 +483,18 @@ void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value)
tr_variantListAddInt(ids, id);
}
core_->exec(&top);
core_->exec(top);
}
void DetailsDialog::Impl::options_page_init(Glib::RefPtr<Gtk::Builder> const& /*builder*/)
{
auto const speed_units_kbyps_str = Speed::units().display_name(Speed::Units::KByps);
honor_limits_check_tag_ = honor_limits_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_honorsSessionLimits, honor_limits_check_->get_active()); });
down_limited_check_->set_label(fmt::format(down_limited_check_->get_label().raw(), fmt::arg("speed_units", speed_K_str)));
down_limited_check_->set_label(
fmt::format(fmt::runtime(down_limited_check_->get_label().raw()), fmt::arg("speed_units", speed_units_kbyps_str)));
down_limited_check_tag_ = down_limited_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_downloadLimited, down_limited_check_->get_active()); });
@ -496,7 +502,8 @@ void DetailsDialog::Impl::options_page_init(Glib::RefPtr<Gtk::Builder> const& /*
down_limit_spin_tag_ = down_limit_spin_->signal_value_changed().connect(
[this]() { torrent_set_int(TR_KEY_downloadLimit, down_limit_spin_->get_value_as_int()); });
up_limited_check_->set_label(fmt::format(up_limited_check_->get_label().raw(), fmt::arg("speed_units", speed_K_str)));
up_limited_check_->set_label(
fmt::format(fmt::runtime(up_limited_check_->get_label().raw()), fmt::arg("speed_units", speed_units_kbyps_str)));
up_limited_check_tag_ = up_limited_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_uploadLimited, up_limited_check_->get_active()); });
@ -601,12 +608,12 @@ void gtr_text_buffer_set_text(Glib::RefPtr<Gtk::TextBuffer> const& b, Glib::ustr
[[nodiscard]] std::string get_date_string(time_t t)
{
return t == 0 ? _("N/A") : fmt::format(FMT_STRING("{:%x}"), fmt::localtime(t));
return t == 0 ? _("N/A") : fmt::format("{:%x}", fmt::localtime(t));
}
[[nodiscard]] std::string get_date_time_string(time_t t)
{
return t == 0 ? _("N/A") : fmt::format(FMT_STRING("{:%c}"), fmt::localtime(t));
return t == 0 ? _("N/A") : fmt::format("{:%c}", fmt::localtime(t));
}
} // namespace
@ -626,7 +633,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
infos.reserve(torrents.size());
for (auto* const torrent : torrents)
{
stats.push_back(tr_torrentStatCached(torrent));
stats.push_back(tr_torrentStat(torrent));
infos.push_back(tr_torrentView(torrent));
}
@ -886,7 +893,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
"({piece_count} BitTorrent pieces @ {piece_size})",
piece_count),
fmt::arg("piece_count", piece_count),
fmt::arg("piece_size", tr_formatter_mem_B(piece_size)));
fmt::arg("piece_size", Memory{ piece_size, Memory::Units::Bytes }.to_string()));
}
}
@ -1106,14 +1113,14 @@ public:
add(key);
add(was_updated);
add(url);
add(download_rate_double);
add(download_rate_speed);
add(download_rate_string);
}
Gtk::TreeModelColumn<std::string> key;
Gtk::TreeModelColumn<bool> was_updated;
Gtk::TreeModelColumn<Glib::ustring> url;
Gtk::TreeModelColumn<double> download_rate_double;
Gtk::TreeModelColumn<Speed> download_rate_speed;
Gtk::TreeModelColumn<Glib::ustring> download_rate_string;
};
@ -1128,9 +1135,9 @@ public:
add(was_updated);
add(address);
add(address_collated);
add(download_rate_double);
add(download_rate_speed);
add(download_rate_string);
add(upload_rate_double);
add(upload_rate_speed);
add(upload_rate_string);
add(client);
add(progress);
@ -1155,9 +1162,9 @@ public:
Gtk::TreeModelColumn<bool> was_updated;
Gtk::TreeModelColumn<Glib::ustring> address;
Gtk::TreeModelColumn<Glib::ustring> address_collated;
Gtk::TreeModelColumn<double> download_rate_double;
Gtk::TreeModelColumn<Speed> download_rate_speed;
Gtk::TreeModelColumn<Glib::ustring> download_rate_string;
Gtk::TreeModelColumn<double> upload_rate_double;
Gtk::TreeModelColumn<Speed> upload_rate_speed;
Gtk::TreeModelColumn<Glib::ustring> upload_rate_string;
Gtk::TreeModelColumn<Glib::ustring> client;
Gtk::TreeModelColumn<int> progress;
@ -1217,25 +1224,28 @@ void initPeerRow(
void refreshPeerRow(Gtk::TreeModel::iterator const& iter, tr_peer_stat const* peer)
{
std::string up_speed;
std::string down_speed;
std::string up_count;
std::string down_count;
std::string blocks_to_peer;
std::string blocks_to_client;
std::string cancelled_by_peer;
std::string cancelled_by_client;
g_return_if_fail(peer != nullptr);
auto const down_speed = Speed{ peer->rateToClient_KBps, Speed::Units::KByps };
auto const up_speed = Speed{ peer->rateToPeer_KBps, Speed::Units::KByps };
auto blocks_to_client = std::string{};
auto blocks_to_peer = std::string{};
auto cancelled_by_client = std::string{};
auto cancelled_by_peer = std::string{};
auto down_count = std::string{};
auto down_speed_string = std::string{};
auto up_count = std::string{};
auto up_speed_string = std::string{};
if (peer->rateToPeer_KBps > 0.01)
{
up_speed = tr_formatter_speed_KBps(peer->rateToPeer_KBps);
up_speed_string = up_speed.to_string();
}
if (peer->rateToClient_KBps > 0)
{
down_speed = tr_formatter_speed_KBps(peer->rateToClient_KBps);
down_speed_string = down_speed.to_string();
}
if (peer->activeReqsToPeer > 0)
@ -1273,10 +1283,10 @@ void refreshPeerRow(Gtk::TreeModel::iterator const& iter, tr_peer_stat const* pe
(*iter)[peer_cols.upload_request_count_string] = up_count;
(*iter)[peer_cols.download_request_count_number] = peer->activeReqsToPeer;
(*iter)[peer_cols.download_request_count_string] = down_count;
(*iter)[peer_cols.download_rate_double] = peer->rateToClient_KBps;
(*iter)[peer_cols.download_rate_string] = down_speed;
(*iter)[peer_cols.upload_rate_double] = peer->rateToPeer_KBps;
(*iter)[peer_cols.upload_rate_string] = up_speed;
(*iter)[peer_cols.download_rate_speed] = down_speed;
(*iter)[peer_cols.download_rate_string] = down_speed_string;
(*iter)[peer_cols.upload_rate_speed] = up_speed;
(*iter)[peer_cols.upload_rate_string] = up_speed_string;
(*iter)[peer_cols.flags] = std::data(peer->flagStr);
(*iter)[peer_cols.was_updated] = true;
(*iter)[peer_cols.blocks_downloaded_count_number] = peer->blocksToClient;
@ -1317,7 +1327,7 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
auto make_key = [](tr_torrent const* tor, tr_peer_stat const* ps)
{
return fmt::format(FMT_STRING("{:d}.{:s}"), tr_torrentId(tor), ps->addr);
return fmt::format("{:d}.{:s}", tr_torrentId(tor), ps->addr);
};
/* step 3: add any new peers */
@ -1385,7 +1395,7 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
auto make_key = [](tr_torrent const* tor, char const* url)
{
return fmt::format(FMT_STRING("{:d}.{:s}"), tr_torrentId(tor), url);
return fmt::format("{:d}.{:s}", tr_torrentId(tor), url);
};
/* step 1: mark all webseeds as not-updated */
@ -1423,11 +1433,11 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
auto const key = make_key(tor, webseed.url);
auto const iter = store->get_iter(hash.at(key).get_path());
auto const KBps = double(webseed.download_bytes_per_second) / speed_K;
auto const buf = webseed.is_downloading ? tr_formatter_speed_KBps(KBps) : std::string();
auto const speed = Speed{ webseed.download_bytes_per_second, Speed::Units::Byps };
auto const speed_string = webseed.is_downloading ? speed.to_string() : std::string{};
(*iter)[webseed_cols.download_rate_double] = KBps;
(*iter)[webseed_cols.download_rate_string] = buf;
(*iter)[webseed_cols.download_rate_speed] = speed;
(*iter)[webseed_cols.download_rate_string] = speed_string;
(*iter)[webseed_cols.was_updated] = true;
}
}
@ -1575,25 +1585,9 @@ void setPeerViewColumns(Gtk::TreeView* peer_view)
if (more)
{
view_columns.push_back(&peer_cols.download_request_count_string);
}
if (more)
{
view_columns.push_back(&peer_cols.blocks_downloaded_count_string);
}
if (more)
{
view_columns.push_back(&peer_cols.blocks_uploaded_count_string);
}
if (more)
{
view_columns.push_back(&peer_cols.reqs_cancelled_by_client_count_string);
}
if (more)
{
view_columns.push_back(&peer_cols.reqs_cancelled_by_peer_count_string);
}
@ -1681,7 +1675,7 @@ void setPeerViewColumns(Gtk::TreeView* peer_view)
r->property_xalign() = 1.0F;
c = Gtk::make_managed<Gtk::TreeViewColumn>(_("Down"), *r);
c->add_attribute(r->property_text(), *col);
sort_col = &peer_cols.download_rate_double;
sort_col = &peer_cols.download_rate_speed;
}
else if (*col == peer_cols.upload_rate_string)
{
@ -1689,7 +1683,7 @@ void setPeerViewColumns(Gtk::TreeView* peer_view)
r->property_xalign() = 1.0F;
c = Gtk::make_managed<Gtk::TreeViewColumn>(_("Up"), *r);
c->add_attribute(r->property_text(), *col);
sort_col = &peer_cols.upload_rate_double;
sort_col = &peer_cols.upload_rate_speed;
}
else if (*col == peer_cols.client)
{
@ -1759,7 +1753,7 @@ void DetailsDialog::Impl::peer_page_init(Glib::RefPtr<Gtk::Builder> const& build
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
auto* c = Gtk::make_managed<Gtk::TreeViewColumn>(_("Down"), *r);
c->add_attribute(r->property_text(), webseed_cols.download_rate_string);
c->set_sort_column(webseed_cols.download_rate_double);
c->set_sort_column(webseed_cols.download_rate_speed);
v->append_column(*c);
}
@ -1963,7 +1957,7 @@ void buildTrackerSummary(
gstr << text_dir_mark.at(static_cast<int>(direction));
gstr << (tracker.isBackup ? "<i>" : "<b>");
gstr << Glib::Markup::escape_text(
!key.empty() ? fmt::format(FMT_STRING("{:s} - {:s}"), tracker.host_and_port, key) : tracker.host_and_port);
!key.empty() ? fmt::format("{:s} - {:s}", tracker.host_and_port, key) : tracker.host_and_port);
gstr << (tracker.isBackup ? "</i>" : "</b>");
if (!tracker.isBackup)
@ -2380,7 +2374,7 @@ void AddTrackerDialog::on_response(int response)
auto* const trackers = tr_variantDictAddList(args, TR_KEY_trackerAdd, 1);
tr_variantListAddStr(trackers, url.raw());
core_->exec(&top);
core_->exec(top);
parent_.refresh();
}
else
@ -2427,7 +2421,7 @@ void DetailsDialog::Impl::on_tracker_list_remove_button_clicked()
auto* const trackers = tr_variantDictAddList(args, TR_KEY_trackerRemove, 1);
tr_variantListAddInt(trackers, tracker_id);
core_->exec(&top);
core_->exec(top);
refresh();
}
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2012-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2009-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -285,7 +285,7 @@ bool refreshFilesForeach(
if (new_progress != old_progress)
{
(*iter)[file_cols.prog] = new_progress;
(*iter)[file_cols.prog_str] = fmt::format(FMT_STRING("{:d}%"), new_progress);
(*iter)[file_cols.prog_str] = fmt::format("{:d}%", new_progress);
}
return false; /* keep walking */
@ -725,24 +725,25 @@ bool FileList::Impl::onViewPathToggled(Gtk::TreeViewColumn* col, Gtk::TreeModel:
if (cid == file_cols.priority.index())
{
auto priority = iter->get_value(file_cols.priority);
auto const old_priority = iter->get_value(file_cols.priority);
auto new_priority = TR_PRI_NORMAL;
switch (priority)
switch (old_priority)
{
case TR_PRI_NORMAL:
priority = TR_PRI_HIGH;
new_priority = TR_PRI_HIGH;
break;
case TR_PRI_HIGH:
priority = TR_PRI_LOW;
new_priority = TR_PRI_LOW;
break;
default:
priority = TR_PRI_NORMAL;
new_priority = TR_PRI_NORMAL;
break;
}
tr_torrentSetFilePriorities(tor, indexBuf.data(), indexBuf.size(), priority);
tr_torrentSetFilePriorities(tor, indexBuf.data(), indexBuf.size(), new_priority);
}
else
{

View File

@ -1,4 +1,4 @@
// This file Copyright © 2009-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2012-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2012-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2023-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2023-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -53,7 +53,7 @@ bool FreeSpaceLabel::Impl::on_freespace_timer()
auto const capacity = tr_sys_path_get_capacity(dir_);
auto const text = capacity ? fmt::format(_("{disk_space} free"), fmt::arg("disk_space", tr_strlsize(capacity->free))) :
_("Error");
label_.set_markup(fmt::format(FMT_STRING("<i>{:s}</i>"), text));
label_.set_markup(fmt::format("<i>{:s}</i>", text));
return true;
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -11,11 +11,11 @@
#include <giomm/contenttype.h>
#include <algorithm>
#include <functional> // for std::less<>
#include <map>
#include <string>
#include <string_view>
#include <utility>
#include <utility> // for std::move()
using namespace std::literals;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
@ -19,7 +19,7 @@
#endif
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> // tr_formatter_speed_KBps()
#include <libtransmission/values.h>
#include <gdkmm/cursor.h>
#include <gdkmm/rectangle.h>
@ -61,6 +61,7 @@
using namespace std::string_literals;
using namespace std::string_view_literals;
using namespace libtransmission::Values;
using VariantInt = Glib::Variant<int>;
using VariantDouble = Glib::Variant<double>;
@ -379,8 +380,8 @@ void MainWindow::Impl::syncAltSpeedButton()
alt_speed_button_->set_tooltip_text(fmt::format(
b ? _("Click to disable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)") :
_("Click to enable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)"),
fmt::arg("download_speed", tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_down))),
fmt::arg("upload_speed", tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_up)))));
fmt::arg("download_speed", Speed{ gtr_pref_int_get(TR_KEY_alt_speed_down), Speed::Units::KByps }.to_string()),
fmt::arg("upload_speed", Speed{ gtr_pref_int_get(TR_KEY_alt_speed_up), Speed::Units::KByps }.to_string())));
}
void MainWindow::Impl::alt_speed_toggled_cb()
@ -451,7 +452,7 @@ Glib::RefPtr<Gio::MenuModel> MainWindow::Impl::createSpeedMenu(
for (auto const KBps : { 50, 100, 250, 500, 1000, 2500, 5000, 10000 })
{
auto item = Gio::MenuItem::create(tr_formatter_speed_KBps(KBps), full_stock_action_name);
auto item = Gio::MenuItem::create(Speed{ KBps, Speed::Units::KByps }.to_string(), full_stock_action_name);
item->set_action_and_target(full_stock_action_name, VariantInt::create(KBps));
section->append_item(item);
}
@ -566,12 +567,12 @@ void MainWindow::Impl::onOptionsClicked()
update_menu(
speed_menu_info_[TR_DOWN],
tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_speed_limit_down)),
Speed{ gtr_pref_int_get(TR_KEY_speed_limit_down), Speed::Units::KByps }.to_string(),
TR_KEY_speed_limit_down_enabled);
update_menu(
speed_menu_info_[TR_UP],
tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_speed_limit_up)),
Speed{ gtr_pref_int_get(TR_KEY_speed_limit_up), Speed::Units::KByps }.to_string(),
TR_KEY_speed_limit_up_enabled);
update_menu(
@ -791,9 +792,9 @@ void MainWindow::Impl::updateSpeeds()
if (session != nullptr)
{
auto dn_count = int{};
auto dn_speed = double{};
auto dn_speed = Speed{};
auto up_count = int{};
auto up_speed = double{};
auto up_speed = Speed{};
auto const model = core_->get_model();
for (auto i = 0U, count = model->get_n_items(); i < count; ++i)
@ -805,10 +806,10 @@ void MainWindow::Impl::updateSpeeds()
up_speed += torrent->get_speed_up();
}
dl_lb_->set_text(fmt::format(_("{download_speed} ▼"), fmt::arg("download_speed", tr_formatter_speed_KBps(dn_speed))));
dl_lb_->set_text(fmt::format(fmt::runtime(_("{download_speed} ▼")), fmt::arg("download_speed", dn_speed.to_string())));
dl_lb_->set_visible(dn_count > 0);
ul_lb_->set_text(fmt::format(_("{upload_speed} ▲"), fmt::arg("upload_speed", tr_formatter_speed_KBps(up_speed))));
ul_lb_->set_text(fmt::format(fmt::runtime(_("{upload_speed} ▲")), fmt::arg("upload_speed", up_speed.to_string())));
ul_lb_->set_visible(dn_count > 0 || up_count > 0);
}
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -14,7 +14,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/makemeta.h>
#include <libtransmission/utils.h> /* tr_formatter_mem_B() */
#include <libtransmission/values.h>
#include <giomm/file.h>
#include <glibmm/convert.h>
@ -51,6 +51,7 @@
#include <utility>
using namespace std::literals;
using namespace libtransmission::Values;
#if GTKMM_CHECK_VERSION(4, 0, 0)
using FileListValue = Glib::Value<GSList*>;
@ -67,7 +68,7 @@ public:
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::future<tr_error> future,
std::string_view target,
Glib::RefPtr<Session> const& core);
~MakeProgressDialog() override;
@ -77,7 +78,7 @@ public:
static std::unique_ptr<MakeProgressDialog> create(
std::string_view target,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::future<tr_error> future,
Glib::RefPtr<Session> const& core);
[[nodiscard]] bool success() const
@ -93,7 +94,7 @@ private:
private:
tr_metainfo_builder& builder_;
std::future<tr_error*> future_;
std::future<tr_error> future_;
std::string const target_;
Glib::RefPtr<Session> const core_;
bool success_ = false;
@ -190,14 +191,14 @@ bool MakeProgressDialog::onProgressDialogRefresh()
}
else
{
tr_error* error = future_.get();
auto error = future_.get();
if (error == nullptr)
if (!error)
{
builder_.save(target_, &error);
}
if (error == nullptr)
if (!error)
{
str = fmt::format(_("Created '{path}'"), fmt::arg("path", base));
success = true;
@ -207,9 +208,8 @@ bool MakeProgressDialog::onProgressDialogRefresh()
str = fmt::format(
_("Couldn't create '{path}': {error} ({error_code})"),
fmt::arg("path", base),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code));
tr_error_free(error);
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code()));
}
}
@ -280,7 +280,7 @@ MakeProgressDialog::MakeProgressDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::future<tr_error> future,
std::string_view target,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(cast_item)
@ -302,7 +302,7 @@ MakeProgressDialog::MakeProgressDialog(
std::unique_ptr<MakeProgressDialog> MakeProgressDialog::create(
std::string_view target,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::future<tr_error> future,
Glib::RefPtr<Session> const& core)
{
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("MakeProgressDialog.ui"));
@ -395,7 +395,7 @@ void MakeDialog::Impl::updatePiecesLabel()
"({piece_count} BitTorrent pieces @ {piece_size})",
builder_->piece_count()),
fmt::arg("piece_count", builder_->piece_count()),
fmt::arg("piece_size", tr_formatter_mem_B(builder_->piece_size())));
fmt::arg("piece_size", Memory{ builder_->piece_size(), Memory::Units::Bytes }.to_string()));
}
pieces_lb_->set_text(gstr);
@ -542,7 +542,7 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& bui
file_radio_->signal_toggled().connect([this]() { onSourceToggled(file_radio_, file_chooser_); });
file_chooser_->signal_selection_changed().connect([this]() { onChooserChosen(file_chooser_); });
pieces_lb_->set_markup(fmt::format(FMT_STRING("<i>{:s}</i>"), _("No source selected")));
pieces_lb_->set_markup(fmt::format("<i>{:s}</i>", _("No source selected")));
piece_size_scale_->set_visible(false);
piece_size_scale_->signal_value_changed().connect([this]() { onPieceSizeUpdated(); });

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2010-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -118,7 +118,7 @@ void OptionsDialog::Impl::addResponseCB(int response)
{
if (response == TR_GTK_RESPONSE_TYPE(ACCEPT))
{
tr_torrentSetPriority(tor_, gtr_combo_box_get_active_enum(*priority_combo_));
tr_torrentSetPriority(tor_, static_cast<tr_priority_t>(gtr_combo_box_get_active_enum(*priority_combo_)));
if (run_check_->get_active())
{
@ -334,23 +334,22 @@ void TorrentFileChooserDialog::onOpenDialogResponse(int response, Glib::RefPtr<S
{
if (response == TR_GTK_RESPONSE_TYPE(ACCEPT))
{
/* remember this folder the next time we use this dialog */
gtr_pref_string_set(TR_KEY_open_dialog_dir, IF_GTKMM4(get_current_folder, get_current_folder_file)()->get_path());
bool const do_start = gtr_pref_flag_get(TR_KEY_start_added_torrents);
bool const do_prompt = get_choice(std::string(ShowOptionsDialogChoice)) == "true";
bool const do_notify = false;
#if GTKMM_CHECK_VERSION(4, 0, 0)
auto files = std::vector<Glib::RefPtr<Gio::File>>();
auto files_model = get_files();
for (auto i = guint{ 0 }; i < files_model->get_n_items(); ++i)
auto const files = IF_GTKMM4(get_files2, get_files)();
g_assert(!files.empty());
/* remember this folder the next time we use this dialog */
if (auto const folder = IF_GTKMM4(get_current_folder, get_current_folder_file)(); folder != nullptr)
{
files.push_back(gtr_ptr_dynamic_cast<Gio::File>(files_model->get_object(i)));
gtr_pref_string_set(TR_KEY_open_dialog_dir, folder->get_path());
}
else if (auto const parent = files.front()->get_parent(); parent != nullptr)
{
gtr_pref_string_set(TR_KEY_open_dialog_dir, parent->get_path());
}
#else
auto const files = get_files();
#endif
core->add_files(files, do_start, do_prompt, do_notify);
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2010-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2021-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
@ -25,13 +25,9 @@ void gtr_pref_init(std::string_view config_dir)
gl_confdir = config_dir;
}
/***
****
**** Preferences
****
***/
[[nodiscard]] static std::string get_default_download_dir()
namespace
{
[[nodiscard]] std::string get_default_download_dir()
{
if (auto dir = Glib::get_user_special_dir(TR_GLIB_USER_DIRECTORY(DOWNLOAD)); !std::empty(dir))
{
@ -50,7 +46,7 @@ void gtr_pref_init(std::string_view config_dir)
* This is where we initialize the preferences file with the default values.
* If you add a new preferences key, you /must/ add a default value here.
*/
[[nodiscard]] static tr_variant get_default_app_settings()
[[nodiscard]] tr_variant get_default_app_settings()
{
auto const dir = get_default_download_dir();
@ -88,7 +84,7 @@ void gtr_pref_init(std::string_view config_dir)
return tr_variant{ std::move(map) };
}
static void ensure_sound_cmd_is_a_list(tr_variant* dict)
void ensure_sound_cmd_is_a_list(tr_variant* dict)
{
tr_quark const key = TR_KEY_torrent_complete_sound_command;
tr_variant* list = nullptr;
@ -106,23 +102,20 @@ static void ensure_sound_cmd_is_a_list(tr_variant* dict)
tr_variantListAddStr(list, "transmission torrent downloaded"sv);
}
static tr_variant& getPrefs()
tr_variant& getPrefs()
{
static auto settings = tr_variant{};
if (!settings.has_value())
{
settings = get_default_app_settings();
settings.merge(tr_sessionLoadSettings(gl_confdir.c_str(), nullptr));
auto const app_defaults = get_default_app_settings();
settings.merge(tr_sessionLoadSettings(&app_defaults, gl_confdir.c_str(), nullptr));
ensure_sound_cmd_is_a_list(&settings);
}
return settings;
}
/***
****
***/
} // namespace
tr_variant& gtr_pref_get_all()
{
@ -153,9 +146,7 @@ void gtr_pref_double_set(tr_quark const key, double value)
tr_variantDictAddReal(&getPrefs(), key, value);
}
/***
****
***/
// ---
bool gtr_pref_flag_get(tr_quark const key)
{
@ -169,9 +160,7 @@ void gtr_pref_flag_set(tr_quark const key, bool value)
tr_variantDictAddBool(&getPrefs(), key, value);
}
/***
****
***/
// ---
std::vector<std::string> gtr_pref_strv_get(tr_quark const key)
{
@ -207,9 +196,7 @@ void gtr_pref_string_set(tr_quark const key, std::string_view value)
tr_variantDictAddStr(&getPrefs(), key, value);
}
/***
****
***/
// ---
void gtr_pref_save(tr_session* session)
{

View File

@ -1,4 +1,4 @@
// This file Copyright © 2005-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -49,6 +49,8 @@
#include <sstream>
#include <string>
using namespace libtransmission::Values;
/**
***
**/
@ -840,20 +842,22 @@ public:
SpeedPage::SpeedPage(BaseObjectType* cast_item, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core)
: PageBase(cast_item, builder, core)
{
auto const speed_units_kbyps_str = Speed::units().display_name(Speed::Units::KByps);
localize_label(
*init_check_button("upload_limit_check", TR_KEY_speed_limit_up_enabled),
fmt::arg("speed_units", speed_K_str));
fmt::arg("speed_units", speed_units_kbyps_str));
init_spin_button("upload_limit_spin", TR_KEY_speed_limit_up, 0, std::numeric_limits<int>::max(), 5);
localize_label(
*init_check_button("download_limit_check", TR_KEY_speed_limit_down_enabled),
fmt::arg("speed_units", speed_K_str));
fmt::arg("speed_units", speed_units_kbyps_str));
init_spin_button("download_limit_spin", TR_KEY_speed_limit_down, 0, std::numeric_limits<int>::max(), 5);
localize_label(*get_widget<Gtk::Label>("alt_upload_limit_label"), fmt::arg("speed_units", speed_K_str));
localize_label(*get_widget<Gtk::Label>("alt_upload_limit_label"), fmt::arg("speed_units", speed_units_kbyps_str));
init_spin_button("alt_upload_limit_spin", TR_KEY_alt_speed_up, 0, std::numeric_limits<int>::max(), 5);
localize_label(*get_widget<Gtk::Label>("alt_download_limit_label"), fmt::arg("speed_units", speed_K_str));
localize_label(*get_widget<Gtk::Label>("alt_download_limit_label"), fmt::arg("speed_units", speed_units_kbyps_str));
init_spin_button("alt_download_limit_spin", TR_KEY_alt_speed_down, 0, std::numeric_limits<int>::max(), 5);
init_time_combo("alt_speed_start_time_combo", TR_KEY_alt_speed_time_begin);

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2009-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -75,7 +75,7 @@ void RelocateDialog::Impl::startMovingNextTorrent()
if (tor != nullptr)
{
tr_torrentSetLocation(tor, targetLocation.c_str(), do_move_, nullptr, &done_);
tr_torrentSetLocation(tor, targetLocation.c_str(), do_move_, &done_);
}
torrent_ids_.pop_back();

View File

@ -1,4 +1,4 @@
// This file Copyright © 2009-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2021-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// This file is licensed under the MIT (SPDX: MIT) license,
// A copy of this license can be found in licenses/ .
@ -79,11 +79,11 @@ public:
void torrents_added();
void add_files(std::vector<Glib::RefPtr<Gio::File>> const& files, bool do_start, bool do_prompt, bool do_notify);
int add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify);
void add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify);
void add_torrent(Glib::RefPtr<Torrent> const& torrent, bool do_notify);
bool add_from_url(Glib::ustring const& url);
void send_rpc_request(tr_variant const* request, int64_t tag, std::function<void(tr_variant&)> const& response_func);
void send_rpc_request(tr_variant const& request, int64_t tag, std::function<void(tr_variant&)> const& response_func);
void commit_prefs_change(tr_quark key);
@ -690,12 +690,12 @@ Glib::RefPtr<Torrent> Session::Impl::create_new_torrent(tr_ctor* ctor)
return Torrent::create(tor);
}
int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
void Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
{
auto const* metainfo = tr_ctorGetMetainfo(ctor);
if (metainfo == nullptr)
{
return TR_PARSE_ERR;
return;
}
if (tr_torrentFindFromMetainfo(get_session(), metainfo) != nullptr)
@ -709,18 +709,17 @@ int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
}
tr_ctorFree(ctor);
return TR_PARSE_DUPLICATE;
return;
}
if (!do_prompt)
{
add_torrent(create_new_torrent(ctor), do_notify);
tr_ctorFree(ctor);
return 0;
return;
}
signal_add_prompt_.emit(ctor);
return 0;
}
namespace
@ -802,7 +801,7 @@ void Session::Impl::add_file_async_callback(
bool Session::Impl::add_file(Glib::RefPtr<Gio::File> const& file, bool do_start, bool do_prompt, bool do_notify)
{
auto const* const session = get_session();
auto* const session = get_session();
if (session == nullptr)
{
return false;
@ -911,7 +910,7 @@ void Session::remove_torrent(tr_torrent_id_t id, bool delete_files)
tr_torrentRemove(
&torrent->get_underlying(),
delete_files,
[](char const* filename, void* /*user_data*/, tr_error** error)
[](char const* filename, void* /*user_data*/, tr_error* error)
{ return gtr_file_trash_or_remove(filename, error); },
nullptr);
}
@ -968,7 +967,7 @@ void Session::start_now(tr_torrent_id_t id)
auto* args = tr_variantDictAddDict(&top, TR_KEY_arguments, 1);
auto* ids = tr_variantDictAddList(args, TR_KEY_ids, 1);
tr_variantListAddInt(ids, id);
exec(&top);
exec(top);
}
void Session::Impl::update()
@ -1187,19 +1186,16 @@ bool core_read_rpc_response_idle(tr_variant& response)
return false;
}
void core_read_rpc_response(tr_session* /*session*/, tr_variant* response, gpointer /*user_data*/)
void core_read_rpc_response(tr_session* /*session*/, tr_variant&& response)
{
auto owned_response = std::make_shared<tr_variant>();
tr_variantInitBool(owned_response.get(), false);
std::swap(*owned_response, *response);
auto owned_response = std::make_shared<tr_variant>(std::move(response));
Glib::signal_idle().connect([owned_response]() mutable { return core_read_rpc_response_idle(*owned_response); });
}
} // namespace
void Session::Impl::send_rpc_request(
tr_variant const* request,
tr_variant const& request,
int64_t tag,
std::function<void(tr_variant&)> const& response_func)
{
@ -1217,7 +1213,7 @@ void Session::Impl::send_rpc_request(
gtr_message(fmt::format("request: [{}]", tr_variantToStr(request, TR_VARIANT_FMT_JSON_LEAN)));
#endif
tr_rpc_request_exec_json(session_, request, core_read_rpc_response, nullptr);
tr_rpc_request_exec(session_, request, core_read_rpc_response);
}
}
@ -1235,7 +1231,7 @@ void Session::port_test()
tr_variantDictAddStrView(&request, TR_KEY_method, "port-test");
tr_variantDictAddInt(&request, TR_KEY_tag, tag);
impl_->send_rpc_request(
&request,
request,
tag,
[this](auto& response)
{
@ -1266,7 +1262,7 @@ void Session::blocklist_update()
tr_variantDictAddStrView(&request, TR_KEY_method, "blocklist-update");
tr_variantDictAddInt(&request, TR_KEY_tag, tag);
impl_->send_rpc_request(
&request,
request,
tag,
[this](auto& response)
{
@ -1292,7 +1288,7 @@ void Session::blocklist_update()
****
***/
void Session::exec(tr_variant const* request)
void Session::exec(tr_variant const& request)
{
auto const tag = nextTag;
++nextTag;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2021-2023 Transmission authors and contributors.
// This file Copyright © Transmission authors and contributors.
// This file is licensed under the MIT (SPDX: MIT) license,
// A copy of this license can be found in licenses/ .
@ -31,8 +31,8 @@ class Session : public Glib::Object
public:
enum ErrorCode
{
ERR_ADD_TORRENT_ERR = TR_PARSE_ERR,
ERR_ADD_TORRENT_DUP = TR_PARSE_DUPLICATE,
ERR_ADD_TORRENT_ERR = 1,
ERR_ADD_TORRENT_DUP = 2,
ERR_NO_MORE_TORRENTS = 1000 /* finished adding a batch */
};
@ -131,7 +131,7 @@ public:
void blocklist_update();
void exec(tr_variant const* request);
void exec(tr_variant const& request);
void open_folder(tr_torrent_id_t torrent_id) const;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2023-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2023-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -49,6 +49,7 @@
#endif
using namespace std::literals;
using namespace libtransmission::Values;
namespace
{
@ -214,6 +215,6 @@ std::string SystemTrayIcon::Impl::make_tooltip_text() const
auto const* const session = core_->get_session();
return fmt::format(
_("{upload_speed} ▲ {download_speed} ▼"),
fmt::arg("upload_speed", tr_formatter_speed_KBps(tr_sessionGetRawSpeed_KBps(session, TR_UP))),
fmt::arg("download_speed", tr_formatter_speed_KBps(tr_sessionGetRawSpeed_KBps(session, TR_DOWN))));
fmt::arg("upload_speed", Speed{ tr_sessionGetRawSpeed_KBps(session, TR_UP), Speed::Units::KByps }.to_string()),
fmt::arg("download_speed", Speed{ tr_sessionGetRawSpeed_KBps(session, TR_DOWN), Speed::Units::KByps }.to_string()));
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -12,6 +12,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
#include <libtransmission/values.h>
#include <glibmm/i18n.h>
#include <glibmm/value.h>
@ -24,6 +25,8 @@
using namespace std::string_view_literals;
using namespace libtransmission::Values;
namespace
{
@ -137,12 +140,15 @@ public:
std::string_view mime_type;
uint64_t have_unchecked = {};
uint64_t have_valid = {};
uint64_t left_until_done = {};
uint64_t size_when_done = {};
uint64_t total_size = {};
uint64_t uploaded_ever = {};
Storage have_unchecked = {};
Storage have_valid = {};
Storage left_until_done = {};
Storage size_when_done = {};
Storage total_size = {};
Storage uploaded_ever = {};
Speed speed_down = {};
Speed speed_up = {};
size_t queue_position = {};
@ -171,8 +177,6 @@ public:
float ratio = {};
float seed_ratio = {};
float speed_down = {};
float speed_up = {};
tr_priority_t priority = {};
@ -241,10 +245,19 @@ Torrent::ChangeFlags Torrent::Impl::update_cache()
auto seed_ratio = 0.0;
auto const has_seed_ratio = tr_torrentGetSeedRatio(raw_torrent_, &seed_ratio);
auto const view = tr_torrentView(raw_torrent_);
update_cache_value(cache_.name, tr_torrentName(raw_torrent_), result, ChangeFlag::NAME);
update_cache_value(cache_.speed_up, stats->pieceUploadSpeed_KBps, 0.01F, result, ChangeFlag::SPEED_UP);
update_cache_value(cache_.speed_down, stats->pieceDownloadSpeed_KBps, 0.01F, result, ChangeFlag::SPEED_DOWN);
update_cache_value(cache_.name, view.name, result, ChangeFlag::NAME);
update_cache_value(
cache_.speed_up,
Speed{ stats->pieceUploadSpeed_KBps, Speed::Units::KByps },
result,
ChangeFlag::SPEED_UP);
update_cache_value(
cache_.speed_down,
Speed{ stats->pieceDownloadSpeed_KBps, Speed::Units::KByps },
result,
ChangeFlag::SPEED_DOWN);
update_cache_value(cache_.active_peers_up, stats->peersGettingFromUs, result, ChangeFlag::ACTIVE_PEERS_UP);
update_cache_value(
cache_.active_peers_down,
@ -290,16 +303,36 @@ Torrent::ChangeFlags Torrent::Impl::update_cache()
Percents(stats->seedRatioPercentDone),
result,
ChangeFlag::SEED_RATIO_PERCENT_DONE);
update_cache_value(cache_.total_size, tr_torrentTotalSize(raw_torrent_), result, ChangeFlag::TOTAL_SIZE);
update_cache_value(cache_.total_size, Storage{ view.total_size, Storage::Units::Bytes }, result, ChangeFlag::TOTAL_SIZE);
update_cache_value(cache_.has_seed_ratio, has_seed_ratio, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.have_unchecked, stats->haveUnchecked, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.have_valid, stats->haveValid, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.left_until_done, stats->leftUntilDone, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.have_unchecked,
Storage{ stats->haveUnchecked, Storage::Units::Bytes },
result,
ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.have_valid,
Storage{ stats->haveValid, Storage::Units::Bytes },
result,
ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.left_until_done,
Storage{ stats->leftUntilDone, Storage::Units::Bytes },
result,
ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.percent_done, Percents(stats->percentDone), result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.seed_ratio, static_cast<float>(seed_ratio), 0.01F, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.size_when_done, stats->sizeWhenDone, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(cache_.uploaded_ever, stats->uploadedEver, result, ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.size_when_done,
Storage{ stats->sizeWhenDone, Storage::Units::Bytes },
result,
ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.uploaded_ever,
Storage{ stats->uploadedEver, Storage::Units::Bytes },
result,
ChangeFlag::LONG_PROGRESS);
update_cache_value(
cache_.metadata_percent_complete,
@ -313,7 +346,7 @@ Torrent::ChangeFlags Torrent::Impl::update_cache()
if (result.test(ChangeFlag::NAME))
{
cache_.name_collated = fmt::format("{}\t{}", cache_.name.lowercase(), tr_torrentView(raw_torrent_).hash_string);
cache_.name_collated = fmt::format("{}\t{}", cache_.name.lowercase(), view.hash_string);
}
return result;
@ -409,7 +442,7 @@ Glib::ustring Torrent::Impl::get_short_status_text() const
case TR_STATUS_DOWNLOAD:
case TR_STATUS_SEED:
return fmt::format(
FMT_STRING("{:s} {:s}"),
"{:s} {:s}",
get_short_transfer_text(),
fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(cache_.ratio))));
@ -422,7 +455,7 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
{
Glib::ustring gstr;
bool const isDone = cache_.left_until_done == 0;
bool const isDone = cache_.left_until_done.is_zero();
auto const haveTotal = cache_.have_unchecked + cache_.have_valid;
bool const isSeed = cache_.have_valid >= cache_.total_size;
@ -515,7 +548,7 @@ Glib::ustring Torrent::Impl::get_long_status_text() const
default:
if (auto const buf = get_short_transfer_text(); !std::empty(buf))
{
status_str += fmt::format(FMT_STRING(" - {:s}"), buf);
status_str += fmt::format(" - {:s}", buf);
}
}
@ -581,14 +614,14 @@ Glib::ustring Torrent::Impl::get_short_transfer_text() const
if (cache_.has_metadata && cache_.active_peers_down > 0)
{
return fmt::format(
_("{download_speed} ▼ {upload_speed} ▲"),
fmt::arg("upload_speed", tr_formatter_speed_KBps(cache_.speed_up)),
fmt::arg("download_speed", tr_formatter_speed_KBps(cache_.speed_down)));
fmt::runtime(_("{download_speed} ▼ {upload_speed} ▲")),
fmt::arg("upload_speed", cache_.speed_up.to_string()),
fmt::arg("download_speed", cache_.speed_down.to_string()));
}
if (cache_.has_metadata && cache_.active_peers_up > 0)
{
return fmt::format(_("{upload_speed} ▲"), fmt::arg("upload_speed", tr_formatter_speed_KBps(cache_.speed_up)));
return fmt::format(fmt::runtime(_("{upload_speed} ▲")), fmt::arg("upload_speed", cache_.speed_up.to_string()));
}
if (cache_.stalled)
@ -714,12 +747,12 @@ tr_torrent& Torrent::get_underlying() const
return *impl_->get_raw_torrent();
}
float Torrent::get_speed_up() const
Speed Torrent::get_speed_up() const
{
return impl_->get_cache().speed_up;
}
float Torrent::get_speed_down() const
Speed Torrent::get_speed_down() const
{
return impl_->get_cache().speed_down;
}
@ -784,7 +817,7 @@ int Torrent::get_active_peer_count() const
return impl_->get_cache().active_peer_count;
}
uint64_t Torrent::get_total_size() const
Storage Torrent::get_total_size() const
{
return impl_->get_cache().total_size;
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -8,6 +8,7 @@
#include "Flags.h"
#include <libtransmission/transmission.h>
#include <libtransmission/values.h>
#include <giomm/icon.h>
#include <glibmm/extraclassinit.h>
@ -71,6 +72,9 @@ public:
using ChangeFlags = Flags<ChangeFlag>;
public:
using Speed = libtransmission::Values::Speed;
using Storage = libtransmission::Values::Storage;
int get_active_peer_count() const;
int get_active_peers_down() const;
int get_active_peers_up() const;
@ -92,10 +96,10 @@ public:
float get_ratio() const;
Percents get_recheck_progress() const;
Percents get_seed_ratio_percent_done() const;
float get_speed_down() const;
float get_speed_up() const;
Speed get_speed_down() const;
Speed get_speed_up() const;
tr_torrent& get_underlying() const;
uint64_t get_total_size() const;
Storage get_total_size() const;
unsigned int get_trackers() const;
Glib::RefPtr<Gio::Icon> get_icon() const;

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -420,7 +420,7 @@ void TorrentCellRenderer::Impl::render_compact(
icon_renderer_->render(context, widget, icon_area, icon_area, flags);
progress_renderer_->property_value() = percent_done;
progress_renderer_->property_text() = fmt::format(FMT_STRING("{:d}%"), percent_done);
progress_renderer_->property_text() = fmt::format("{:d}%", percent_done);
progress_renderer_->property_sensitive() = sensitive;
render_progress_bar(context, widget, prog_area, flags, progress_color);

View File

@ -1,4 +1,4 @@
// This file Copyright © 2007-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2022-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -13,6 +13,7 @@
#include <libtransmission/error.h>
#include <libtransmission/torrent-metainfo.h>
#include <libtransmission/utils.h> /* tr_strratio() */
#include <libtransmission/values.h>
#include <libtransmission/version.h> /* SHORT_VERSION_STRING */
#include <libtransmission/web-utils.h>
@ -58,27 +59,7 @@
using namespace std::literals;
/***
**** UNITS
***/
int const mem_K = 1024;
char const* const mem_K_str = N_("KiB");
char const* const mem_M_str = N_("MiB");
char const* const mem_G_str = N_("GiB");
char const* const mem_T_str = N_("TiB");
int const disk_K = 1000;
char const* const disk_K_str = N_("kB");
char const* const disk_M_str = N_("MB");
char const* const disk_G_str = N_("GB");
char const* const disk_T_str = N_("TB");
int const speed_K = 1000;
char const* const speed_K_str = N_("kB/s");
char const* const speed_M_str = N_("MB/s");
char const* const speed_G_str = N_("GB/s");
char const* const speed_T_str = N_("TB/s");
using namespace libtransmission::Values;
/***
****
@ -132,9 +113,14 @@ Glib::ustring tr_strlratio(double ratio)
return tr_strratio(ratio, gtr_get_unicode_string(GtrUnicode::Inf).c_str());
}
Glib::ustring tr_strlsize(guint64 size_in_bytes)
Glib::ustring tr_strlsize(libtransmission::Values::Storage const& storage)
{
return size_in_bytes == 0 ? Q_("None") : tr_formatter_size_B(size_in_bytes);
return storage.is_zero() ? Q_("None") : storage.to_string();
}
Glib::ustring tr_strlsize(guint64 n_bytes)
{
return tr_strlsize(Storage{ n_bytes, Storage::Units::Bytes });
}
namespace
@ -524,14 +510,18 @@ void setup_item_view_button_event_handling(
#endif
bool gtr_file_trash_or_remove(std::string const& filename, tr_error** error)
bool gtr_file_trash_or_remove(std::string const& filename, tr_error* error)
{
bool trashed = false;
bool result = true;
g_return_val_if_fail(!filename.empty(), false);
auto local_error = tr_error{};
if (error == nullptr)
{
error = &local_error;
}
auto const file = Gio::File::create_for_path(filename);
bool trashed = false;
if (gtr_pref_flag_get(TR_KEY_trash_can_enabled))
{
@ -541,15 +531,16 @@ bool gtr_file_trash_or_remove(std::string const& filename, tr_error** error)
}
catch (Glib::Error const& e)
{
error->set(e.code(), TR_GLIB_EXCEPTION_WHAT(e));
gtr_message(fmt::format(
_("Couldn't move '{path}' to trash: {error} ({error_code})"),
fmt::arg("path", filename),
fmt::arg("error", TR_GLIB_EXCEPTION_WHAT(e)),
fmt::arg("error_code", e.code())));
tr_error_set(error, e.code(), TR_GLIB_EXCEPTION_WHAT(e));
fmt::arg("error", error->message()),
fmt::arg("error_code", error->code())));
}
}
bool result = true;
if (!trashed)
{
try
@ -558,13 +549,12 @@ bool gtr_file_trash_or_remove(std::string const& filename, tr_error** error)
}
catch (Glib::Error const& e)
{
error->set(e.code(), TR_GLIB_EXCEPTION_WHAT(e));
gtr_message(fmt::format(
_("Couldn't remove '{path}': {error} ({error_code})"),
fmt::arg("path", filename),
fmt::arg("error", TR_GLIB_EXCEPTION_WHAT(e)),
fmt::arg("error_code", e.code())));
tr_error_clear(error);
tr_error_set(error, e.code(), TR_GLIB_EXCEPTION_WHAT(e));
fmt::arg("error", error->message()),
fmt::arg("error_code", error->code())));
result = false;
}
}

View File

@ -1,4 +1,4 @@
// This file Copyright © 2008-2023 Mnemosyne LLC.
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
@ -9,6 +9,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/tr-macros.h>
#include <libtransmission/values.h>
#include <glibmm/objectbase.h>
#include <glibmm/refptr.h>
@ -43,28 +44,6 @@
****
***/
extern int const mem_K;
extern char const* const mem_K_str;
extern char const* const mem_M_str;
extern char const* const mem_G_str;
extern char const* const mem_T_str;
extern int const disk_K;
extern char const* const disk_K_str;
extern char const* const disk_M_str;
extern char const* const disk_G_str;
extern char const* const disk_T_str;
extern int const speed_K;
extern char const* const speed_K_str;
extern char const* const speed_M_str;
extern char const* const speed_G_str;
extern char const* const speed_T_str;
/***
****
***/
void gtr_message(std::string const& message);
void gtr_warning(std::string const& message);
void gtr_error(std::string const& message);
@ -85,6 +64,7 @@ Glib::ustring gtr_get_unicode_string(GtrUnicode uni);
/* return a human-readable string for the size given in bytes. */
Glib::ustring tr_strlsize(guint64 size_in_bytes);
Glib::ustring tr_strlsize(libtransmission::Values::Storage const& storage);
/* return a human-readable string for the given ratio. */
Glib::ustring tr_strlratio(double ratio);
@ -185,7 +165,7 @@ void setup_item_view_button_event_handling(
#endif
/* move a file to the trashcan if GIO is available; otherwise, delete it */
bool gtr_file_trash_or_remove(std::string const& filename, tr_error** error);
bool gtr_file_trash_or_remove(std::string const& filename, tr_error* error = nullptr);
void gtr_paste_clipboard_url_into_entry(Gtk::Entry& entry);

Some files were not shown because too many files have changed in this diff Show More