mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-01 01:20:25 +00:00
Merge branch 'main' into beta
This commit is contained in:
commit
704c32ca25
@ -31,8 +31,9 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/laf/cmake/cxx
|
|||||||
project(aseprite C CXX)
|
project(aseprite C CXX)
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
# As we compile with CMAKE_OSX_DEPLOYMENT_TARGET=10.7, we have to
|
# As we compile with CMAKE_OSX_DEPLOYMENT_TARGET=10.9, we have to
|
||||||
# explicitly say that we want to use libc++ instead of the GNU libstdc++
|
# explicitly say that we want to use libc++ instead of the GNU
|
||||||
|
# libstdc++
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -277,8 +278,10 @@ include_directories(${FREETYPE_INCLUDE_DIRS})
|
|||||||
if(USE_SHARED_HARFBUZZ)
|
if(USE_SHARED_HARFBUZZ)
|
||||||
find_package(HarfBuzz)
|
find_package(HarfBuzz)
|
||||||
else()
|
else()
|
||||||
set(HARFBUZZ_LIBRARIES harfbuzz)
|
if(NOT LAF_BACKEND STREQUAL "skia")
|
||||||
set(HARFBUZZ_INCLUDE_DIRS ${HARFBUZZ_DIR}/src)
|
set(HARFBUZZ_LIBRARIES harfbuzz)
|
||||||
|
set(HARFBUZZ_INCLUDE_DIRS ${HARFBUZZ_DIR}/src)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
include_directories(${HARFBUZZ_INCLUDE_DIRS})
|
include_directories(${HARFBUZZ_INCLUDE_DIRS})
|
||||||
|
|
||||||
@ -338,6 +341,16 @@ set(LAF_WITH_TESTS ${ENABLE_TESTS} CACHE BOOL "Enable LAF tests")
|
|||||||
set(UNDO_TESTS ${ENABLE_TESTS} CACHE BOOL "Enable undo tests")
|
set(UNDO_TESTS ${ENABLE_TESTS} CACHE BOOL "Enable undo tests")
|
||||||
|
|
||||||
add_subdirectory(laf)
|
add_subdirectory(laf)
|
||||||
|
|
||||||
|
# Use the Skia harfbuzz (it's a modified version with C++11 mutexes).
|
||||||
|
if(LAF_BACKEND STREQUAL "skia")
|
||||||
|
add_definitions(-DHAVE_CONFIG_OVERRIDE_H=1)
|
||||||
|
set(HARFBUZZ_LIBRARIES ${SKIA_HARFBUZZ_LIBRARY})
|
||||||
|
set(HARFBUZZ_INCLUDE_DIRS
|
||||||
|
${SKIA_DIR}/third_party/harfbuzz
|
||||||
|
${SKIA_DIR}/third_party/externals/harfbuzz/src)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
29
INSTALL.md
29
INSTALL.md
@ -163,7 +163,7 @@ Run `cmake` with the following parameters and then `ninja`:
|
|||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 \
|
||||||
-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
|
-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
|
||||||
-DLAF_BACKEND=skia \
|
-DLAF_BACKEND=skia \
|
||||||
-DSKIA_DIR=$HOME/deps/skia \
|
-DSKIA_DIR=$HOME/deps/skia \
|
||||||
-DSKIA_LIBRARY_DIR=$HOME/deps/skia/out/Release-x64 \
|
-DSKIA_LIBRARY_DIR=$HOME/deps/skia/out/Release-x64 \
|
||||||
@ -175,9 +175,28 @@ Run `cmake` with the following parameters and then `ninja`:
|
|||||||
In this case, `$HOME/deps/skia` is the directory where Skia was
|
In this case, `$HOME/deps/skia` is the directory where Skia was
|
||||||
compiled or downloaded. Make sure that `CMAKE_OSX_SYSROOT` is
|
compiled or downloaded. Make sure that `CMAKE_OSX_SYSROOT` is
|
||||||
pointing to the correct SDK directory (in this case
|
pointing to the correct SDK directory (in this case
|
||||||
`/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk`),
|
`/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk`),
|
||||||
but it could be different in your Mac.
|
but it could be different in your Mac.
|
||||||
|
|
||||||
|
### Apple Silicon
|
||||||
|
|
||||||
|
If you running macOS on an ARM64/AArch64/Apple Silicon Mac (e.g. M1),
|
||||||
|
you can compile a native ARM64 version of Aseprite following the same
|
||||||
|
steps as above but when we call `cmake`, we have some differences:
|
||||||
|
|
||||||
|
cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||||
|
-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
|
||||||
|
-DLAF_BACKEND=skia \
|
||||||
|
-DSKIA_DIR=$HOME/deps/skia \
|
||||||
|
-DSKIA_LIBRARY_DIR=$HOME/deps/skia/out/Release-arm64 \
|
||||||
|
-DSKIA_LIBRARY=$HOME/deps/skia/out/Release-arm64/libskia.a \
|
||||||
|
-DPNG_ARM_NEON:STRING=on \
|
||||||
|
-G Ninja \
|
||||||
|
..
|
||||||
|
|
||||||
### Issues with Retina displays
|
### Issues with Retina displays
|
||||||
|
|
||||||
If you have a Retina display, check the following issue:
|
If you have a Retina display, check the following issue:
|
||||||
@ -204,6 +223,12 @@ Run `cmake` with the following parameters and then `ninja`:
|
|||||||
In this case, `$HOME/deps/skia` is the directory where Skia was
|
In this case, `$HOME/deps/skia` is the directory where Skia was
|
||||||
compiled or uncompressed.
|
compiled or uncompressed.
|
||||||
|
|
||||||
|
### GCC compiler
|
||||||
|
|
||||||
|
In case that you are using the pre-compiled Skia version, you will
|
||||||
|
need to use the Clang compiler to compile Aseprite. Only if you
|
||||||
|
compile Skia with GCC, you will be able to compile Aseprite with GCC.
|
||||||
|
|
||||||
# Using shared third party libraries
|
# Using shared third party libraries
|
||||||
|
|
||||||
If you don't want to use the embedded code of third party libraries
|
If you don't want to use the embedded code of third party libraries
|
||||||
|
2
laf
2
laf
@ -1 +1 @@
|
|||||||
Subproject commit 726481f27c8258aa8fba577bf1529a5f40acec59
|
Subproject commit 31ba3f99f17ba1ce7eeef6ad75b6d12396f34b04
|
@ -325,6 +325,11 @@ int App::initialize(const AppOptions& options)
|
|||||||
// Show the main window (this is not modal, the code continues)
|
// Show the main window (this is not modal, the code continues)
|
||||||
m_mainWindow->openWindow();
|
m_mainWindow->openWindow();
|
||||||
|
|
||||||
|
#if LAF_LINUX // TODO check why this is required and we cannot call
|
||||||
|
// updateAllDisplaysWithNewScale() on Linux/X11
|
||||||
|
// Redraw the whole screen.
|
||||||
|
manager->invalidate();
|
||||||
|
#else
|
||||||
// To know the initial manager size we call to
|
// To know the initial manager size we call to
|
||||||
// Manager::updateAllDisplaysWithNewScale(...) so we receive a
|
// Manager::updateAllDisplaysWithNewScale(...) so we receive a
|
||||||
// Manager::onNewDisplayConfiguration() (which will update the
|
// Manager::onNewDisplayConfiguration() (which will update the
|
||||||
@ -334,6 +339,7 @@ int App::initialize(const AppOptions& options)
|
|||||||
// the dialog is centered correctly to the manager bounds.
|
// the dialog is centered correctly to the manager bounds.
|
||||||
const int scale = Preferences::instance().general.screenScale();
|
const int scale = Preferences::instance().general.screenScale();
|
||||||
manager->updateAllDisplaysWithNewScale(scale);
|
manager->updateAllDisplaysWithNewScale(scale);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif // ENABLE_UI
|
#endif // ENABLE_UI
|
||||||
|
|
||||||
@ -385,8 +391,10 @@ void App::run()
|
|||||||
rf.includeDataDir(fmt::format("icons/ase{0}.png", size).c_str());
|
rf.includeDataDir(fmt::format("icons/ase{0}.png", size).c_str());
|
||||||
if (rf.findFirst()) {
|
if (rf.findFirst()) {
|
||||||
os::SurfaceRef surf = os::instance()->loadRgbaSurface(rf.filename().c_str());
|
os::SurfaceRef surf = os::instance()->loadRgbaSurface(rf.filename().c_str());
|
||||||
if (surf)
|
if (surf) {
|
||||||
|
surf->setImmutable();
|
||||||
icons.push_back(surf);
|
icons.push_back(surf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
|
|
||||||
// Increment this value if the scripting API is modified between two
|
// Increment this value if the scripting API is modified between two
|
||||||
// released Aseprite versions.
|
// released Aseprite versions.
|
||||||
#define API_VERSION 15
|
#define API_VERSION 16
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -192,7 +192,7 @@ class SpriteEvents : public Events
|
|||||||
, public DocUndoObserver
|
, public DocUndoObserver
|
||||||
, public DocObserver {
|
, public DocObserver {
|
||||||
public:
|
public:
|
||||||
enum : EventType { Unknown = -1, Change };
|
enum : EventType { Unknown = -1, Change, FilenameChange };
|
||||||
|
|
||||||
SpriteEvents(const Sprite* sprite)
|
SpriteEvents(const Sprite* sprite)
|
||||||
: m_spriteId(sprite->id()) {
|
: m_spriteId(sprite->id()) {
|
||||||
@ -211,6 +211,8 @@ public:
|
|||||||
EventType eventType(const char* eventName) const {
|
EventType eventType(const char* eventName) const {
|
||||||
if (std::strcmp(eventName, "change") == 0)
|
if (std::strcmp(eventName, "change") == 0)
|
||||||
return Change;
|
return Change;
|
||||||
|
else if (std::strcmp(eventName, "filenamechange") == 0)
|
||||||
|
return FilenameChange;
|
||||||
else
|
else
|
||||||
return Unknown;
|
return Unknown;
|
||||||
}
|
}
|
||||||
@ -225,6 +227,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onFileNameChanged(Doc* doc) override { call(FilenameChange); }
|
||||||
|
|
||||||
// DocUndoObserver impl
|
// DocUndoObserver impl
|
||||||
void onAddUndoState(DocUndo* history) override { call(Change); }
|
void onAddUndoState(DocUndo* history) override { call(Change); }
|
||||||
void onCurrentUndoStateChange(DocUndo* history) override { call(Change); }
|
void onCurrentUndoStateChange(DocUndo* history) override { call(Change); }
|
||||||
|
@ -235,15 +235,16 @@ int Image_drawImage(lua_State* L)
|
|||||||
doc::copy_image(dst, src, pos.x, pos.y);
|
doc::copy_image(dst, src, pos.x, pos.y);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gfx::Rect bounds(pos, src->size());
|
gfx::Rect bounds(0, 0, src->size().w, src->size().h);
|
||||||
gfx::Rect output;
|
|
||||||
if (doc::algorithm::shrink_bounds2(src, dst, bounds, output)) {
|
// TODO Use something similar to doc::algorithm::shrink_bounds2()
|
||||||
Tx tx;
|
// but we need something that does the render and compares
|
||||||
tx(new cmd::CopyRegion(
|
// the minimal modified area.
|
||||||
dst, src, gfx::Region(output),
|
Tx tx;
|
||||||
gfx::Point(0, 0)));
|
tx(new cmd::CopyRegion(
|
||||||
tx.commit();
|
dst, src, gfx::Region(bounds),
|
||||||
}
|
gfx::Point(pos.x + bounds.x, pos.y + bounds.y)));
|
||||||
|
tx.commit();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ int Layers_index(lua_State* L)
|
|||||||
auto obj = get_obj<LayersObj>(L, 1);
|
auto obj = get_obj<LayersObj>(L, 1);
|
||||||
|
|
||||||
// Index by layer name
|
// Index by layer name
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_type(L, 2) == LUA_TSTRING) {
|
||||||
if (const char* name = lua_tostring(L, 2)) {
|
if (const char* name = lua_tostring(L, 2)) {
|
||||||
for (ObjectId layerId : obj->layers) {
|
for (ObjectId layerId : obj->layers) {
|
||||||
Layer* layer = doc::get<Layer>(layerId);
|
Layer* layer = doc::get<Layer>(layerId);
|
||||||
|
@ -340,6 +340,7 @@ void SkinTheme::loadSheet()
|
|||||||
m_sheet = newSheet;
|
m_sheet = newSheet;
|
||||||
if (m_sheet)
|
if (m_sheet)
|
||||||
m_sheet->applyScale(guiscale());
|
m_sheet->applyScale(guiscale());
|
||||||
|
m_sheet->setImmutable();
|
||||||
|
|
||||||
// Reset sprite sheet and font of all layer styles (to avoid
|
// Reset sprite sheet and font of all layer styles (to avoid
|
||||||
// dangling pointers to os::Surface or os::Font).
|
// dangling pointers to os::Surface or os::Font).
|
||||||
@ -756,6 +757,7 @@ os::SurfaceRef SkinTheme::sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds
|
|||||||
os::SurfaceLock lockSrc(m_sheet.get());
|
os::SurfaceLock lockSrc(m_sheet.get());
|
||||||
os::SurfaceLock lockDst(sur.get());
|
os::SurfaceLock lockDst(sur.get());
|
||||||
m_sheet->blitTo(sur.get(), bounds.x, bounds.y, 0, 0, bounds.w, bounds.h);
|
m_sheet->blitTo(sur.get(), bounds.x, bounds.y, 0, 0, bounds.w, bounds.h);
|
||||||
|
sur->setImmutable();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ASSERT(!sur);
|
ASSERT(!sur);
|
||||||
|
@ -984,6 +984,8 @@ void Tabs::createFloatingOverlay(Tab* tab)
|
|||||||
drawTab(&g, g.getClipBounds(), tab, 0, true, true);
|
drawTab(&g, g.getClipBounds(), tab, 0, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
surface->setImmutable();
|
||||||
|
|
||||||
m_floatingOverlay = base::make_ref<ui::Overlay>(
|
m_floatingOverlay = base::make_ref<ui::Overlay>(
|
||||||
display, surface, gfx::Point(),
|
display, surface, gfx::Point(),
|
||||||
(ui::Overlay::ZOrder)(Overlay::MouseZOrder-1));
|
(ui::Overlay::ZOrder)(Overlay::MouseZOrder-1));
|
||||||
|
@ -500,14 +500,16 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
os::SurfaceRef sur = os::instance()->loadRgbaSurface(rf.filename().c_str());
|
os::SurfaceRef sur = os::instance()->loadRgbaSurface(rf.filename().c_str());
|
||||||
widget = new ImageView(sur, 0);
|
if (sur) {
|
||||||
|
sur->setImmutable();
|
||||||
|
widget = new ImageView(sur, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
throw base::Exception("Error loading %s file", file);
|
throw base::Exception("Error loading %s file", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (icon) {
|
||||||
if (icon) {
|
|
||||||
SkinPartPtr part = SkinTheme::instance()->getPartById(std::string(icon));
|
SkinPartPtr part = SkinTheme::instance()->getPartById(std::string(icon));
|
||||||
if (part) {
|
if (part) {
|
||||||
widget = new ImageView(part->bitmapRef(0), 0);
|
widget = new ImageView(part->bitmapRef(0), 0);
|
||||||
|
@ -101,6 +101,7 @@ void Overlay::captureOverlappedArea()
|
|||||||
os::SurfaceLock lock(m_overlap.get());
|
os::SurfaceLock lock(m_overlap.get());
|
||||||
displaySurface->blitTo(m_overlap.get(), m_pos.x, m_pos.y, 0, 0,
|
displaySurface->blitTo(m_overlap.get(), m_pos.x, m_pos.y, 0, 0,
|
||||||
m_overlap->width(), m_overlap->height());
|
m_overlap->width(), m_overlap->height());
|
||||||
|
m_overlap->setImmutable();
|
||||||
|
|
||||||
m_captured = displaySurface;
|
m_captured = displaySurface;
|
||||||
}
|
}
|
||||||
|
14
third_party/CMakeLists.txt
vendored
14
third_party/CMakeLists.txt
vendored
@ -35,6 +35,15 @@ endif()
|
|||||||
|
|
||||||
if(WITH_WEBP_SUPPORT)
|
if(WITH_WEBP_SUPPORT)
|
||||||
set(WEBP_BUILD_EXTRAS OFF CACHE BOOL "Build extras.")
|
set(WEBP_BUILD_EXTRAS OFF CACHE BOOL "Build extras.")
|
||||||
|
set(WEBP_BUILD_ANIM_UTILS OFF CACHE BOOL "Build animation utilities.")
|
||||||
|
set(WEBP_BUILD_CWEBP OFF CACHE BOOL "Build the cwebp command line tool.")
|
||||||
|
set(WEBP_BUILD_DWEBP OFF CACHE BOOL "Build the dwebp command line tool.")
|
||||||
|
set(WEBP_BUILD_GIF2WEBP OFF CACHE BOOL "Build the gif2webp conversion tool.")
|
||||||
|
set(WEBP_BUILD_IMG2WEBP OFF CACHE BOOL "Build the img2webp animation tool.")
|
||||||
|
set(WEBP_BUILD_VWEBP OFF CACHE BOOL "Build the vwebp viewer tool.")
|
||||||
|
set(WEBP_BUILD_WEBPINFO OFF CACHE BOOL "Build the webpinfo command line tool.")
|
||||||
|
set(WEBP_BUILD_WEBPMUX OFF CACHE BOOL "Build the webpmux command line tool.")
|
||||||
|
|
||||||
add_subdirectory(libwebp)
|
add_subdirectory(libwebp)
|
||||||
|
|
||||||
if(NOT USE_SHARED_LIBPNG)
|
if(NOT USE_SHARED_LIBPNG)
|
||||||
@ -81,7 +90,7 @@ if(NOT USE_SHARED_FREETYPE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT USE_SHARED_HARFBUZZ)
|
if(NOT USE_SHARED_HARFBUZZ AND NOT LAF_BACKEND STREQUAL "skia")
|
||||||
if(NOT USE_SHARED_FREETYPE)
|
if(NOT USE_SHARED_FREETYPE)
|
||||||
set(ENV{FREETYPE_DIR} ${FREETYPE_DIR})
|
set(ENV{FREETYPE_DIR} ${FREETYPE_DIR})
|
||||||
endif()
|
endif()
|
||||||
@ -113,7 +122,10 @@ add_subdirectory(json11)
|
|||||||
set(ENABLE_WERROR OFF CACHE BOOL "Treat warnings as errors - default is ON for Debug, OFF otherwise.")
|
set(ENABLE_WERROR OFF CACHE BOOL "Treat warnings as errors - default is ON for Debug, OFF otherwise.")
|
||||||
set(ENABLE_TEST OFF CACHE BOOL "Enable unit and regression tests")
|
set(ENABLE_TEST OFF CACHE BOOL "Enable unit and regression tests")
|
||||||
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)")
|
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)")
|
||||||
|
set(ENABLE_LZ4 OFF CACHE BOOL "Enable the use of the system LZ4 library if found")
|
||||||
|
set(ENABLE_LZO OFF CACHE BOOL "Enable the use of the system LZO library if found")
|
||||||
set(ENABLE_LZMA OFF CACHE BOOL "Enable the use of the system LZMA library if found")
|
set(ENABLE_LZMA OFF CACHE BOOL "Enable the use of the system LZMA library if found")
|
||||||
|
set(ENABLE_ZSTD OFF CACHE BOOL "Enable the use of the system zstd library if found")
|
||||||
set(ENABLE_CNG OFF CACHE BOOL "Enable the use of CNG(Crypto Next Generation)")
|
set(ENABLE_CNG OFF CACHE BOOL "Enable the use of CNG(Crypto Next Generation)")
|
||||||
set(ENABLE_BZip2 OFF CACHE BOOL "Enable the use of the system BZip2 library if found")
|
set(ENABLE_BZip2 OFF CACHE BOOL "Enable the use of the system BZip2 library if found")
|
||||||
set(ENABLE_LIBXML2 OFF CACHE BOOL "Enable the use of the system libxml2 library if found")
|
set(ENABLE_LIBXML2 OFF CACHE BOOL "Enable the use of the system libxml2 library if found")
|
||||||
|
2
third_party/harfbuzz
vendored
2
third_party/harfbuzz
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 2040a29984a1d47bc9dd309995bfc1d8d51238d3
|
Subproject commit a52c6df38a38c4e36ff991dfb4b7d92e48a44553
|
2
third_party/libwebp
vendored
2
third_party/libwebp
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 69776e3832fba9969cf9da2513330f500f94e101
|
Subproject commit 9ce5843dbabcfd3f7c39ec7ceba9cbeb213cbfdf
|
Loading…
x
Reference in New Issue
Block a user