From e597d59cb2501bd900facbab94476ee5193543dc Mon Sep 17 00:00:00 2001 From: Alfie King Date: Thu, 19 Sep 2024 13:11:19 +0100 Subject: [PATCH] remove clip-lib --- .vscode/settings.json | 7 + CMakeLists.txt | 3 +- PKGBUILD | 4 +- src/clip-lib/CMakeLists.txt | 98 --- src/clip-lib/CONTRIBUTING.md | 5 - src/clip-lib/README.md | 77 --- src/clip-lib/clip.cpp | 192 ------ src/clip-lib/clip.h | 211 ------- src/clip-lib/clip_common.h | 82 --- src/clip-lib/clip_lock_impl.h | 40 -- src/clip-lib/clip_none.cpp | 86 --- src/clip-lib/clip_osx.h | 35 - src/clip-lib/clip_osx.mm | 377 ----------- src/clip-lib/clip_win.cpp | 407 ------------ src/clip-lib/clip_win.h | 26 - src/clip-lib/clip_win_bmp.cpp | 347 ---------- src/clip-lib/clip_win_bmp.h | 66 -- src/clip-lib/clip_win_wic.cpp | 472 -------------- src/clip-lib/clip_win_wic.h | 76 --- src/clip-lib/clip_x11.cpp | 1123 --------------------------------- src/clip-lib/clip_x11_png.h | 230 ------- src/clip-lib/image.cpp | 99 --- src/main.cpp | 8 - 23 files changed, 10 insertions(+), 4061 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/clip-lib/CMakeLists.txt delete mode 100644 src/clip-lib/CONTRIBUTING.md delete mode 100644 src/clip-lib/README.md delete mode 100644 src/clip-lib/clip.cpp delete mode 100644 src/clip-lib/clip.h delete mode 100644 src/clip-lib/clip_common.h delete mode 100644 src/clip-lib/clip_lock_impl.h delete mode 100644 src/clip-lib/clip_none.cpp delete mode 100644 src/clip-lib/clip_osx.h delete mode 100644 src/clip-lib/clip_osx.mm delete mode 100644 src/clip-lib/clip_win.cpp delete mode 100644 src/clip-lib/clip_win.h delete mode 100644 src/clip-lib/clip_win_bmp.cpp delete mode 100644 src/clip-lib/clip_win_bmp.h delete mode 100644 src/clip-lib/clip_win_wic.cpp delete mode 100644 src/clip-lib/clip_win_wic.h delete mode 100644 src/clip-lib/clip_x11.cpp delete mode 100644 src/clip-lib/clip_x11_png.h delete mode 100644 src/clip-lib/image.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5dd1d02 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "*.pyw": "python", + "string": "cpp", + "ostream": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c8d7a4f..cce3ae2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,5 @@ find_program(CMAKE_C_COMPILER NAMES $ENV{CC} gcc PATHS ENV PATH NO_DEFAULT_PATH) find_program(CMAKE_CXX_COMPILER NAMES $ENV{CXX} g++ PATHS ENV PATH NO_DEFAULT_PATH) project(owo) add_executable(owo src/main.cpp) -add_subdirectory(src/clip-lib) -target_link_libraries(owo clip) +target_link_libraries(owo) diff --git a/PKGBUILD b/PKGBUILD index ea784a3..ff8791c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -8,13 +8,13 @@ makedepends=(cmake git gcc) license=('MIT') source=("git+https://git.alfieking.dev/acetheking987/term-owo-cpp.git") -md5sums=('SKIP') +md5sums=('SKIP') build() { cd "$srcdir/term-owo-cpp" cmake . -B build cd build && make -} +} package() { cd "$srcdir/term-owo-cpp/build" diff --git a/src/clip-lib/CMakeLists.txt b/src/clip-lib/CMakeLists.txt deleted file mode 100644 index d72d150..0000000 --- a/src/clip-lib/CMakeLists.txt +++ /dev/null @@ -1,98 +0,0 @@ -# Clip Library -# Copyright (c) 2015-2024 David Capello - -cmake_minimum_required(VERSION 3.5) - -project(clip LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - # Use libc++ explicitly so we can compile for - # CMAKE_OSX_DEPLOYMENT_TARGET=10.7 or 10.8 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() - -option(CLIP_ENABLE_IMAGE "Compile with support to copy/paste images" on) -if(WIN32) - option(CLIP_ENABLE_LIST_FORMATS "Compile with support to list clipboard formats" off) -endif() -option(CLIP_EXAMPLES "Compile clip examples" on) -option(CLIP_TESTS "Compile clip tests" on) -if(UNIX AND NOT APPLE) - option(CLIP_X11_WITH_PNG "Compile with libpng to support copy/paste image in png format" on) -endif() - -add_library(clip clip.cpp) - -if(CLIP_ENABLE_IMAGE) - target_sources(clip PRIVATE image.cpp) - target_compile_definitions(clip PUBLIC -DCLIP_ENABLE_IMAGE=1) -endif() - -if(CLIP_ENABLE_LIST_FORMATS) - target_compile_definitions(clip PUBLIC -DCLIP_ENABLE_LIST_FORMATS=1) -endif() - -if(WIN32) - option(CLIP_SUPPORT_WINXP "Enable Windows XP support" OFF) - - target_sources(clip PRIVATE clip_win.cpp) - if(CLIP_ENABLE_IMAGE) - target_sources(clip PRIVATE clip_win_bmp.cpp clip_win_wic.cpp) - target_link_libraries(clip shlwapi) - endif() - - if(MSVC) - target_compile_definitions(clip PRIVATE -D_SCL_SECURE_NO_WARNINGS) - endif() - if (CLIP_SUPPORT_WINXP) - target_compile_definitions(clip PRIVATE -DCLIP_SUPPORT_WINXP) - endif() - - # MinGW requires the windowscodecs just because CLSIDs are defined - # in the windowscodecs.a file instead of the wincodec.h file (?!) - if(MINGW) - find_library(CLIP_WINDOWSCODECS_LIBRARY windowscodecs) - if(CLIP_WINDOWSCODECS_LIBRARY) - target_link_libraries(clip ${CLIP_WINDOWSCODECS_LIBRARY}) - endif() - endif() -elseif(APPLE) - target_compile_options(clip PRIVATE -fobjc-arc) - - find_library(COCOA_LIBRARY Cocoa) - if(COCOA_LIBRARY) - target_sources(clip PRIVATE clip_osx.mm) - target_link_libraries(clip ${COCOA_LIBRARY}) - else() - target_sources(clip PRIVATE clip_none.cpp) - endif() -elseif(UNIX) - include(CheckIncludeFiles) - check_include_files(xcb/xcb.h HAVE_XCB_XLIB_H) - - if(HAVE_XCB_XLIB_H) - target_compile_definitions(clip PRIVATE -DHAVE_XCB_XLIB_H) - target_link_libraries(clip xcb pthread) - - if(CLIP_ENABLE_IMAGE AND CLIP_X11_WITH_PNG) - check_include_files(png.h HAVE_PNG_H) - if(CLIP_X11_PNG_LIBRARY) - set(PNG_LIBRARY ${CLIP_X11_PNG_LIBRARY}) - else() - find_library(PNG_LIBRARY png) - endif() - if(HAVE_PNG_H AND PNG_LIBRARY) - target_compile_definitions(clip PRIVATE -DHAVE_PNG_H) - endif() - target_link_libraries(clip ${PNG_LIBRARY}) - endif() - target_sources(clip PRIVATE clip_x11.cpp) - else() - target_sources(clip PRIVATE clip_none.cpp) - endif() -else() - target_sources(clip PRIVATE clip_none.cpp) -endif() \ No newline at end of file diff --git a/src/clip-lib/CONTRIBUTING.md b/src/clip-lib/CONTRIBUTING.md deleted file mode 100644 index 6fb6b3d..0000000 --- a/src/clip-lib/CONTRIBUTING.md +++ /dev/null @@ -1,5 +0,0 @@ -By submitting a pull request, you represent that you have the right to -license your contribution to the Clip project owners and the community, -agree by submitting the patch that your contributions are licensed under -the [Clip license](https://raw.githubusercontent.com/dacap/clip/main/LICENSE.txt), -and agree to future changes to the licensing. diff --git a/src/clip-lib/README.md b/src/clip-lib/README.md deleted file mode 100644 index 8e6b12f..0000000 --- a/src/clip-lib/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Clip Library -*Copyright (c) 2015-2024 David Capello* - -[![build](https://github.com/dacap/clip/workflows/build/badge.svg)](https://github.com/dacap/clip/actions?query=workflow%3Abuild) -[![MIT Licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt) - -Library to copy/retrieve content to/from the clipboard/pasteboard. - -## Features - -Available features on Windows, macOS, and Linux (X11): - -* Copy/paste UTF-8 text. -* Copy/paste user-defined data. -* Copy/paste RGB/RGBA images. This library use non-premultiplied alpha RGB values. - -## Example - -```cpp -#include "clip.h" -#include - -int main() { - clip::set_text("Hello World"); - - std::string value; - clip::get_text(value); - std::cout << value << "\n"; -} -``` - -## User-defined clipboard formats - -```cpp -#include "clip.h" - -int main() { - clip::format my_format = - clip::register_format("com.appname.FormatName"); - - int value = 32; - - clip::lock l; - l.clear(); - l.set_data(clip::text_format(), "Alternative text for value 32"); - l.set_data(my_format, &value, sizeof(int)); -} -``` - -## Platform specific details - -* If two versions of your application (32-bit and 64-bit) can run at - at the same time, remember to avoid storing data types that could - change depending on the platform (e.g. `size_t`) in your custom - format data. -* **Windows**: - - [Limited number of clipboard formats on Windows](http://blogs.msdn.com/b/oldnewthing/archive/2015/03/19/10601208.aspx) -* **Linux**: - - To be able to copy/paste on Linux you need `libx11-dev`/`libX11-devel` package. - - To copy/paste images you will need `libpng-dev`/`libpng-devel` package. - -## Compilation Flags - -* `CLIP_ENABLE_IMAGE`: Enables the support to - [copy](examples/put_image.cpp)/[paste](examples/show_image.cpp) images. -* `CLIP_ENABLE_LIST_FORMATS` (only for Windows): Enables the - `clip::lock::list_formats()` API function and the - [list_clip_formats](examples/list_clip_formats.cpp) example. -* `CLIP_EXAMPLES`: Compile [examples](examples/). -* `CLIP_TESTS`: Compile [tests](tests/). -* `CLIP_X11_WITH_PNG` (only for Linux/X11): Enables support to - copy/paste images using the `libpng` library on Linux. - -## Who is using this library? - -[Check the wiki](https://github.com/dacap/clip/wiki#who-is-using-clip) -to know what projects are using the `clip` library. diff --git a/src/clip-lib/clip.cpp b/src/clip-lib/clip.cpp deleted file mode 100644 index 16ead84..0000000 --- a/src/clip-lib/clip.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip.h" -#include "clip_lock_impl.h" - -#include -#include - -namespace clip { - -namespace { - -void default_error_handler(ErrorCode code) { - static const char* err[] = { - "Cannot lock clipboard", - "Image format is not supported" - }; - throw std::runtime_error(err[static_cast(code)]); -} - -} // anonymous namespace - -error_handler g_error_handler = default_error_handler; - -lock::lock(void* native_window_handle) - : p(new impl(native_window_handle)) { -} - -lock::~lock() = default; - -bool lock::locked() const { - return p->locked(); -} - -bool lock::clear() { - return p->clear(); -} - -bool lock::is_convertible(format f) const { - return p->is_convertible(f); -} - -bool lock::set_data(format f, const char* buf, size_t length) { - return p->set_data(f, buf, length); -} - -bool lock::get_data(format f, char* buf, size_t len) const { - return p->get_data(f, buf, len); -} - -size_t lock::get_data_length(format f) const { - return p->get_data_length(f); -} - -#if CLIP_ENABLE_IMAGE - -bool lock::set_image(const image& img) { - return p->set_image(img); -} - -bool lock::get_image(image& img) const { - return p->get_image(img); -} - -bool lock::get_image_spec(image_spec& spec) const { - return p->get_image_spec(spec); -} - -#endif // CLIP_ENABLE_IMAGE - -#if CLIP_ENABLE_LIST_FORMATS - -std::vector lock::list_formats() const { - return p->list_formats(); -} - -#endif // CLIP_ENABLE_LIST_FORMATS - -format empty_format() { return 0; } -format text_format() { return 1; } -#if CLIP_ENABLE_IMAGE -format image_format() { return 2; } -#endif - -bool has(format f) { - lock l; - if (l.locked()) - return l.is_convertible(f); - else - return false; -} - -bool clear() { - lock l; - if (l.locked()) - return l.clear(); - else - return false; -} - -bool set_text(const std::string& value) { - lock l; - if (l.locked()) { - l.clear(); - return l.set_data(text_format(), value.c_str(), value.size()); - } - else - return false; -} - -bool get_text(std::string& value) { - lock l; - if (!l.locked()) - return false; - - format f = text_format(); - if (!l.is_convertible(f)) - return false; - - size_t len = l.get_data_length(f); - if (len > 0) { - std::vector buf(len); - l.get_data(f, &buf[0], len); - value = &buf[0]; - return true; - } - else { - value.clear(); - return true; - } -} - -#if CLIP_ENABLE_IMAGE - -bool set_image(const image& img) { - lock l; - if (l.locked()) { - l.clear(); - return l.set_image(img); - } - else - return false; -} - -bool get_image(image& img) { - lock l; - if (!l.locked()) - return false; - - format f = image_format(); - if (!l.is_convertible(f)) - return false; - - return l.get_image(img); -} - -bool get_image_spec(image_spec& spec) { - lock l; - if (!l.locked()) - return false; - - format f = image_format(); - if (!l.is_convertible(f)) - return false; - - return l.get_image_spec(spec); -} - -#endif // CLIP_ENABLE_IMAGE - -void set_error_handler(error_handler handler) { - g_error_handler = handler; -} - -error_handler get_error_handler() { - return g_error_handler; -} - -#ifdef HAVE_XCB_XLIB_H -static int g_x11_timeout = 1000; -void set_x11_wait_timeout(int msecs) { g_x11_timeout = msecs; } -int get_x11_wait_timeout() { return g_x11_timeout; } -#else -void set_x11_wait_timeout(int) { } -int get_x11_wait_timeout() { return 1000; } -#endif - -} // namespace clip diff --git a/src/clip-lib/clip.h b/src/clip-lib/clip.h deleted file mode 100644 index a6f0913..0000000 --- a/src/clip-lib/clip.h +++ /dev/null @@ -1,211 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_H_INCLUDED -#define CLIP_H_INCLUDED -#pragma once - -#include -#include -#include -#include - -namespace clip { - - // ====================================================================== - // Low-level API to lock the clipboard/pasteboard and modify it - // ====================================================================== - - // Clipboard format identifier. - typedef size_t format; - -#if CLIP_ENABLE_IMAGE - class image; - struct image_spec; -#endif // CLIP_ENABLE_IMAGE - -#if CLIP_ENABLE_LIST_FORMATS - struct format_info { - format id = 0; - std::string name; - format_info(const format id, - const std::string& name) - : id(id), - name(name) { - } - }; -#endif // CLIP_ENABLE_LIST_FORMATS - - class lock { - public: - // You can give your current HWND as the "native_window_handle." - // Windows clipboard functions use this handle to open/close - // (lock/unlock) the clipboard. From the MSDN documentation we - // need this handler so SetClipboardData() doesn't fail after a - // EmptyClipboard() call. Anyway it looks to work just fine if we - // call OpenClipboard() with a null HWND. - lock(void* native_window_handle = nullptr); - ~lock(); - - // Returns true if we've locked the clipboard successfully in - // lock() constructor. - bool locked() const; - - // Clears the clipboard content. If you don't clear the content, - // previous clipboard content (in unknown formats) could persist - // after the unlock. - bool clear(); - - // Returns true if the clipboard can be converted to the given - // format. - bool is_convertible(format f) const; - bool set_data(format f, const char* buf, size_t len); - bool get_data(format f, char* buf, size_t len) const; - size_t get_data_length(format f) const; - -#if CLIP_ENABLE_IMAGE - // For images - bool set_image(const image& image); - bool get_image(image& image) const; - bool get_image_spec(image_spec& spec) const; -#endif // CLIP_ENABLE_IMAGE - -#if CLIP_ENABLE_LIST_FORMATS - // Returns the list of available formats (by name) in the - // clipboard. - std::vector list_formats() const; -#endif // CLIP_ENABLE_LIST_FORMATS - - private: - class impl; - std::unique_ptr p; - }; - - format register_format(const std::string& name); - - // This format is when the clipboard has no content. - format empty_format(); - - // When the clipboard has UTF8 text. - format text_format(); - -#if CLIP_ENABLE_IMAGE - // When the clipboard has an image. - format image_format(); -#endif - - // Returns true if the clipboard has content of the given type. - bool has(format f); - - // Clears the clipboard content. - bool clear(); - - // ====================================================================== - // Error handling - // ====================================================================== - - enum class ErrorCode { - CannotLock, -#if CLIP_ENABLE_IMAGE - ImageNotSupported, -#endif - }; - - typedef void (*error_handler)(ErrorCode code); - - void set_error_handler(error_handler f); - error_handler get_error_handler(); - - // ====================================================================== - // Text - // ====================================================================== - - // High-level API to put/get UTF8 text in/from the clipboard. These - // functions returns false in case of error. - bool set_text(const std::string& value); - bool get_text(std::string& value); - - // ====================================================================== - // Image - // ====================================================================== - -#if CLIP_ENABLE_IMAGE - - struct image_spec { - unsigned long width = 0; - unsigned long height = 0; - unsigned long bits_per_pixel = 0; - unsigned long bytes_per_row = 0; - unsigned long red_mask = 0; - unsigned long green_mask = 0; - unsigned long blue_mask = 0; - unsigned long alpha_mask = 0; - unsigned long red_shift = 0; - unsigned long green_shift = 0; - unsigned long blue_shift = 0; - unsigned long alpha_shift = 0; - - unsigned long required_data_size() const; - }; - - // The image data must contain straight RGB values - // (non-premultiplied by alpha). The image retrieved from the - // clipboard will be non-premultiplied too. Basically you will be - // always dealing with straight alpha images. - // - // Details: Windows expects premultiplied images on its clipboard - // content, so the library code make the proper conversion - // automatically. macOS handles straight alpha directly, so there is - // no conversion at all. Linux/X11 images are transferred in - // image/png format which are specified in straight alpha. - class image { - public: - image(); - image(const image_spec& spec); - image(const void* data, const image_spec& spec); - image(const image& image); - image(image&& image); - ~image(); - - image& operator=(const image& image); - image& operator=(image&& image); - - char* data() const { return m_data; } - const image_spec& spec() const { return m_spec; } - - bool is_valid() const { return m_data != nullptr; } - void reset(); - - private: - void copy_image(const image& image); - void move_image(image&& image); - - bool m_own_data; - char* m_data; - image_spec m_spec; - }; - - // High-level API to set/get an image in/from the clipboard. These - // functions returns false in case of error. - bool set_image(const image& img); - bool get_image(image& img); - bool get_image_spec(image_spec& spec); - -#endif // CLIP_ENABLE_IMAGE - - // ====================================================================== - // Platform-specific - // ====================================================================== - - // Only for X11: Sets the time (in milliseconds) that we must wait - // for the selection/clipboard owner to receive the content. This - // value is 1000 (one second) by default. - void set_x11_wait_timeout(int msecs); - int get_x11_wait_timeout(); - -} // namespace clip - -#endif // CLIP_H_INCLUDED diff --git a/src/clip-lib/clip_common.h b/src/clip-lib/clip_common.h deleted file mode 100644 index 61fa992..0000000 --- a/src/clip-lib/clip_common.h +++ /dev/null @@ -1,82 +0,0 @@ -// Clip Library -// Copyright (C) 2020-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_COMMON_H_INCLUDED -#define CLIP_COMMON_H_INCLUDED -#pragma once - -#include "clip.h" - -namespace clip { -namespace details { - -#if CLIP_ENABLE_IMAGE - -inline void divide_rgb_by_alpha(image& img, - bool hasAlphaGreaterThanZero = false) { - const image_spec& spec = img.spec(); - - bool hasValidPremultipliedAlpha = true; - - for (unsigned long y=0; y> spec.red_shift ); - const int g = ((c & spec.green_mask) >> spec.green_shift); - const int b = ((c & spec.blue_mask ) >> spec.blue_shift ); - const int a = ((c & spec.alpha_mask) >> spec.alpha_shift); - - if (a > 0) - hasAlphaGreaterThanZero = true; - if (r > a || g > a || b > a) - hasValidPremultipliedAlpha = false; - } - } - - for (unsigned long y=0; y> spec.red_shift ); - int g = ((c & spec.green_mask) >> spec.green_shift); - int b = ((c & spec.blue_mask ) >> spec.blue_shift ); - int a = ((c & spec.alpha_mask) >> spec.alpha_shift); - - // If all alpha values = 0, we make the image opaque. - if (!hasAlphaGreaterThanZero) { - a = 255; - - // We cannot change the image spec (e.g. spec.alpha_mask=0) to - // make the image opaque, because the "spec" of the image is - // read-only. The image spec used by the client is the one - // returned by get_image_spec(). - } - // If there is alpha information and it's pre-multiplied alpha - else if (hasValidPremultipliedAlpha) { - if (a > 0) { - // Convert it to straight alpha - r = r * 255 / a; - g = g * 255 / a; - b = b * 255 / a; - } - } - - *dst = - (r << spec.red_shift ) | - (g << spec.green_shift) | - (b << spec.blue_shift ) | - (a << spec.alpha_shift); - } - } -} - -#endif // CLIP_ENABLE_IMAGE - -} // namespace details -} // namespace clip - -#endif // CLIP_H_INCLUDED diff --git a/src/clip-lib/clip_lock_impl.h b/src/clip-lib/clip_lock_impl.h deleted file mode 100644 index 0c678e5..0000000 --- a/src/clip-lib/clip_lock_impl.h +++ /dev/null @@ -1,40 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_LOCK_IMPL_H_INCLUDED -#define CLIP_LOCK_IMPL_H_INCLUDED - -namespace clip { - -class lock::impl { -public: - impl(void* native_window_handle); - ~impl(); - - bool locked() const { return m_locked; } - bool clear(); - bool is_convertible(format f) const; - bool set_data(format f, const char* buf, size_t len); - bool get_data(format f, char* buf, size_t len) const; - size_t get_data_length(format f) const; - -#if CLIP_ENABLE_IMAGE - bool set_image(const image& image); - bool get_image(image& image) const; - bool get_image_spec(image_spec& spec) const; -#endif // CLIP_ENABLE_IMAGE - -#if CLIP_ENABLE_LIST_FORMATS - std::vector list_formats() const; -#endif // CLIP_ENABLE_LIST_FORMATS - -private: - bool m_locked; -}; - -} // namespace clip - -#endif diff --git a/src/clip-lib/clip_none.cpp b/src/clip-lib/clip_none.cpp deleted file mode 100644 index f4cc40c..0000000 --- a/src/clip-lib/clip_none.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2018 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip.h" -#include "clip_lock_impl.h" - -#include -#include -#include - -namespace clip { - -typedef std::vector Buffer; -typedef std::map Map; - -static format g_last_format = 100; // TODO create an enum with common formats -static Map g_data; - -lock::impl::impl(void* native_handle) : m_locked(true) { -} - -lock::impl::~impl() { -} - -bool lock::impl::clear() { - g_data.clear(); - return true; -} - -bool lock::impl::is_convertible(format f) const { - return (g_data.find(f) != g_data.end()); -} - -bool lock::impl::set_data(format f, const char* buf, size_t len) { - Buffer& dst = g_data[f]; - - dst.resize(len); - if (buf && len > 0) - std::copy(buf, buf+len, dst.begin()); - - if (f == text_format() && - len > 0 && dst.back() != 0) { - dst.push_back(0); - } - - return true; -} - -bool lock::impl::get_data(format f, char* buf, size_t len) const { - assert(buf); - - if (!buf || !is_convertible(f)) - return false; - - const Buffer& src = g_data[f]; - std::copy(src.begin(), src.end(), buf); - return true; -} - -size_t lock::impl::get_data_length(format f) const { - if (is_convertible(f)) - return g_data[f].size(); - else - return 0; -} - -bool lock::impl::set_image(const image& image) { - return false; // TODO -} - -bool lock::impl::get_image(image& image) const { - return false; // TODO -} - -bool lock::impl::get_image_spec(image_spec& spec) const { - return false; // TODO -} - -format register_format(const std::string& name) { - return g_last_format++; -} - -} // namespace clip diff --git a/src/clip-lib/clip_osx.h b/src/clip-lib/clip_osx.h deleted file mode 100644 index 7346976..0000000 --- a/src/clip-lib/clip_osx.h +++ /dev/null @@ -1,35 +0,0 @@ -// Clip Library -// Copyright (c) 2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_OSX_H_INCLUDED -#define CLIP_OSX_H_INCLUDED -#pragma once - -#ifdef __OBJC__ - -#include - -namespace clip { - -class image; -struct image_spec; - -namespace osx { - -#if CLIP_ENABLE_IMAGE - -bool get_image_from_clipboard(NSPasteboard* pasteboard, - image* output_img, - image_spec* output_spec); - -#endif // CLIP_ENABLE_IMAGE - -} // namespace osx -} // namespace clip - -#endif - -#endif diff --git a/src/clip-lib/clip_osx.mm b/src/clip-lib/clip_osx.mm deleted file mode 100644 index a862145..0000000 --- a/src/clip-lib/clip_osx.mm +++ /dev/null @@ -1,377 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2023 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip.h" -#include "clip_common.h" -#include "clip_lock_impl.h" - -#include -#include -#include - -#include -#include -#include - -namespace clip { - -namespace { - - format g_last_format = 100; - std::map g_name_to_format; - std::map g_format_to_name; - -} - -namespace osx { - -#if CLIP_ENABLE_IMAGE - - bool get_image_from_clipboard(NSPasteboard* pasteboard, - image* output_img, - image_spec* output_spec) - { - NSString* result = [pasteboard availableTypeFromArray: - [NSArray arrayWithObjects:NSPasteboardTypeTIFF,NSPasteboardTypePNG,nil]]; - - if (!result) - return false; - - NSData* data = [pasteboard dataForType:result]; - if (!data) - return false; - - NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData:data]; - - if ((bitmap.bitmapFormat & NSBitmapFormatFloatingPointSamples) || - (bitmap.planar)) { - error_handler e = get_error_handler(); - if (e) - e(ErrorCode::ImageNotSupported); - return false; - } - - image_spec spec; - spec.width = bitmap.pixelsWide; - spec.height = bitmap.pixelsHigh; - spec.bits_per_pixel = bitmap.bitsPerPixel; - spec.bytes_per_row = bitmap.bytesPerRow; - - // We need three samples for Red/Green/Blue - if (bitmap.samplesPerPixel >= 3) { - // Here we are guessing the bits per sample (generally 8, not - // sure how many bits per sample macOS uses for 16bpp - // NSBitmapFormat or if this format is even used). - int bits_per_sample = (bitmap.bitsPerPixel == 16 ? 5: 8); - int bits_shift = 0; - - // With alpha - if (bitmap.alpha) { - if (bitmap.bitmapFormat & NSBitmapFormatAlphaFirst) { - spec.alpha_shift = 0; - bits_shift += bits_per_sample; - } - else { - spec.alpha_shift = 3*bits_per_sample; - } - } - - unsigned long* masks = &spec.red_mask; - unsigned long* shifts = &spec.red_shift; - - // Red/green/blue shifts - for (unsigned long* shift=shifts; shift= 3 && - !(bitmap.bitmapFormat & NSBitmapFormatAlphaNonpremultiplied)) { - details::divide_rgb_by_alpha( - img, - true); // hasAlphaGreaterThanZero=true because we have valid alpha information - } - - std::swap(*output_img, img); - } - - return true; - } - -#endif // CLIP_ENABLE_IMAGE - -} // namespace osx - -lock::impl::impl(void*) : m_locked(true) { -} - -lock::impl::~impl() { -} - -bool lock::impl::clear() { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard clearContents]; - return true; - } -} - -bool lock::impl::is_convertible(format f) const { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - NSString* result = nil; - - if (f == text_format()) { - result = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:NSPasteboardTypeString]]; - } -#if CLIP_ENABLE_IMAGE - else if (f == image_format()) { - result = [pasteboard availableTypeFromArray: - [NSArray arrayWithObjects:NSPasteboardTypeTIFF,NSPasteboardTypePNG,nil]]; - } -#endif // CLIP_ENABLE_IMAGE - else { - auto it = g_format_to_name.find(f); - if (it != g_format_to_name.end()) { - const std::string& name = it->second; - NSString* string = [[NSString alloc] initWithBytesNoCopy:(void*)name.c_str() - length:name.size() - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - result = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:string]]; - } - } - - return (result ? true: false); - } -} - -bool lock::impl::set_data(format f, const char* buf, size_t len) { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - - if (f == text_format()) { - NSString* string = [[NSString alloc] initWithBytesNoCopy:(void*)buf - length:len - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - [pasteboard setString:string forType:NSPasteboardTypeString]; - return true; - } - else { - auto it = g_format_to_name.find(f); - if (it != g_format_to_name.end()) { - const std::string& formatName = it->second; - NSString* typeString = [[NSString alloc] - initWithBytesNoCopy:(void*)formatName.c_str() - length:formatName.size() - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - NSData* data = [NSData dataWithBytesNoCopy:(void*)buf - length:len - freeWhenDone:NO]; - - if ([pasteboard setData:data forType:typeString]) - return true; - } - } - return false; - } -} - -bool lock::impl::get_data(format f, char* buf, size_t len) const { - @autoreleasepool { - assert(buf); - if (!buf || !is_convertible(f)) - return false; - - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - - if (f == text_format()) { - NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; - int reqsize = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+1; - - assert(reqsize <= len); - if (reqsize > len) { - // Buffer is too small - return false; - } - - if (reqsize == 0) - return true; - - memcpy(buf, [string UTF8String], reqsize); - return true; - } - - auto it = g_format_to_name.find(f); - if (it == g_format_to_name.end()) - return false; - - const std::string& formatName = it->second; - NSString* typeString = - [[NSString alloc] initWithBytesNoCopy:(void*)formatName.c_str() - length:formatName.size() - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - - NSData* data = [pasteboard dataForType:typeString]; - if (!data) - return false; - - [data getBytes:buf length:len]; - return true; - } -} - -size_t lock::impl::get_data_length(format f) const { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - - if (f == text_format()) { - NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; - return [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+1; - } - - auto it = g_format_to_name.find(f); - if (it == g_format_to_name.end()) - return 0; - - const std::string& formatName = it->second; - NSString* typeString = - [[NSString alloc] initWithBytesNoCopy:(void*)formatName.c_str() - length:formatName.size() - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - - NSData* data = [pasteboard dataForType:typeString]; - if (!data) - return 0; - - return data.length; - } -} - -#if CLIP_ENABLE_IMAGE - -bool lock::impl::set_image(const image& image) { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - const image_spec& spec = image.spec(); - - NSBitmapFormat bitmapFormat = 0; - int samples_per_pixel = 0; - if (spec.alpha_mask) { - samples_per_pixel = 4; - if (spec.alpha_shift == 0) - bitmapFormat |= NSBitmapFormatAlphaFirst; - bitmapFormat |= NSBitmapFormatAlphaNonpremultiplied; - } - else if (spec.red_mask || spec.green_mask || spec.blue_mask) { - samples_per_pixel = 3; - } - else { - samples_per_pixel = 1; - } - - if (spec.bits_per_pixel == 32) - bitmapFormat |= NSBitmapFormatThirtyTwoBitLittleEndian; - else if (spec.bits_per_pixel == 16) - bitmapFormat |= NSBitmapFormatSixteenBitLittleEndian; - - std::vector planes(1); - planes[0] = (unsigned char*)image.data(); - - NSBitmapImageRep* bitmap = - [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:&planes[0] - pixelsWide:spec.width - pixelsHigh:spec.height - bitsPerSample:spec.bits_per_pixel / samples_per_pixel - samplesPerPixel:samples_per_pixel - hasAlpha:(spec.alpha_mask ? YES: NO) - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bitmapFormat:bitmapFormat - bytesPerRow:spec.bytes_per_row - bitsPerPixel:spec.bits_per_pixel]; - if (!bitmap) - return false; - - NSData* data = bitmap.TIFFRepresentation; - if (!data) - return false; - - if ([pasteboard setData:data forType:NSPasteboardTypeTIFF]) - return true; - - return false; - } -} - -bool lock::impl::get_image(image& img) const { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - return osx::get_image_from_clipboard(pasteboard, &img, nullptr); -} - -bool lock::impl::get_image_spec(image_spec& spec) const { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - return osx::get_image_from_clipboard(pasteboard, nullptr, &spec); -} - -#endif // CLIP_ENABLE_IMAGE - -format register_format(const std::string& name) { - // Check if the format is already registered - auto it = g_name_to_format.find(name); - if (it != g_name_to_format.end()) - return it->second; - - format new_format = g_last_format++; - g_name_to_format[name] = new_format; - g_format_to_name[new_format] = name; - return new_format; -} - -} // namespace clip diff --git a/src/clip-lib/clip_win.cpp b/src/clip-lib/clip_win.cpp deleted file mode 100644 index 058ea43..0000000 --- a/src/clip-lib/clip_win.cpp +++ /dev/null @@ -1,407 +0,0 @@ -// Clip Library -// Copyright (C) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip_win.h" - -#include "clip.h" -#include "clip_lock_impl.h" - -#include -#include -#include -#include -#include - -namespace clip { - -namespace { - -// Data type used as header for custom formats to indicate the exact -// size of the user custom data. This is necessary because it looks -// like GlobalSize() might not return the exact size, but a greater -// value. -typedef uint64_t CustomSizeT; - -class Hglobal { -public: - Hglobal() : m_handle(nullptr) { - } - - explicit Hglobal(HGLOBAL handle) : m_handle(handle) { - } - - explicit Hglobal(size_t len) : m_handle(GlobalAlloc(GHND, len)) { - } - - ~Hglobal() { - if (m_handle) - GlobalFree(m_handle); - } - - void release() { - m_handle = nullptr; - } - - operator HGLOBAL() { - return m_handle; - } - -private: - HGLOBAL m_handle; -}; - -// From: https://issues.chromium.org/issues/40080988#comment8 -// -// "Adds impersonation of the anonymous token around calls to the -// CloseClipboard() system call. On Windows 8+ the win32k driver -// captures the access token of the caller and makes it available to -// other users on the desktop through the system call -// GetClipboardAccessToken(). This introduces a risk of privilege -// escalation in sandboxed processes. By performing the -// impersonation then whenever Chrome writes data to the clipboard -// only the anonymous token is available." -// -class AnonymousTokenImpersonator { -public: - AnonymousTokenImpersonator() - : m_must_revert(ImpersonateAnonymousToken(GetCurrentThread())) - {} - - ~AnonymousTokenImpersonator() { - if (m_must_revert) - RevertToSelf(); - } -private: - const bool m_must_revert; -}; - -} // anonymous namespace - -lock::impl::impl(void* hwnd) : m_locked(false) { - for (int i=0; i<5; ++i) { - if (OpenClipboard((HWND)hwnd)) { - m_locked = true; - break; - } - Sleep(20); - } - - if (!m_locked) { - error_handler e = get_error_handler(); - if (e) - e(ErrorCode::CannotLock); - } -} - -lock::impl::~impl() { - if (m_locked) { - AnonymousTokenImpersonator guard; - CloseClipboard(); - } -} - -bool lock::impl::clear() { - return (EmptyClipboard() ? true: false); -} - -bool lock::impl::is_convertible(format f) const { - if (f == text_format()) { - return - (IsClipboardFormatAvailable(CF_TEXT) || - IsClipboardFormatAvailable(CF_UNICODETEXT) || - IsClipboardFormatAvailable(CF_OEMTEXT)); - } -#if CLIP_ENABLE_IMAGE - else if (f == image_format()) { - return (IsClipboardFormatAvailable(CF_DIB) || - win::wic_image_format_available(nullptr) != nullptr); - } -#endif // CLIP_ENABLE_IMAGE - else - return IsClipboardFormatAvailable(f); -} - -bool lock::impl::set_data(format f, const char* buf, size_t len) { - bool result = false; - - if (f == text_format()) { - if (len > 0) { - int reqsize = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0); - if (reqsize > 0) { - ++reqsize; - - Hglobal hglobal(sizeof(WCHAR)*reqsize); - LPWSTR lpstr = static_cast(GlobalLock(hglobal)); - MultiByteToWideChar(CP_UTF8, 0, buf, len, lpstr, reqsize); - GlobalUnlock(hglobal); - - result = (SetClipboardData(CF_UNICODETEXT, hglobal)) ? true: false; - if (result) - hglobal.release(); - } - } - } - else { - Hglobal hglobal(len+sizeof(CustomSizeT)); - if (hglobal) { - auto dst = (uint8_t*)GlobalLock(hglobal); - if (dst) { - *((CustomSizeT*)dst) = len; - memcpy(dst+sizeof(CustomSizeT), buf, len); - GlobalUnlock(hglobal); - result = (SetClipboardData(f, hglobal) ? true: false); - if (result) - hglobal.release(); - } - } - } - - return result; -} - -bool lock::impl::get_data(format f, char* buf, size_t len) const { - assert(buf); - - if (!buf || !is_convertible(f)) - return false; - - bool result = false; - - if (f == text_format()) { - if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { - HGLOBAL hglobal = GetClipboardData(CF_UNICODETEXT); - if (hglobal) { - LPWSTR lpstr = static_cast(GlobalLock(hglobal)); - if (lpstr) { - size_t reqsize = - WideCharToMultiByte(CP_UTF8, 0, lpstr, -1, - nullptr, 0, nullptr, nullptr); - - assert(reqsize <= len); - if (reqsize <= len) { - WideCharToMultiByte(CP_UTF8, 0, lpstr, -1, - buf, reqsize, nullptr, nullptr); - result = true; - } - GlobalUnlock(hglobal); - } - } - } - else if (IsClipboardFormatAvailable(CF_TEXT)) { - HGLOBAL hglobal = GetClipboardData(CF_TEXT); - if (hglobal) { - LPSTR lpstr = static_cast(GlobalLock(hglobal)); - if (lpstr) { - // TODO check length - memcpy(buf, lpstr, len); - result = true; - GlobalUnlock(hglobal); - } - } - } - } - else { - if (IsClipboardFormatAvailable(f)) { - HGLOBAL hglobal = GetClipboardData(f); - if (hglobal) { - const SIZE_T total_size = GlobalSize(hglobal); - auto ptr = (const uint8_t*)GlobalLock(hglobal); - if (ptr) { - CustomSizeT reqsize = *((CustomSizeT*)ptr); - - // If the registered length of data in the first CustomSizeT - // number of bytes of the hglobal data is greater than the - // GlobalSize(hglobal), something is wrong, it should not - // happen. - assert(reqsize <= total_size); - if (reqsize > total_size) - reqsize = total_size - sizeof(CustomSizeT); - - if (reqsize <= len) { - memcpy(buf, ptr+sizeof(CustomSizeT), reqsize); - result = true; - } - GlobalUnlock(hglobal); - } - } - } - } - - return result; -} - -size_t lock::impl::get_data_length(format f) const { - size_t len = 0; - - if (f == text_format()) { - if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { - HGLOBAL hglobal = GetClipboardData(CF_UNICODETEXT); - if (hglobal) { - LPWSTR lpstr = static_cast(GlobalLock(hglobal)); - if (lpstr) { - len = - WideCharToMultiByte(CP_UTF8, 0, lpstr, -1, - nullptr, 0, nullptr, nullptr); - GlobalUnlock(hglobal); - } - } - } - else if (IsClipboardFormatAvailable(CF_TEXT)) { - HGLOBAL hglobal = GetClipboardData(CF_TEXT); - if (hglobal) { - LPSTR lpstr = (LPSTR)GlobalLock(hglobal); - if (lpstr) { - len = strlen(lpstr) + 1; - GlobalUnlock(hglobal); - } - } - } - } - else if (f != empty_format()) { - if (IsClipboardFormatAvailable(f)) { - HGLOBAL hglobal = GetClipboardData(f); - if (hglobal) { - const SIZE_T total_size = GlobalSize(hglobal); - auto ptr = (const uint8_t*)GlobalLock(hglobal); - if (ptr) { - len = *((CustomSizeT*)ptr); - - assert(len <= total_size); - if (len > total_size) - len = total_size - sizeof(CustomSizeT); - - GlobalUnlock(hglobal); - } - } - } - } - - return len; -} - -#if CLIP_ENABLE_LIST_FORMATS - -std::vector lock::impl::list_formats() const { - static const char* standard_formats[CF_MAX] = { - "", "CF_TEXT", "CF_BITMAP", "CF_METAFILEPICT", - "CF_SYLK", "CF_DIF", "CF_TIFF", "CF_OEMTEXT", - "CF_DIB", "CF_PALETTE", "CF_PENDATA", "CF_RIFF", - "CF_WAVE", "CF_UNICODETEXT", "CF_ENHMETAFILE", "CF_HDROP", - "CF_LOCALE", "CF_DIBV5" - }; - - std::vector formats; - std::vector format_name(512); - - formats.reserve(CountClipboardFormats()); - - UINT format_id = EnumClipboardFormats(0); - while (format_id != 0) { - if (format_id >= CF_TEXT && format_id < CF_MAX) { - // Standard clipboard format - formats.emplace_back(format_id, standard_formats[format_id]); - } - // Get user-defined format name - else { - int size = GetClipboardFormatNameA( - format_id, - format_name.data(), - format_name.size()); - - formats.emplace_back(format_id, std::string(format_name.data(), size)); - } - - format_id = EnumClipboardFormats(format_id); - } - - return formats; -} - -#endif // CLIP_ENABLE_LIST_FORMATS - -#if CLIP_ENABLE_IMAGE - -bool lock::impl::set_image(const image& image) { - const image_spec& spec = image.spec(); - - // Add the PNG clipboard format for images with alpha channel - // (useful to communicate with some Windows programs that only use - // alpha data from PNG clipboard format) - if (spec.bits_per_pixel == 32 && - spec.alpha_mask) { - UINT png_format = RegisterClipboardFormatA("PNG"); - if (png_format) { - Hglobal png_handle(win::write_png(image)); - if (png_handle) - SetClipboardData(png_format, png_handle); - } - } - - Hglobal hmem(clip::win::create_dibv5(image)); - if (!hmem) - return false; - - SetClipboardData(CF_DIBV5, hmem); - return true; -} - -bool lock::impl::get_image(image& output_img) const { - // Tries to get the first image format that can be read using WIC - // ("PNG", "JPG", "GIF", etc). - UINT cbformat; - if (auto read_img = win::wic_image_format_available(&cbformat)) { - HANDLE handle = GetClipboardData(cbformat); - if (handle) { - size_t size = GlobalSize(handle); - uint8_t* data = (uint8_t*)GlobalLock(handle); - bool result = read_img(data, size, &output_img, nullptr); - GlobalUnlock(handle); - if (result) - return true; - } - } - - // If we couldn't find any, we try to use the regular DIB format. - win::BitmapInfo bi; - return bi.to_image(output_img); -} - -bool lock::impl::get_image_spec(image_spec& spec) const { - UINT cbformat; - if (auto read_img = win::wic_image_format_available(&cbformat)) { - HANDLE handle = GetClipboardData(cbformat); - if (handle) { - size_t size = GlobalSize(handle); - uint8_t* data = (uint8_t*)GlobalLock(handle); - bool result = read_img(data, size, nullptr, &spec); - GlobalUnlock(handle); - if (result) - return true; - } - } - - win::BitmapInfo bi; - if (!bi.is_valid()) - return false; - bi.fill_spec(spec); - return true; -} - -#endif // CLIP_ENABLE_IMAGE - -format register_format(const std::string& name) { - int reqsize = 1+MultiByteToWideChar(CP_UTF8, 0, - name.c_str(), name.size(), NULL, 0); - std::vector buf(reqsize); - MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name.size(), - &buf[0], reqsize); - - // From MSDN, registered clipboard formats are identified by values - // in the range 0xC000 through 0xFFFF. - return (format)RegisterClipboardFormatW(&buf[0]); -} - -} // namespace clip diff --git a/src/clip-lib/clip_win.h b/src/clip-lib/clip_win.h deleted file mode 100644 index 8efccd8..0000000 --- a/src/clip-lib/clip_win.h +++ /dev/null @@ -1,26 +0,0 @@ -// Clip Library -// Copyright (c) 2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_WIN_H_INCLUDED -#define CLIP_WIN_H_INCLUDED -#pragma once - -#include - -#ifndef LCS_WINDOWS_COLOR_SPACE -#define LCS_WINDOWS_COLOR_SPACE 'Win ' -#endif - -#ifndef CF_DIBV5 -#define CF_DIBV5 17 -#endif - -#if CLIP_ENABLE_IMAGE - #include "clip_win_bmp.h" - #include "clip_win_wic.h" -#endif - -#endif // CLIP_WIN_H_INCLUDED diff --git a/src/clip-lib/clip_win_bmp.cpp b/src/clip-lib/clip_win_bmp.cpp deleted file mode 100644 index ffb0d6f..0000000 --- a/src/clip-lib/clip_win_bmp.cpp +++ /dev/null @@ -1,347 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip_win_bmp.h" - -#include "clip.h" -#include "clip_common.h" - -#include - -namespace clip { -namespace win { - -namespace { - -unsigned long get_shift_from_mask(unsigned long mask) { - unsigned long shift = 0; - for (shift=0; shiftbV5BitCount == 32 && - ((b5->bV5Compression == BI_RGB) || - (b5->bV5Compression == BI_BITFIELDS && - b5->bV5RedMask && b5->bV5GreenMask && - b5->bV5BlueMask && b5->bV5AlphaMask))) { - width = b5->bV5Width; - height = b5->bV5Height; - bit_count = b5->bV5BitCount; - compression = b5->bV5Compression; - if (compression == BI_BITFIELDS) { - red_mask = b5->bV5RedMask; - green_mask = b5->bV5GreenMask; - blue_mask = b5->bV5BlueMask; - alpha_mask = b5->bV5AlphaMask; - } - else { - red_mask = 0xff0000; - green_mask = 0xff00; - blue_mask = 0xff; - alpha_mask = 0xff000000; - } - return true; - } - return false; -} - -bool BitmapInfo::load_from(BITMAPINFO* bi) { - if (!bi) - return false; - - width = bi->bmiHeader.biWidth; - height = bi->bmiHeader.biHeight; - bit_count = bi->bmiHeader.biBitCount; - compression = bi->bmiHeader.biCompression; - - if (compression == BI_BITFIELDS) { - red_mask = *((uint32_t*)&bi->bmiColors[0]); - green_mask = *((uint32_t*)&bi->bmiColors[1]); - blue_mask = *((uint32_t*)&bi->bmiColors[2]); - if (bit_count == 32) - alpha_mask = 0xff000000; - return true; - } - if (compression == BI_RGB) { - switch (bit_count) { - case 32: - red_mask = 0xff0000; - green_mask = 0xff00; - blue_mask = 0xff; - alpha_mask = 0xff000000; - break; - case 24: - case 8: // We return 8bpp images as 24bpp - red_mask = 0xff0000; - green_mask = 0xff00; - blue_mask = 0xff; - break; - case 16: - red_mask = 0x7c00; - green_mask = 0x03e0; - blue_mask = 0x001f; - break; - } - return true; - } - return false; -} - -BitmapInfo::BitmapInfo(BITMAPV5HEADER* pb5) { - if (load_from(pb5)) - b5 = pb5; -} - -BitmapInfo::BitmapInfo(BITMAPINFO* pbi) { - if (load_from(pbi)) - bi = pbi; -} - -void BitmapInfo::fill_spec(image_spec& spec) const { - spec.width = width; - spec.height = (height >= 0 ? height: -height); - // We convert indexed to 24bpp RGB images to match the OS X behavior - spec.bits_per_pixel = bit_count; - if (spec.bits_per_pixel <= 8) - spec.bits_per_pixel = 24; - spec.bytes_per_row = width*((spec.bits_per_pixel+7)/8); - spec.red_mask = red_mask; - spec.green_mask = green_mask; - spec.blue_mask = blue_mask; - spec.alpha_mask = alpha_mask; - - switch (spec.bits_per_pixel) { - - case 24: { - // We need one extra byte to avoid a crash updating the last - // pixel on last row using: - // - // *((uint32_t*)ptr) = pixel24bpp; - // - ++spec.bytes_per_row; - - // Align each row to 32bpp - int padding = (4-(spec.bytes_per_row&3))&3; - spec.bytes_per_row += padding; - break; - } - - case 16: { - int padding = (4-(spec.bytes_per_row&3))&3; - spec.bytes_per_row += padding; - break; - } - } - - unsigned long* masks = &spec.red_mask; - unsigned long* shifts = &spec.red_shift; - for (unsigned long* shift=shifts, *mask=masks; shiftbV5Size; - else - src = ((uint8_t*)bi) + bi->bmiHeader.biSize; - if (compression == BI_BITFIELDS) - src += sizeof(RGBQUAD)*3; - } - - if (src) { - const int src_bytes_per_row = spec.width*((bit_count+7)/8); - const int padding = (4-(src_bytes_per_row&3))&3; - - for (long y=spec.height-1; y>=0; --y, src+=src_bytes_per_row+padding) { - char* dst = img.data()+y*spec.bytes_per_row; - std::copy(src, src+src_bytes_per_row, dst); - } - } - - // Windows uses premultiplied RGB values, and we use straight - // alpha. So we have to divide all RGB values by its alpha. - if (bit_count == 32 && spec.alpha_mask) { - details::divide_rgb_by_alpha(img); - } - break; - } - - case 8: { - assert(bi); - - const int colors = (bi->bmiHeader.biClrUsed > 0 ? bi->bmiHeader.biClrUsed: 256); - std::vector palette(colors); - for (int c=0; cbmiColors[c].rgbRed << spec.red_shift) | - (bi->bmiColors[c].rgbGreen << spec.green_shift) | - (bi->bmiColors[c].rgbBlue << spec.blue_shift); - } - - const uint8_t* src = (((uint8_t*)bi) + bi->bmiHeader.biSize + sizeof(RGBQUAD)*colors); - const int padding = (4-(spec.width&3))&3; - - for (long y=spec.height-1; y>=0; --y, src+=padding) { - char* dst = img.data()+y*spec.bytes_per_row; - - for (unsigned long x=0; x= colors) - idx = colors-1; - - *((uint32_t*)dst) = palette[idx]; - } - } - break; - } - } - - std::swap(output_img, img); - return true; -} - -HGLOBAL create_dibv5(const image& image) { - const image_spec& spec = image.spec(); - image_spec out_spec = spec; - - int palette_colors = 0; - int padding = 0; - switch (spec.bits_per_pixel) { - case 24: padding = (4-((spec.width*3)&3))&3; break; - case 16: padding = ((4-((spec.width*2)&3))&3)/2; break; - case 8: padding = (4-(spec.width&3))&3; break; - } - out_spec.bytes_per_row += padding; - - // Create the BITMAPV5HEADER structure - HGLOBAL hmem = - GlobalAlloc( - GHND, - sizeof(BITMAPV5HEADER) - + palette_colors*sizeof(RGBQUAD) - + out_spec.bytes_per_row*out_spec.height); - if (!hmem) - return nullptr; - - out_spec.red_mask = 0x00ff0000; - out_spec.green_mask = 0xff00; - out_spec.blue_mask = 0xff; - out_spec.alpha_mask = 0xff000000; - out_spec.red_shift = 16; - out_spec.green_shift = 8; - out_spec.blue_shift = 0; - out_spec.alpha_shift = 24; - - BITMAPV5HEADER* bi = (BITMAPV5HEADER*)GlobalLock(hmem); - bi->bV5Size = sizeof(BITMAPV5HEADER); - bi->bV5Width = out_spec.width; - bi->bV5Height = out_spec.height; - bi->bV5Planes = 1; - bi->bV5BitCount = (WORD)out_spec.bits_per_pixel; - bi->bV5Compression = BI_RGB; - bi->bV5SizeImage = out_spec.bytes_per_row*spec.height; - bi->bV5RedMask = out_spec.red_mask; - bi->bV5GreenMask = out_spec.green_mask; - bi->bV5BlueMask = out_spec.blue_mask; - bi->bV5AlphaMask = out_spec.alpha_mask; - bi->bV5CSType = LCS_WINDOWS_COLOR_SPACE; - bi->bV5Intent = LCS_GM_GRAPHICS; - bi->bV5ClrUsed = 0; - - switch (spec.bits_per_pixel) { - case 32: { - const char* src = image.data(); - char* dst = (((char*)bi)+bi->bV5Size) + (out_spec.height-1)*out_spec.bytes_per_row; - for (long y=spec.height-1; y>=0; --y) { - const uint32_t* src_x = (const uint32_t*)src; - uint32_t* dst_x = (uint32_t*)dst; - - for (unsigned long x=0; x> spec.red_shift ); - int g = ((c & spec.green_mask) >> spec.green_shift); - int b = ((c & spec.blue_mask ) >> spec.blue_shift ); - int a = ((c & spec.alpha_mask) >> spec.alpha_shift); - - // Windows requires premultiplied RGBA values - r = r * a / 255; - g = g * a / 255; - b = b * a / 255; - - *dst_x = - (r << out_spec.red_shift ) | - (g << out_spec.green_shift) | - (b << out_spec.blue_shift ) | - (a << out_spec.alpha_shift); - } - - src += spec.bytes_per_row; - dst -= out_spec.bytes_per_row; - } - break; - } - default: - GlobalUnlock(hmem); - GlobalFree(hmem); - - error_handler e = get_error_handler(); - if (e) - e(ErrorCode::ImageNotSupported); - return nullptr; - } - - GlobalUnlock(hmem); - return hmem; -} - -} // namespace win -} // namespace clip diff --git a/src/clip-lib/clip_win_bmp.h b/src/clip-lib/clip_win_bmp.h deleted file mode 100644 index 5c9c38b..0000000 --- a/src/clip-lib/clip_win_bmp.h +++ /dev/null @@ -1,66 +0,0 @@ -// Clip Library -// Copyright (c) 2015-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_WIN_BMP_H_INCLUDED -#define CLIP_WIN_BMP_H_INCLUDED -#pragma once - -#if !CLIP_ENABLE_IMAGE - #error This file can be include only when CLIP_ENABLE_IMAGE is defined -#endif - -#include - -#include - -namespace clip { - -class image; -struct image_spec; - -namespace win { - -struct BitmapInfo { - BITMAPV5HEADER* b5 = nullptr; - BITMAPINFO* bi = nullptr; - int width = 0; - int height = 0; - uint16_t bit_count = 0; - uint32_t compression = 0; - uint32_t red_mask = 0; - uint32_t green_mask = 0; - uint32_t blue_mask = 0; - uint32_t alpha_mask = 0; - - BitmapInfo(); - explicit BitmapInfo(BITMAPV5HEADER* pb5); - explicit BitmapInfo(BITMAPINFO* pbi); - - bool is_valid() const { - return (b5 || bi); - } - - void fill_spec(image_spec& spec) const; - - // Fills the output_img with the data provided by this - // BitmapInfo. Returns true if it was able to fill the output image - // or false otherwise. - bool to_image(image& output_img) const; - -private: - bool load_from(BITMAPV5HEADER* b5); - bool load_from(BITMAPINFO* bi); -}; - -// Returns a handle to the HGLOBAL memory reserved to create a DIBV5 -// based on the image passed by parameter. Returns null if it cannot -// create the handle. -HGLOBAL create_dibv5(const image& image); - -} // namespace win -} // namespace clip - -#endif // CLIP_WIN_BMP_H_INCLUDED diff --git a/src/clip-lib/clip_win_wic.cpp b/src/clip-lib/clip_win_wic.cpp deleted file mode 100644 index dd37158..0000000 --- a/src/clip-lib/clip_win_wic.cpp +++ /dev/null @@ -1,472 +0,0 @@ -// Clip Library -// Copyright (c) 2020-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip_win_wic.h" - -#include "clip.h" - -#include -#include - -#include -#include - -namespace clip { -namespace win { - -namespace { - -// Successful calls to CoInitialize() (S_OK or S_FALSE) must match -// the calls to CoUninitialize(). -// From: https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize#remarks -struct coinit { - HRESULT hr; - coinit() { - hr = CoInitialize(nullptr); - } - ~coinit() { - if (hr == S_OK || hr == S_FALSE) - CoUninitialize(); - } -}; - -template -class comptr { -public: - comptr() { } - explicit comptr(T* ptr) : m_ptr(ptr) { } - comptr(const comptr&) = delete; - comptr& operator=(const comptr&) = delete; - ~comptr() { reset(); } - - T** operator&() { return &m_ptr; } - T* operator->() { return m_ptr; } - bool operator!() const { return !m_ptr; } - - T* get() { return m_ptr; } - void reset() { - if (m_ptr) { - m_ptr->Release(); - m_ptr = nullptr; - } - } -private: - T* m_ptr = nullptr; -}; - -#ifdef CLIP_SUPPORT_WINXP -class hmodule { -public: - hmodule(LPCWSTR name) : m_ptr(LoadLibraryW(name)) { } - hmodule(const hmodule&) = delete; - hmodule& operator=(const hmodule&) = delete; - ~hmodule() { - if (m_ptr) - FreeLibrary(m_ptr); - } - - operator HMODULE() { return m_ptr; } - bool operator!() const { return !m_ptr; } -private: - HMODULE m_ptr = nullptr; -}; -#endif - -struct WicImageFormat { - const char* names[3]; // Alternative names of this format - UINT ids[3]; // Clipboard format ID for each name of this format - ReadWicImageFormatFunc read; // Function used to decode data in this format -}; - -WicImageFormat wic_image_formats[] = { - { { "PNG", "image/png", nullptr }, { 0, 0, 0 }, read_png }, - { { "JPG", "image/jpeg", "JPEG" }, { 0, 0, 0 }, read_jpg }, - { { "BMP", "image/bmp", nullptr }, { 0, 0, 0 }, read_bmp }, - { { "GIF", "image/gif", nullptr }, { 0, 0, 0 }, read_gif } -}; - -} // anonymous namespace - -ReadWicImageFormatFunc wic_image_format_available(UINT* output_cbformat) { - for (auto& fmt : wic_image_formats) { - for (int i=0; i<3; ++i) { - const char* name = fmt.names[i]; - if (!name) - break; - - // Although RegisterClipboardFormatA() already returns the same - // value for the same "name" (even for different apps), we - // prefer to cache the value to avoid calling - // RegisterClipboardFormatA() several times (as internally that - // function must do some kind of hash map name -> ID - // conversion). - UINT cbformat = fmt.ids[i]; - if (cbformat == 0) - fmt.ids[i] = cbformat = RegisterClipboardFormatA(name); - - if (cbformat && IsClipboardFormatAvailable(cbformat)) { - if (output_cbformat) - *output_cbformat = cbformat; - return fmt.read; - } - } - } - return nullptr; -} - -////////////////////////////////////////////////////////////////////// -// Encode the image as PNG format - -bool write_png_on_stream(const image& image, - IStream* stream) { - const image_spec& spec = image.spec(); - - comptr encoder; - HRESULT hr = CoCreateInstance(CLSID_WICPngEncoder, - nullptr, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&encoder)); - if (FAILED(hr)) - return false; - - hr = encoder->Initialize(stream, WICBitmapEncoderNoCache); - if (FAILED(hr)) - return false; - - comptr frame; - comptr options; - hr = encoder->CreateNewFrame(&frame, &options); - if (FAILED(hr)) - return false; - - hr = frame->Initialize(options.get()); - if (FAILED(hr)) - return false; - - // PNG encoder (and decoder) only supports GUID_WICPixelFormat32bppBGRA for 32bpp. - // See: https://docs.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats#png-native-codec - WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppBGRA; - hr = frame->SetPixelFormat(&pixelFormat); - if (FAILED(hr)) - return false; - - hr = frame->SetSize(spec.width, spec.height); - if (FAILED(hr)) - return false; - - std::vector buf; - uint8_t* ptr = (uint8_t*)image.data(); - int bytes_per_row = spec.bytes_per_row; - - // Convert to GUID_WICPixelFormat32bppBGRA if needed - if (spec.red_mask != 0xff0000 || - spec.green_mask != 0xff00 || - spec.blue_mask != 0xff || - spec.alpha_mask != 0xff000000) { - buf.resize(spec.width * spec.height); - uint32_t* dst = (uint32_t*)&buf[0]; - uint32_t* src = (uint32_t*)image.data(); - for (int y=0; y> spec.red_shift ) << 16) | - (((c & spec.green_mask) >> spec.green_shift) << 8) | - (((c & spec.blue_mask ) >> spec.blue_shift ) ) | - (((c & spec.alpha_mask) >> spec.alpha_shift) << 24)); - ++dst; - ++src; - } - src = (uint32_t*)(((uint8_t*)src_line_start) + spec.bytes_per_row); - } - ptr = (uint8_t*)&buf[0]; - bytes_per_row = 4 * spec.width; - } - - hr = frame->WritePixels(spec.height, - bytes_per_row, - bytes_per_row * spec.height, - (BYTE*)ptr); - if (FAILED(hr)) - return false; - - hr = frame->Commit(); - if (FAILED(hr)) - return false; - - hr = encoder->Commit(); - if (FAILED(hr)) - return false; - - return true; -} - -HGLOBAL write_png(const image& image) { - coinit com; - - comptr stream; - HRESULT hr = CreateStreamOnHGlobal(nullptr, false, &stream); - if (FAILED(hr)) - return nullptr; - - bool result = write_png_on_stream(image, stream.get()); - - HGLOBAL handle; - hr = GetHGlobalFromStream(stream.get(), &handle); - if (result) - return handle; - - GlobalFree(handle); - return nullptr; -} - -IStream* create_stream(const BYTE* pInit, UINT cbInit) -{ -#ifdef CLIP_SUPPORT_WINXP - // Pull SHCreateMemStream from shlwapi.dll by ordinal 12 - // for Windows XP support - // From: https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream#remarks - - typedef IStream*(WINAPI * SHCreateMemStreamPtr)(const BYTE* pInit, - UINT cbInit); - hmodule shlwapiDll(L"shlwapi.dll"); - if (!shlwapiDll) - return false; - - auto SHCreateMemStream = reinterpret_cast( - GetProcAddress(shlwapiDll, (LPCSTR)12)); - if (!SHCreateMemStream) - return false; -#endif - return SHCreateMemStream(pInit, cbInit); -} - -image_spec spec_from_pixelformat(const WICPixelFormatGUID& pixelFormat, unsigned long w, unsigned long h) -{ - image_spec spec; - spec.width = w; - spec.height = h; - if (pixelFormat == GUID_WICPixelFormat32bppBGRA || - pixelFormat == GUID_WICPixelFormat32bppBGR) { - spec.bits_per_pixel = 32; - spec.red_mask = 0xff0000; - spec.green_mask = 0xff00; - spec.blue_mask = 0xff; - spec.alpha_mask = 0xff000000; - spec.red_shift = 16; - spec.green_shift = 8; - spec.blue_shift = 0; - spec.alpha_shift = 24; - // Reset mask and shift for BGR pixel format. - if (pixelFormat == GUID_WICPixelFormat32bppBGR) { - spec.alpha_mask = 0; - spec.alpha_shift = 0; - } - } - else if (pixelFormat == GUID_WICPixelFormat24bppBGR || - pixelFormat == GUID_WICPixelFormat8bppIndexed) { - spec.bits_per_pixel = 24; - spec.red_mask = 0xff0000; - spec.green_mask = 0xff00; - spec.blue_mask = 0xff; - spec.alpha_mask = 0; - spec.red_shift = 16; - spec.green_shift = 8; - spec.blue_shift = 0; - spec.alpha_shift = 0; - } - spec.bytes_per_row = ((w*spec.bits_per_pixel+31) / 32) * 4; - return spec; -} - -// Tries to decode the input buf of size len using the specified -// decoders. If output_image is not null, the decoded image is -// returned there, if output_spec is not null then the image -// specifications are set there. -bool decode(const GUID decoder_clsid1, - const GUID decoder_clsid2, - const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec) -{ - coinit com; - - comptr decoder; - HRESULT hr = CoCreateInstance(decoder_clsid1, nullptr, - CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&decoder)); - if (FAILED(hr) && decoder_clsid2 != GUID_NULL) { - hr = CoCreateInstance(decoder_clsid2, nullptr, - CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&decoder)); - } - if (FAILED(hr)) - return false; - - // Can decoder be nullptr if hr is S_OK/successful? We've received - // some crash reports that might indicate this. - if (!decoder) - return false; - - comptr stream(create_stream(buf, len)); - if (!stream) - return false; - - hr = decoder->Initialize(stream.get(), WICDecodeMetadataCacheOnDemand); - if (FAILED(hr)) - return false; - - comptr frame; - hr = decoder->GetFrame(0, &frame); - if (FAILED(hr)) - return false; - - WICPixelFormatGUID pixelFormat; - hr = frame->GetPixelFormat(&pixelFormat); - if (FAILED(hr)) - return false; - - // Only support these pixel formats - // TODO add support for more pixel formats - if (pixelFormat != GUID_WICPixelFormat32bppBGRA && - pixelFormat != GUID_WICPixelFormat32bppBGR && - pixelFormat != GUID_WICPixelFormat24bppBGR && - pixelFormat != GUID_WICPixelFormat8bppIndexed) - return false; - - UINT width = 0, height = 0; - hr = frame->GetSize(&width, &height); - if (FAILED(hr)) - return false; - - image_spec spec = spec_from_pixelformat(pixelFormat, width, height); - - if (output_spec) - *output_spec = spec; - - image img; - if (output_image) { - if (pixelFormat == GUID_WICPixelFormat8bppIndexed) { - std::vector pixels(spec.width * spec.height); - hr = frame->CopyPixels(nullptr, // Entire bitmap - spec.width, - spec.width * spec.height, - pixels.data()); - - if (FAILED(hr)) - return false; - - comptr factory; - HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, - nullptr, - CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&factory)); - if (FAILED(hr)) - return false; - - comptr palette; - hr = factory->CreatePalette(&palette); - if (FAILED(hr)) - return false; - - hr = frame->CopyPalette(palette.get()); - if (FAILED(hr)) - return false; - - UINT numcolors; - hr = palette->GetColorCount(&numcolors); - if (FAILED(hr)) - return false; - - UINT actualNumcolors; - std::vector colors(numcolors); - hr = palette->GetColors(numcolors, colors.data(), &actualNumcolors); - if (FAILED(hr)) - return false; - - BOOL hasAlpha = false; - palette->HasAlpha(&hasAlpha); - if (hasAlpha) { - spec = spec_from_pixelformat(GUID_WICPixelFormat32bppBGRA, width, height); - } - - img = image(spec); - char* dst = img.data(); - BYTE* src = pixels.data(); - for (int y = 0; y < spec.height; ++y) { - char* dst_x = dst; - for (int x = 0; x < spec.width; ++x, dst_x+=spec.bits_per_pixel/8, ++src) { - *((uint32_t*)dst_x) = (*src < numcolors ? colors[*src] : 0); - } - dst += spec.bytes_per_row; - } - } - else { - img = image(spec); - hr = frame->CopyPixels(nullptr, // Entire bitmap - spec.bytes_per_row, - spec.bytes_per_row * spec.height, - (BYTE*)img.data()); - if (FAILED(hr)) - return false; - } - - - std::swap(*output_image, img); - } - - return true; -} - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from PNG format - -bool read_png(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec) { - return decode(CLSID_WICPngDecoder2, CLSID_WICPngDecoder1, - buf, len, output_image, output_spec); -} - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from JPEG format - -bool read_jpg(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec) -{ - return decode(CLSID_WICJpegDecoder, GUID_NULL, - buf, len, output_image, output_spec); -} - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from GIF format - -bool read_gif(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec) -{ - return decode(CLSID_WICGifDecoder, GUID_NULL, - buf, len, output_image, output_spec); -} - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from BMP format - -bool read_bmp(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec) -{ - return decode(CLSID_WICBmpDecoder, GUID_NULL, - buf, len, output_image, output_spec); -} - -} // namespace win -} // namespace clip diff --git a/src/clip-lib/clip_win_wic.h b/src/clip-lib/clip_win_wic.h deleted file mode 100644 index dfef88c..0000000 --- a/src/clip-lib/clip_win_wic.h +++ /dev/null @@ -1,76 +0,0 @@ -// Clip Library -// Copyright (c) 2020-2024 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef CLIP_WIN_WIC_H_INCLUDED -#define CLIP_WIN_WIC_H_INCLUDED -#pragma once - -#if !CLIP_ENABLE_IMAGE - #error This file can be include only when CLIP_ENABLE_IMAGE is defined -#endif - -#include - -#include - -namespace clip { - -class image; -struct image_spec; - -namespace win { - -typedef bool (*ReadWicImageFormatFunc)(const uint8_t*, - const UINT, - clip::image*, - clip::image_spec*); - -ReadWicImageFormatFunc wic_image_format_available(UINT* output_cbformat); - -////////////////////////////////////////////////////////////////////// -// Encode the image as PNG format - -bool write_png_on_stream(const image& image, IStream* stream); - -HGLOBAL write_png(const image& image); - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from PNG format - -bool read_png(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec); - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from JPEG format - -bool read_jpg(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec); - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from GIF format - -bool read_gif(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec); - -////////////////////////////////////////////////////////////////////// -// Decode the clipboard data from BMP format - -bool read_bmp(const uint8_t* buf, - const UINT len, - image* output_image, - image_spec* output_spec); - - -} // namespace win -} // namespace clip - -#endif // CLIP_WIN_WIC_H_INCLUDED diff --git a/src/clip-lib/clip_x11.cpp b/src/clip-lib/clip_x11.cpp deleted file mode 100644 index b12d09f..0000000 --- a/src/clip-lib/clip_x11.cpp +++ /dev/null @@ -1,1123 +0,0 @@ -// Clip Library -// Copyright (c) 2018-2022 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip.h" -#include "clip_lock_impl.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if CLIP_ENABLE_IMAGE && HAVE_PNG_H - #include "clip_x11_png.h" -#endif - -#define CLIP_SUPPORT_SAVE_TARGETS 1 - -namespace clip { - -namespace { - -enum CommonAtom { - ATOM, - INCR, - TARGETS, - CLIPBOARD, -#ifdef HAVE_PNG_H - MIME_IMAGE_PNG, -#endif -#ifdef CLIP_SUPPORT_SAVE_TARGETS - ATOM_PAIR, - SAVE_TARGETS, - MULTIPLE, - CLIPBOARD_MANAGER, -#endif -}; - -const char* kCommonAtomNames[] = { - "ATOM", - "INCR", - "TARGETS", - "CLIPBOARD", -#ifdef HAVE_PNG_H - "image/png", -#endif -#ifdef CLIP_SUPPORT_SAVE_TARGETS - "ATOM_PAIR", - "SAVE_TARGETS", - "MULTIPLE", - "CLIPBOARD_MANAGER", -#endif -}; - -const int kBaseForCustomFormats = 100; - -class Manager { -public: - typedef std::shared_ptr> buffer_ptr; - typedef std::vector atoms; - typedef std::function notify_callback; - - Manager() - : m_lock(m_mutex, std::defer_lock) - , m_connection(xcb_connect(nullptr, nullptr)) - , m_window(0) - , m_incr_process(false) { - if (!m_connection) - return; - - const xcb_setup_t* setup = xcb_get_setup(m_connection); - if (!setup) - return; - - xcb_screen_t* screen = xcb_setup_roots_iterator(setup).data; - if (!screen) - return; - - uint32_t event_mask = - // Just in case that some program reports SelectionNotify events - // with XCB_EVENT_MASK_PROPERTY_CHANGE mask. - XCB_EVENT_MASK_PROPERTY_CHANGE | - // To receive DestroyNotify event and stop the message loop. - XCB_EVENT_MASK_STRUCTURE_NOTIFY; - - m_window = xcb_generate_id(m_connection); - xcb_create_window(m_connection, 0, - m_window, - screen->root, - 0, 0, 1, 1, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - screen->root_visual, - XCB_CW_EVENT_MASK, - &event_mask); - - m_thread = std::thread( - [this]{ - process_x11_events(); - }); - } - - ~Manager() { -#ifdef CLIP_SUPPORT_SAVE_TARGETS - if (!m_data.empty() && - m_window && - m_window == get_x11_selection_owner()) { - // If the CLIPBOARD_MANAGER atom is not 0, we assume that there - // is a clipboard manager available were we can leave our data. - xcb_atom_t x11_clipboard_manager = get_atom(CLIPBOARD_MANAGER); - if (x11_clipboard_manager) { - // We have to lock the m_lock mutex that will be used to wait - // the m_cv condition in get_data_from_selection_owner(). - if (try_lock()) { - // Start the SAVE_TARGETS mechanism so the X11 - // CLIPBOARD_MANAGER will save our clipboard data - // from now on. - get_data_from_selection_owner( - { get_atom(SAVE_TARGETS) }, - []() -> bool { return true; }, - x11_clipboard_manager); - } - } - } -#endif - - if (m_window) { - xcb_destroy_window(m_connection, m_window); - xcb_flush(m_connection); - } - - if (m_thread.joinable()) - m_thread.join(); - - if (m_connection) - xcb_disconnect(m_connection); - } - - bool try_lock() { - bool res = m_lock.try_lock(); - if (!res) { - // TODO make this configurable (the same for Windows retries) - for (int i=0; i<5 && !res; ++i) { - res = m_lock.try_lock(); - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - } - } - return res; - } - - void unlock() { - m_lock.unlock(); - } - - // Clear our data - void clear_data() { - m_data.clear(); -#if CLIP_ENABLE_IMAGE - m_image.reset(); -#endif - } - - void clear() { - clear_data(); - - // As we want to clear the clipboard content, we set us as the new - // clipboard owner (with an empty clipboard). If this fails, we'll - // try to send a XCB_SELECTION_CLEAR request to the real owner - // (but that can fail anyway because it's a request that the owner - // could ignore). - if (set_x11_selection_owner()) - return; - - // Clear the clipboard data from the selection owner - const xcb_window_t owner = get_x11_selection_owner(); - if (m_window != owner) { - xcb_selection_clear_event_t event; - event.response_type = XCB_SELECTION_CLEAR; - event.pad0 = 0; - event.sequence = 0; - event.time = XCB_CURRENT_TIME; - event.owner = owner; - event.selection = get_atom(CLIPBOARD); - - xcb_send_event(m_connection, false, - owner, - XCB_EVENT_MASK_NO_EVENT, - (const char*)&event); - - xcb_flush(m_connection); - } - } - - bool is_convertible(format f) const { - const atoms atoms = get_format_atoms(f); - const xcb_window_t owner = get_x11_selection_owner(); - - // If we are the owner, we just can check the m_data map - if (owner == m_window) { - for (xcb_atom_t atom : atoms) { - auto it = m_data.find(atom); - if (it != m_data.end()) - return true; - } - } - // Ask to the selection owner the available formats/atoms/targets. - else if (owner) { - return - get_data_from_selection_owner( - { get_atom(TARGETS) }, - [this, &atoms]() -> bool { - assert(m_reply_data); - if (!m_reply_data) - return false; - - const xcb_atom_t* sel_atoms = (const xcb_atom_t*)&(*m_reply_data)[0]; - int sel_natoms = m_reply_data->size() / sizeof(xcb_atom_t); - auto atoms_begin = atoms.begin(); - auto atoms_end = atoms.end(); - for (int i=0; i>(len); - std::copy(buf, - buf+len, - shared_data_buf->begin()); - for (xcb_atom_t atom : atoms) - m_data[atom] = shared_data_buf; - - return true; - } - - bool get_data(format f, char* buf, size_t len) const { - const atoms atoms = get_format_atoms(f); - const xcb_window_t owner = get_x11_selection_owner(); - if (owner == m_window) { - for (xcb_atom_t atom : atoms) { - auto it = m_data.find(atom); - if (it != m_data.end()) { - size_t n = std::min(len, it->second->size()); - std::copy(it->second->begin(), - it->second->begin()+n, - buf); - - if (f == text_format()) { - // Add an extra null char - if (n < len) - buf[n] = 0; - } - - return true; - } - } - } - else if (owner) { - if (get_data_from_selection_owner( - atoms, - [this, buf, len, f]() -> bool { - size_t n = std::min(len, m_reply_data->size()); - std::copy(m_reply_data->begin(), - m_reply_data->begin()+n, - buf); - - if (f == text_format()) { - if (n < len) - buf[n] = 0; // Include a null character - } - - return true; - })) { - return true; - } - } - return false; - } - - size_t get_data_length(format f) const { - size_t len = 0; - const atoms atoms = get_format_atoms(f); - const xcb_window_t owner = get_x11_selection_owner(); - if (owner == m_window) { - for (xcb_atom_t atom : atoms) { - auto it = m_data.find(atom); - if (it != m_data.end()) { - len = it->second->size(); - break; - } - } - } - else if (owner) { - if (!get_data_from_selection_owner( - atoms, - [this, &len]() -> bool { - len = m_reply_data->size(); - return true; - })) { - // Error getting data length - return 0; - } - } - if (f == text_format() && len > 0) { - ++len; // Add an extra byte for the null char - } - return len; - } - -#if CLIP_ENABLE_IMAGE - - bool set_image(const image& image) { - if (!set_x11_selection_owner()) - return false; - - m_image = image; - -#ifdef HAVE_PNG_H - // Put a nullptr in the m_data for image/png format and then we'll - // encode the png data when the image is requested in this format. - m_data[get_atom(MIME_IMAGE_PNG)] = buffer_ptr(); -#endif - - return true; - } - - bool get_image(image& output_img) const { - const xcb_window_t owner = get_x11_selection_owner(); - if (owner == m_window) { - if (m_image.is_valid()) { - output_img = m_image; - return true; - } - } -#ifdef HAVE_PNG_H - else if (owner && - get_data_from_selection_owner( - { get_atom(MIME_IMAGE_PNG) }, - [this, &output_img]() -> bool { - return x11::read_png(&(*m_reply_data)[0], - m_reply_data->size(), - &output_img, nullptr); - })) { - return true; - } -#endif - return false; - } - - bool get_image_spec(image_spec& spec) const { - const xcb_window_t owner = get_x11_selection_owner(); - if (owner == m_window) { - if (m_image.is_valid()) { - spec = m_image.spec(); - return true; - } - } -#ifdef HAVE_PNG_H - else if (owner && - get_data_from_selection_owner( - { get_atom(MIME_IMAGE_PNG) }, - [this, &spec]() -> bool { - return x11::read_png(&(*m_reply_data)[0], - m_reply_data->size(), - nullptr, &spec); - })) { - return true; - } -#endif - return false; - } - -#endif // CLIP_ENABLE_IMAGE - - format register_format(const std::string& name) { - xcb_atom_t atom = get_atom(name.c_str()); - m_custom_formats.push_back(atom); - return (format)(m_custom_formats.size()-1) + kBaseForCustomFormats; - } - -private: - - void process_x11_events() { - bool stop = false; - xcb_generic_event_t* event; - while (!stop && (event = xcb_wait_for_event(m_connection))) { - int type = (event->response_type & ~0x80); - - switch (type) { - - case XCB_DESTROY_NOTIFY: - // To stop the message loop we can just destroy the window - stop = true; - break; - - // Someone else has new content in the clipboard, so is - // notifying us that we should delete our data now. - case XCB_SELECTION_CLEAR: - handle_selection_clear_event( - (xcb_selection_clear_event_t*)event); - break; - - // Someone is requesting the clipboard content from us. - case XCB_SELECTION_REQUEST: - handle_selection_request_event( - (xcb_selection_request_event_t*)event); - break; - - // We've requested the clipboard content and this is the - // answer. - case XCB_SELECTION_NOTIFY: - handle_selection_notify_event( - (xcb_selection_notify_event_t*)event); - break; - - case XCB_PROPERTY_NOTIFY: - handle_property_notify_event( - (xcb_property_notify_event_t*)event); - break; - - } - - free(event); - } - } - - void handle_selection_clear_event(xcb_selection_clear_event_t* event) { - if (event->selection == get_atom(CLIPBOARD)) { - std::lock_guard lock(m_mutex); - clear_data(); // Clear our clipboard data - } - } - - void handle_selection_request_event(xcb_selection_request_event_t* event) { - std::lock_guard lock(m_mutex); - - if (event->target == get_atom(TARGETS)) { - atoms targets; - targets.push_back(get_atom(TARGETS)); -#ifdef CLIP_SUPPORT_SAVE_TARGETS - targets.push_back(get_atom(SAVE_TARGETS)); - targets.push_back(get_atom(MULTIPLE)); -#endif - for (const auto& it : m_data) - targets.push_back(it.first); - - // Set the "property" of "requestor" with the clipboard - // formats ("targets", atoms) that we provide. - xcb_change_property( - m_connection, - XCB_PROP_MODE_REPLACE, - event->requestor, - event->property, - get_atom(ATOM), - 8*sizeof(xcb_atom_t), - targets.size(), - &targets[0]); - } -#ifdef CLIP_SUPPORT_SAVE_TARGETS - else if (event->target == get_atom(SAVE_TARGETS)) { - // Do nothing - } - else if (event->target == get_atom(MULTIPLE)) { - xcb_get_property_reply_t* reply = - get_and_delete_property(event->requestor, - event->property, - get_atom(ATOM_PAIR), - false); - if (reply) { - for (xcb_atom_t - *ptr=(xcb_atom_t*)xcb_get_property_value(reply), - *end=ptr + (xcb_get_property_value_length(reply)/sizeof(xcb_atom_t)); - ptrrequestor, - property, - target)) { - xcb_change_property( - m_connection, - XCB_PROP_MODE_REPLACE, - event->requestor, - event->property, - XCB_ATOM_NONE, 0, 0, nullptr); - } - } - - free(reply); - } - } -#endif // CLIP_SUPPORT_SAVE_TARGETS - else { - if (!set_requestor_property_with_clipboard_content( - event->requestor, - event->property, - event->target)) { - // If the requested "target" type is not present in our - // clipboard, we continue normally sending a SelectionNotify - // to the "requestor" anyway because some text editors - // (e.g. Emacs) request the TIMESTAMP target (without asking - // if it's present in TARGETS) after asking for UTF8_STRING. - // - // Sending the SelectionNotify will wake up the "requestor" - // that is asking for the clipboard content. In this way we - // avoid a "Timed out waiting for reply from selection owner" - // error in Emacs (and probably other text editors). - } - } - - // Notify the "requestor" that we've already updated the property. - xcb_selection_notify_event_t notify; - notify.response_type = XCB_SELECTION_NOTIFY; - notify.pad0 = 0; - notify.sequence = 0; - notify.time = event->time; - notify.requestor = event->requestor; - notify.selection = event->selection; - notify.target = event->target; - notify.property = event->property; - - xcb_send_event(m_connection, false, - event->requestor, - XCB_EVENT_MASK_NO_EVENT, // SelectionNotify events go without mask - (const char*)¬ify); - - xcb_flush(m_connection); - } - - bool set_requestor_property_with_clipboard_content(const xcb_atom_t requestor, - const xcb_atom_t property, - const xcb_atom_t target) { - auto it = m_data.find(target); - if (it == m_data.end()) { - // Nothing to do (unsupported target) - return false; - } - - // This can be null of the data was set from an image but we - // didn't encode the image yet (e.g. to image/png format). - if (!it->second) { - encode_data_on_demand(*it); - - // Return nothing, the given "target" cannot be constructed - // (maybe by some encoding error). - if (!it->second) - return false; - } - - // Set the "property" of "requestor" with the - // clipboard content in the requested format ("target"). - xcb_change_property( - m_connection, - XCB_PROP_MODE_REPLACE, - requestor, - property, - target, - 8, - it->second->size(), - &(*it->second)[0]); - return true; - } - - void handle_selection_notify_event(xcb_selection_notify_event_t* event) { - assert(event->requestor == m_window); - - if (event->target == get_atom(TARGETS)) - m_target_atom = get_atom(ATOM); - else - m_target_atom = event->target; - - xcb_get_property_reply_t* reply = - get_and_delete_property(event->requestor, - event->property, - m_target_atom); - if (reply) { - // In this case, We're going to receive the clipboard content in - // chunks of data with several PropertyNotify events. - if (reply->type == get_atom(INCR)) { - free(reply); - - reply = get_and_delete_property(event->requestor, - event->property, - get_atom(INCR)); - if (reply) { - if (xcb_get_property_value_length(reply) == 4) { - uint32_t n = *(uint32_t*)xcb_get_property_value(reply); - m_reply_data = std::make_shared>(n); - m_reply_offset = 0; - m_incr_process = true; - m_incr_received = true; - } - free(reply); - } - } - else { - // Simple case, the whole clipboard content in just one reply - // (without the INCR method). - m_reply_data.reset(); - m_reply_offset = 0; - copy_reply_data(reply); - - call_callback(reply); - - free(reply); - } - } - } - - void handle_property_notify_event(xcb_property_notify_event_t* event) { - if (m_incr_process && - event->state == XCB_PROPERTY_NEW_VALUE && - event->atom == get_atom(CLIPBOARD)) { - xcb_get_property_reply_t* reply = - get_and_delete_property(event->window, - event->atom, - m_target_atom); - if (reply) { - m_incr_received = true; - - // When the length is 0 it means that the content was - // completely sent by the selection owner. - if (xcb_get_property_value_length(reply) > 0) { - copy_reply_data(reply); - } - else { - // Now that m_reply_data has the complete clipboard content, - // we can call the m_callback. - call_callback(reply); - m_incr_process = false; - } - free(reply); - } - } - } - - xcb_get_property_reply_t* get_and_delete_property(xcb_window_t window, - xcb_atom_t property, - xcb_atom_t atom, - bool delete_prop = true) { - xcb_get_property_cookie_t cookie = - xcb_get_property(m_connection, - delete_prop, - window, - property, - atom, - 0, 0x1fffffff); // 0x1fffffff = INT32_MAX / 4 - - xcb_generic_error_t* err = nullptr; - xcb_get_property_reply_t* reply = - xcb_get_property_reply(m_connection, cookie, &err); - if (err) { - // TODO report error - free(err); - } - return reply; - } - - // Concatenates the new data received in "reply" into "m_reply_data" - // buffer. - void copy_reply_data(xcb_get_property_reply_t* reply) { - const uint8_t* src = (const uint8_t*)xcb_get_property_value(reply); - // n = length of "src" in bytes - size_t n = xcb_get_property_value_length(reply); - - size_t req = m_reply_offset+n; - if (!m_reply_data) { - m_reply_data = std::make_shared>(req); - } - // The "m_reply_data" size can be smaller because the size - // specified in INCR property is just a lower bound. - else if (req > m_reply_data->size()) { - m_reply_data->resize(req); - } - - std::copy(src, src+n, m_reply_data->begin()+m_reply_offset); - m_reply_offset += n; - } - - // Calls the current m_callback() to handle the clipboard content - // received from the owner. - void call_callback(xcb_get_property_reply_t* reply) { - m_callback_result = false; - if (m_callback) - m_callback_result = m_callback(); - - m_cv.notify_one(); - - m_reply_data.reset(); - } - - bool get_data_from_selection_owner(const atoms& atoms, - const notify_callback&& callback, - xcb_atom_t selection = 0) const { - if (!selection) - selection = get_atom(CLIPBOARD); - - // Put the callback on "m_callback" so we can call it on - // SelectionNotify event. - m_callback = std::move(callback); - - // Clear data if we are not the selection owner. - if (m_window != get_x11_selection_owner()) - m_data.clear(); - - // Ask to the selection owner for its content on each known - // text format/atom. - for (xcb_atom_t atom : atoms) { - xcb_convert_selection(m_connection, - m_window, // Send us the result - selection, // Clipboard selection - atom, // The clipboard format that we're requesting - get_atom(CLIPBOARD), // Leave result in this window's property - XCB_CURRENT_TIME); - - xcb_flush(m_connection); - - // We use the "m_incr_received" to wait several timeouts in case - // that we've received the INCR SelectionNotify or - // PropertyNotify events. - do { - m_incr_received = false; - - // Wait a response for 100 milliseconds - std::cv_status status = - m_cv.wait_for(m_lock, - std::chrono::milliseconds(get_x11_wait_timeout())); - if (status == std::cv_status::no_timeout) { - // If the condition variable was notified, it means that the - // callback was called correctly. - return m_callback_result; - } - } while (m_incr_received); - } - - // Reset callback - m_callback = notify_callback(); - return false; - } - - atoms get_atoms(const char** names, - const int n) const { - atoms result(n, 0); - std::vector cookies(n); - - for (int i=0; isecond; - else - cookies[i] = xcb_intern_atom( - m_connection, 0, - std::strlen(names[i]), names[i]); - } - - for (int i=0; iatom; - free(reply); - } - } - } - - return result; - } - - xcb_atom_t get_atom(const char* name) const { - auto it = m_atoms.find(name); - if (it != m_atoms.end()) - return it->second; - - xcb_atom_t result = 0; - xcb_intern_atom_cookie_t cookie = - xcb_intern_atom(m_connection, 0, - std::strlen(name), name); - - xcb_intern_atom_reply_t* reply = - xcb_intern_atom_reply(m_connection, - cookie, - nullptr); - if (reply) { - result = m_atoms[name] = reply->atom; - free(reply); - } - return result; - } - - xcb_atom_t get_atom(CommonAtom i) const { - if (m_common_atoms.empty()) { - m_common_atoms = - get_atoms(kCommonAtomNames, - sizeof(kCommonAtomNames) / sizeof(kCommonAtomNames[0])); - } - return m_common_atoms[i]; - } - - const atoms& get_text_format_atoms() const { - if (m_text_atoms.empty()) { - const char* names[] = { - // Prefer utf-8 formats first - "UTF8_STRING", - "text/plain;charset=utf-8", - "text/plain;charset=UTF-8", - "GTK_TEXT_BUFFER_CONTENTS", // Required for gedit (and maybe gtk+ apps) - // ANSI C strings? - "STRING", - "TEXT", - "text/plain", - }; - m_text_atoms = get_atoms(names, sizeof(names) / sizeof(names[0])); - } - return m_text_atoms; - } - -#if CLIP_ENABLE_IMAGE - - const atoms& get_image_format_atoms() const { - if (m_image_atoms.empty()) { -#ifdef HAVE_PNG_H - m_image_atoms.push_back(get_atom(MIME_IMAGE_PNG)); -#endif - } - return m_image_atoms; - } - -#endif // CLIP_ENABLE_IMAGE - - atoms get_format_atoms(const format f) const { - atoms atoms; - if (f == text_format()) { - atoms = get_text_format_atoms(); - } -#if CLIP_ENABLE_IMAGE - else if (f == image_format()) { - atoms = get_image_format_atoms(); - } -#endif // CLIP_ENABLE_IMAGE - else { - xcb_atom_t atom = get_format_atom(f); - if (atom) - atoms.push_back(atom); - } - return atoms; - } - -#if !defined(NDEBUG) - // This can be used to print debugging messages. - std::string get_atom_name(xcb_atom_t atom) const { - std::string result; - xcb_get_atom_name_cookie_t cookie = - xcb_get_atom_name(m_connection, atom); - xcb_generic_error_t* err = nullptr; - xcb_get_atom_name_reply_t* reply = - xcb_get_atom_name_reply(m_connection, cookie, &err); - if (err) { - free(err); - } - if (reply) { - int len = xcb_get_atom_name_name_length(reply); - if (len > 0) { - result.resize(len); - char* name = xcb_get_atom_name_name(reply); - if (name) - std::copy(name, name+len, result.begin()); - } - free(reply); - } - return result; - } -#endif - - bool set_x11_selection_owner() const { - xcb_void_cookie_t cookie = - xcb_set_selection_owner_checked(m_connection, - m_window, - get_atom(CLIPBOARD), - XCB_CURRENT_TIME); - xcb_generic_error_t* err = - xcb_request_check(m_connection, - cookie); - if (err) { - free(err); - return false; - } - return true; - } - - xcb_window_t get_x11_selection_owner() const { - xcb_window_t result = 0; - xcb_get_selection_owner_cookie_t cookie = - xcb_get_selection_owner(m_connection, - get_atom(CLIPBOARD)); - - xcb_get_selection_owner_reply_t* reply = - xcb_get_selection_owner_reply(m_connection, cookie, nullptr); - if (reply) { - result = reply->owner; - free(reply); - } - return result; - } - - xcb_atom_t get_format_atom(const format f) const { - int i = f - kBaseForCustomFormats; - if (i >= 0 && i < int(m_custom_formats.size())) - return m_custom_formats[i]; - else - return 0; - } - - void encode_data_on_demand(std::pair& e) { -#if defined(CLIP_ENABLE_IMAGE) && defined(HAVE_PNG_H) - if (e.first == get_atom(MIME_IMAGE_PNG)) { - assert(m_image.is_valid()); - if (!m_image.is_valid()) - return; - - std::vector output; - if (x11::write_png(m_image, output)) { - e.second = - std::make_shared>( - std::move(output)); - } - // else { TODO report png conversion errors } - } -#endif // defined(CLIP_ENABLE_IMAGE) && defined(HAVE_PNG_H) - } - - // Access to the whole Manager - std::mutex m_mutex; - - // Lock used in the main thread using the Manager (i.e. by lock::impl) - mutable std::unique_lock m_lock; - - // Connection to X11 server - xcb_connection_t* m_connection; - - // Temporal background window used to own the clipboard and process - // all events related about the clipboard in a background thread - xcb_window_t m_window; - - // Used to wait/notify the arrival of the SelectionNotify event when - // we requested the clipboard content from other selection owner. - mutable std::condition_variable m_cv; - - // Thread used to run a background message loop to wait X11 events - // about clipboard. The X11 selection owner will be a hidden window - // created by us just for the clipboard purpose/communication. - std::thread m_thread; - - // Internal callback used when a SelectionNotify is received (or the - // whole data content is received by the INCR method). So this - // callback can use the notification by different purposes (e.g. get - // the data length only, or get/process the data content, etc.). - mutable notify_callback m_callback; - - // Result returned by the m_callback. Used as return value in the - // get_data_from_selection_owner() function. For example, if the - // callback must read a "image/png" file from the clipboard data and - // fails, the callback can return false and finally the get_image() - // will return false (i.e. there is data, but it's not a valid image - // format). - std::atomic m_callback_result; - - // Cache of known atoms - mutable std::map m_atoms; - - // Cache of common used atoms by us - mutable atoms m_common_atoms; - - // Cache of atoms related to text or image content - mutable atoms m_text_atoms; -#if CLIP_ENABLE_IMAGE - mutable atoms m_image_atoms; -#endif - - // Actual clipboard data generated by us (when we "copy" content in - // the clipboard, it means that we own the X11 "CLIPBOARD" - // selection, and in case of SelectionRequest events, we've to - // return the data stored in this "m_data" field) - mutable std::map m_data; - - // Copied image in the clipboard. As we have to transfer the image - // in some specific format (e.g. image/png) we want to keep a copy - // of the image and make the conversion when the clipboard data is - // requested by other process. -#if CLIP_ENABLE_IMAGE - mutable image m_image; -#endif - - // True if we have received an INCR notification so we're going to - // process several PropertyNotify to concatenate all data chunks. - bool m_incr_process; - - // Variable used to wait more time if we've received an INCR - // notification, which means that we're going to receive large - // amounts of data from the selection owner. - mutable bool m_incr_received; - - // Target/selection format used in the SelectionNotify. Used in the - // INCR method to get data from the same property in the same format - // (target) on each PropertyNotify. - xcb_atom_t m_target_atom; - - // Each time we receive data from the selection owner, we put that - // data in this buffer. If we get the data with the INCR method, - // we'll concatenate chunks of data in this buffer to complete the - // whole clipboard content. - buffer_ptr m_reply_data; - - // Used to concatenate chunks of data in "m_reply_data" from several - // PropertyNotify when we are getting the selection owner data with - // the INCR method. - size_t m_reply_offset; - - // List of user-defined formats/atoms. - std::vector m_custom_formats; -}; - -Manager* manager = nullptr; - -void delete_manager_atexit() { - if (manager) { - delete manager; - manager = nullptr; - } -} - -Manager* get_manager() { - if (!manager) { - manager = new Manager; - std::atexit(delete_manager_atexit); - } - return manager; -} - -} // anonymous namespace - -lock::impl::impl(void*) : m_locked(false) { - m_locked = get_manager()->try_lock(); -} - -lock::impl::~impl() { - if (m_locked) - manager->unlock(); -} - -bool lock::impl::clear() { - manager->clear(); - return true; -} - -bool lock::impl::is_convertible(format f) const { - return manager->is_convertible(f); -} - -bool lock::impl::set_data(format f, const char* buf, size_t len) { - return manager->set_data(f, buf, len); -} - -bool lock::impl::get_data(format f, char* buf, size_t len) const { - return manager->get_data(f, buf, len); -} - -size_t lock::impl::get_data_length(format f) const { - return manager->get_data_length(f); -} - -#if CLIP_ENABLE_IMAGE - -bool lock::impl::set_image(const image& image) { - return manager->set_image(image); -} - -bool lock::impl::get_image(image& output_img) const { - return manager->get_image(output_img); -} - -bool lock::impl::get_image_spec(image_spec& spec) const { - return manager->get_image_spec(spec); -} - -#endif // CLIP_ENABLE_IMAGE - -format register_format(const std::string& name) { - return get_manager()->register_format(name); -} - -} // namespace clip diff --git a/src/clip-lib/clip_x11_png.h b/src/clip-lib/clip_x11_png.h deleted file mode 100644 index cf3b514..0000000 --- a/src/clip-lib/clip_x11_png.h +++ /dev/null @@ -1,230 +0,0 @@ -// Clip Library -// Copyright (c) 2018-2021 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "clip.h" - -#include -#include - -#include "png.h" - -namespace clip { -namespace x11 { - -////////////////////////////////////////////////////////////////////// -// Functions to convert clip::image into png data to store it in the -// clipboard. - -void write_data_fn(png_structp png, png_bytep buf, png_size_t len) { - std::vector& output = *(std::vector*)png_get_io_ptr(png); - const size_t i = output.size(); - output.resize(i+len); - std::copy(buf, buf+len, output.begin()+i); -} - -bool write_png(const image& image, - std::vector& output) { - png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, - nullptr, nullptr, nullptr); - if (!png) - return false; - - png_infop info = png_create_info_struct(png); - if (!info) { - png_destroy_write_struct(&png, nullptr); - return false; - } - - if (setjmp(png_jmpbuf(png))) { - png_destroy_write_struct(&png, &info); - return false; - } - - png_set_write_fn(png, - (png_voidp)&output, - write_data_fn, - nullptr); // No need for a flush function - - const image_spec& spec = image.spec(); - int color_type = (spec.alpha_mask ? - PNG_COLOR_TYPE_RGB_ALPHA: - PNG_COLOR_TYPE_RGB); - - png_set_IHDR(png, info, - spec.width, spec.height, 8, color_type, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - png_write_info(png, info); - png_set_packing(png); - - png_bytep row = - (png_bytep)png_malloc(png, png_get_rowbytes(png, info)); - - for (png_uint_32 y=0; y> spec.red_shift; - *(dst++) = (c & spec.green_mask) >> spec.green_shift; - *(dst++) = (c & spec.blue_mask ) >> spec.blue_shift; - if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) - *(dst++) = (c & spec.alpha_mask) >> spec.alpha_shift; - } - - png_write_rows(png, &row, 1); - } - - png_free(png, row); - png_write_end(png, info); - png_destroy_write_struct(&png, &info); - return true; -} - -////////////////////////////////////////////////////////////////////// -// Functions to convert png data stored in the clipboard to a -// clip::image. - -struct read_png_io { - const uint8_t* buf; - size_t len; - size_t pos; -}; - -void read_data_fn(png_structp png, png_bytep buf, png_size_t len) { - read_png_io& io = *(read_png_io*)png_get_io_ptr(png); - if (io.pos < io.len) { - size_t n = std::min(len, io.len-io.pos); - if (n > 0) { - std::copy(io.buf+io.pos, - io.buf+io.pos+n, - buf); - io.pos += n; - } - } -} - -bool read_png(const uint8_t* buf, - const size_t len, - image* output_image, - image_spec* output_spec) { - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, - nullptr, nullptr, nullptr); - if (!png) - return false; - - png_infop info = png_create_info_struct(png); - if (!info) { - png_destroy_read_struct(&png, nullptr, nullptr); - return false; - } - - if (setjmp(png_jmpbuf(png))) { - png_destroy_read_struct(&png, &info, nullptr); - return false; - } - - read_png_io io = { buf, len, 0 }; - png_set_read_fn(png, (png_voidp)&io, read_data_fn); - - png_read_info(png, info); - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type; - png_get_IHDR(png, info, &width, &height, - &bit_depth, &color_type, - &interlace_type, - nullptr, nullptr); - - image_spec spec; - spec.width = width; - spec.height = height; - spec.bits_per_pixel = 32; - - // Don't use png_get_rowbytes(png, info) here because this is the - // bytes_per_row of the output clip::image (the png file could - // contain 24bpp but we want to return a 32bpp anyway with alpha=255 - // in that case). - spec.bytes_per_row = 4*width; - - spec.red_mask = 0x000000ff; - spec.green_mask = 0x0000ff00; - spec.blue_mask = 0x00ff0000; - spec.red_shift = 0; - spec.green_shift = 8; - spec.blue_shift = 16; - - if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) { - spec.alpha_mask = 0xff000000; - spec.alpha_shift = 24; - } - else { - spec.alpha_mask = 0; - spec.alpha_shift = 0; - } - - if (output_spec) - *output_spec = spec; - - if (output_image && - width > 0 && - height > 0) { - image img(spec); - - // We want RGB 24-bit or RGBA 32-bit as a result - png_set_strip_16(png); // Down to 8-bit (TODO we might support 16-bit values) - png_set_packing(png); // Use one byte if color depth < 8-bit - png_set_expand_gray_1_2_4_to_8(png); - png_set_palette_to_rgb(png); - png_set_gray_to_rgb(png); - png_set_tRNS_to_alpha(png); - - int number_passes = png_set_interlace_handling(png); - png_read_update_info(png, info); - - const int src_bytes_per_row = png_get_rowbytes(png, info); - png_bytepp rows = (png_bytepp)png_malloc(png, sizeof(png_bytep)*height); - png_uint_32 y; - for (y=0; y 0) - n += 4 - (n % 4); - else - ++n; - } - - return n; -} - -image::image() - : m_own_data(false), - m_data(nullptr) -{ -} - -image::image(const image_spec& spec) - : m_own_data(true), - m_data(new char[spec.required_data_size()]), - m_spec(spec) { -} - -image::image(const void* data, const image_spec& spec) - : m_own_data(false), - m_data((char*)data), - m_spec(spec) { -} - -image::image(const image& image) - : m_own_data(false), - m_data(nullptr), - m_spec(image.m_spec) { - copy_image(image); -} - -image::image(image&& image) - : m_own_data(false), - m_data(nullptr) { - move_image(std::move(image)); -} - -image::~image() { - reset(); -} - -image& image::operator=(const image& image) { - copy_image(image); - return *this; -} - -image& image::operator=(image&& image) { - move_image(std::move(image)); - return *this; -} - -void image::reset() { - if (m_own_data) { - delete[] m_data; - m_own_data = false; - m_data = nullptr; - } -} - -void image::copy_image(const image& image) { - reset(); - - m_spec = image.spec(); - std::size_t n = m_spec.required_data_size(); - - m_own_data = true; - m_data = new char[n]; - std::copy(image.data(), - image.data()+n, - m_data); -} - -void image::move_image(image&& image) { - std::swap(m_own_data, image.m_own_data); - std::swap(m_data, image.m_data); - std::swap(m_spec, image.m_spec); -} - -} // namespace clip diff --git a/src/main.cpp b/src/main.cpp index 49098a1..1e5fa36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "clip-lib/clip.h" #include using namespace std; @@ -98,7 +97,6 @@ int help() { cout << " -h Show this help message" << endl; cout << " -f Read from file" << endl; cout << " -o Write to file" << endl; - cout << " -c Copy to clipboard" << endl; cout << " -ne Stops extra stuff from being added to the end of sentences" << endl; cout << " -nf Stops full word replacements" << endl; cout << " text Text to convert" << endl; @@ -129,8 +127,6 @@ int main(int argc, char *argv[]) { return help(); } else if (i == argc-1) { input = string(argv[i]); - } else if (string(argv[i]) == "-c") { - clipboard = true; } else if (string(argv[i]) == "-ne") { no_random_ending = true; } else if (string(argv[i]) == "-nf") { @@ -189,9 +185,6 @@ int main(int argc, char *argv[]) { } else if (input != "") { if (outputfile != "") { *output << owo(input, no_full_word, no_random_ending) << endl; - } else if (clipboard) { - clip::set_text(owo(input, no_full_word, no_random_ending)); - cout << owo("Copied to clipboard") << endl; } else { cout << owo(input, no_full_word, no_random_ending) << endl; } @@ -201,6 +194,5 @@ int main(int argc, char *argv[]) { ((ofstream*)output)->close(); } - return 0; } \ No newline at end of file