chore: CRLF to LF
This commit is contained in:
+143
-143
@@ -1,143 +1,143 @@
|
||||
#include "clipboard.h"
|
||||
|
||||
// GDI+ Headers
|
||||
// WARNING: Must stay in this order to compile
|
||||
#include <windows.h>
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
#ifdef __GNUC__
|
||||
#include <gdiplus/gdiplusflat.h>
|
||||
#else
|
||||
#include <gdiplusflat.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace clipboard {
|
||||
namespace imports {
|
||||
static bool ATTEMPTED_LOAD_LIBRARY = false;
|
||||
|
||||
static decltype(Gdiplus::GdiplusShutdown) *GdiplusShutdown = nullptr;
|
||||
static decltype(Gdiplus::GdiplusStartup) *GdiplusStartup = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateBitmapFromFile) *GdipCreateBitmapFromFile = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateHBITMAPFromBitmap) *GdipCreateHBITMAPFromBitmap = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipDisposeImage) *GdipDisposeImage = nullptr;
|
||||
}
|
||||
|
||||
void copy_image_handler(const std::filesystem::path &path) {
|
||||
if (!imports::ATTEMPTED_LOAD_LIBRARY) {
|
||||
imports::ATTEMPTED_LOAD_LIBRARY = true;
|
||||
|
||||
auto gdiplus = libutils::try_library("gdiplus.dll");
|
||||
|
||||
if (gdiplus) {
|
||||
imports::GdiplusShutdown = (decltype(imports::GdiplusShutdown)) libutils::try_proc(
|
||||
gdiplus, "GdiplusShutdown");
|
||||
imports::GdiplusStartup = (decltype(imports::GdiplusStartup)) libutils::try_proc(
|
||||
gdiplus, "GdiplusStartup");
|
||||
imports::GdipCreateBitmapFromFile = (decltype(imports::GdipCreateBitmapFromFile)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateBitmapFromFile");
|
||||
imports::GdipCreateHBITMAPFromBitmap = (decltype(imports::GdipCreateHBITMAPFromBitmap)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateHBITMAPFromBitmap");
|
||||
imports::GdipDisposeImage = (decltype(imports::GdipDisposeImage)) libutils::try_proc(
|
||||
gdiplus, "GdipDisposeImage");
|
||||
} else {
|
||||
log_warning("clipboard", "GDI+ library not found, disabling clipboard functionality");
|
||||
}
|
||||
}
|
||||
if (!imports::GdiplusShutdown ||
|
||||
!imports::GdiplusStartup ||
|
||||
!imports::GdipCreateBitmapFromFile ||
|
||||
!imports::GdipCreateHBITMAPFromBitmap ||
|
||||
!imports::GdipDisposeImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the screenshot button is print screen, the OpenClipboard call seems to fail often if we only
|
||||
// call it once, probably due to a race condition. So, we can try calling a lot until we can open it.
|
||||
bool clipboard_open = false;
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
clipboard_open = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipboard_open) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start gdiplus
|
||||
Gdiplus::GdiplusStartupInput input {};
|
||||
ULONG_PTR token;
|
||||
imports::GdiplusStartup(&token, &input, nullptr);
|
||||
|
||||
// Convert the file path to a wstring and open the screenshot file
|
||||
Gdiplus::GpBitmap *bitmap = nullptr;
|
||||
auto status = imports::GdipCreateBitmapFromFile(path.c_str(), &bitmap);
|
||||
if (status != Gdiplus::Ok) {
|
||||
log_warning("clipboard", "failed to create GDI+ bitmap: {}", status);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
CloseClipboard();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the HBITMAP from the Bitmap object
|
||||
HBITMAP hbitmap {};
|
||||
status = imports::GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
|
||||
if (status == Gdiplus::Ok) {
|
||||
|
||||
// Convert the HBITMAP to a DIB to copy to the clipboard
|
||||
BITMAP bm;
|
||||
GetObject(hbitmap, sizeof(bm), &bm);
|
||||
|
||||
BITMAPINFOHEADER info;
|
||||
info.biSize = sizeof(info);
|
||||
info.biWidth = bm.bmWidth;
|
||||
info.biHeight = bm.bmHeight;
|
||||
info.biPlanes = 1;
|
||||
info.biBitCount = bm.bmBitsPixel;
|
||||
info.biCompression = BI_RGB;
|
||||
|
||||
std::vector<BYTE> dimensions(bm.bmWidthBytes * bm.bmHeight);
|
||||
auto hdc = GetDC(nullptr);
|
||||
GetDIBits(hdc, hbitmap, 0, info.biHeight, dimensions.data(), reinterpret_cast<BITMAPINFO *>(&info), 0);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
|
||||
auto hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof(info) + dimensions.size());
|
||||
auto buffer = reinterpret_cast<BYTE *>(GlobalLock(hmem));
|
||||
memcpy(buffer, &info, sizeof(info));
|
||||
memcpy(&buffer[sizeof(info)], dimensions.data(), dimensions.size());
|
||||
GlobalUnlock(hmem);
|
||||
|
||||
if (SetClipboardData(CF_DIB, hmem)) {
|
||||
log_info("clipboard", "saved image to clipboard");
|
||||
} else {
|
||||
log_warning("clipboard", "failed to save image to clipboard");
|
||||
}
|
||||
} else {
|
||||
log_warning("clipboard", "failed to retrieve HBITMAP from image bitmap: {}", status);
|
||||
}
|
||||
|
||||
// Clean up after ourselves. hmem can't be deleted because it is owned by the clipboard now.
|
||||
imports::GdipDisposeImage(bitmap);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void copy_image(const std::filesystem::path path) {
|
||||
|
||||
// Create a new thread since we loop to open the clipboard
|
||||
std::thread handle(copy_image_handler, std::move(path));
|
||||
|
||||
handle.detach();
|
||||
}
|
||||
}
|
||||
#include "clipboard.h"
|
||||
|
||||
// GDI+ Headers
|
||||
// WARNING: Must stay in this order to compile
|
||||
#include <windows.h>
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
#ifdef __GNUC__
|
||||
#include <gdiplus/gdiplusflat.h>
|
||||
#else
|
||||
#include <gdiplusflat.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace clipboard {
|
||||
namespace imports {
|
||||
static bool ATTEMPTED_LOAD_LIBRARY = false;
|
||||
|
||||
static decltype(Gdiplus::GdiplusShutdown) *GdiplusShutdown = nullptr;
|
||||
static decltype(Gdiplus::GdiplusStartup) *GdiplusStartup = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateBitmapFromFile) *GdipCreateBitmapFromFile = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateHBITMAPFromBitmap) *GdipCreateHBITMAPFromBitmap = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipDisposeImage) *GdipDisposeImage = nullptr;
|
||||
}
|
||||
|
||||
void copy_image_handler(const std::filesystem::path &path) {
|
||||
if (!imports::ATTEMPTED_LOAD_LIBRARY) {
|
||||
imports::ATTEMPTED_LOAD_LIBRARY = true;
|
||||
|
||||
auto gdiplus = libutils::try_library("gdiplus.dll");
|
||||
|
||||
if (gdiplus) {
|
||||
imports::GdiplusShutdown = (decltype(imports::GdiplusShutdown)) libutils::try_proc(
|
||||
gdiplus, "GdiplusShutdown");
|
||||
imports::GdiplusStartup = (decltype(imports::GdiplusStartup)) libutils::try_proc(
|
||||
gdiplus, "GdiplusStartup");
|
||||
imports::GdipCreateBitmapFromFile = (decltype(imports::GdipCreateBitmapFromFile)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateBitmapFromFile");
|
||||
imports::GdipCreateHBITMAPFromBitmap = (decltype(imports::GdipCreateHBITMAPFromBitmap)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateHBITMAPFromBitmap");
|
||||
imports::GdipDisposeImage = (decltype(imports::GdipDisposeImage)) libutils::try_proc(
|
||||
gdiplus, "GdipDisposeImage");
|
||||
} else {
|
||||
log_warning("clipboard", "GDI+ library not found, disabling clipboard functionality");
|
||||
}
|
||||
}
|
||||
if (!imports::GdiplusShutdown ||
|
||||
!imports::GdiplusStartup ||
|
||||
!imports::GdipCreateBitmapFromFile ||
|
||||
!imports::GdipCreateHBITMAPFromBitmap ||
|
||||
!imports::GdipDisposeImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the screenshot button is print screen, the OpenClipboard call seems to fail often if we only
|
||||
// call it once, probably due to a race condition. So, we can try calling a lot until we can open it.
|
||||
bool clipboard_open = false;
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
clipboard_open = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipboard_open) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start gdiplus
|
||||
Gdiplus::GdiplusStartupInput input {};
|
||||
ULONG_PTR token;
|
||||
imports::GdiplusStartup(&token, &input, nullptr);
|
||||
|
||||
// Convert the file path to a wstring and open the screenshot file
|
||||
Gdiplus::GpBitmap *bitmap = nullptr;
|
||||
auto status = imports::GdipCreateBitmapFromFile(path.c_str(), &bitmap);
|
||||
if (status != Gdiplus::Ok) {
|
||||
log_warning("clipboard", "failed to create GDI+ bitmap: {}", status);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
CloseClipboard();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the HBITMAP from the Bitmap object
|
||||
HBITMAP hbitmap {};
|
||||
status = imports::GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
|
||||
if (status == Gdiplus::Ok) {
|
||||
|
||||
// Convert the HBITMAP to a DIB to copy to the clipboard
|
||||
BITMAP bm;
|
||||
GetObject(hbitmap, sizeof(bm), &bm);
|
||||
|
||||
BITMAPINFOHEADER info;
|
||||
info.biSize = sizeof(info);
|
||||
info.biWidth = bm.bmWidth;
|
||||
info.biHeight = bm.bmHeight;
|
||||
info.biPlanes = 1;
|
||||
info.biBitCount = bm.bmBitsPixel;
|
||||
info.biCompression = BI_RGB;
|
||||
|
||||
std::vector<BYTE> dimensions(bm.bmWidthBytes * bm.bmHeight);
|
||||
auto hdc = GetDC(nullptr);
|
||||
GetDIBits(hdc, hbitmap, 0, info.biHeight, dimensions.data(), reinterpret_cast<BITMAPINFO *>(&info), 0);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
|
||||
auto hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof(info) + dimensions.size());
|
||||
auto buffer = reinterpret_cast<BYTE *>(GlobalLock(hmem));
|
||||
memcpy(buffer, &info, sizeof(info));
|
||||
memcpy(&buffer[sizeof(info)], dimensions.data(), dimensions.size());
|
||||
GlobalUnlock(hmem);
|
||||
|
||||
if (SetClipboardData(CF_DIB, hmem)) {
|
||||
log_info("clipboard", "saved image to clipboard");
|
||||
} else {
|
||||
log_warning("clipboard", "failed to save image to clipboard");
|
||||
}
|
||||
} else {
|
||||
log_warning("clipboard", "failed to retrieve HBITMAP from image bitmap: {}", status);
|
||||
}
|
||||
|
||||
// Clean up after ourselves. hmem can't be deleted because it is owned by the clipboard now.
|
||||
imports::GdipDisposeImage(bitmap);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void copy_image(const std::filesystem::path path) {
|
||||
|
||||
// Create a new thread since we loop to open the clipboard
|
||||
std::thread handle(copy_image_handler, std::move(path));
|
||||
|
||||
handle.detach();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user