mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 03:14:45 +00:00
Merge branch 'QB64-Phoenix-Edition:main' into logical-operators
This commit is contained in:
commit
19df445cf3
31 changed files with 50099 additions and 46787 deletions
2
.github/workflows/build-process.yml
vendored
2
.github/workflows/build-process.yml
vendored
|
@ -46,7 +46,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: ${{ matrix.prefix == 'lnx' }}
|
if: ${{ matrix.prefix == 'lnx' }}
|
||||||
run: sudo apt update && sudo apt install build-essential x11-utils mesa-common-dev libglu1-mesa-dev libasound2-dev zlib1g-dev pulseaudio dbus-x11 libportaudio2 libcurl4-openssl-dev
|
run: sudo apt update && sudo apt install build-essential x11-utils mesa-common-dev libglu1-mesa-dev libasound2-dev libpng-dev pulseaudio dbus-x11 libportaudio2 libcurl4-openssl-dev
|
||||||
|
|
||||||
# Pulseaudio puts a dummy ALSA device in place, which allows us to do
|
# Pulseaudio puts a dummy ALSA device in place, which allows us to do
|
||||||
# audio testing on Linux
|
# audio testing on Linux
|
||||||
|
|
15
Makefile
15
Makefile
|
@ -145,17 +145,17 @@ CXXFLAGS += -fno-strict-aliasing
|
||||||
CXXFLAGS += -Wno-conversion-null
|
CXXFLAGS += -Wno-conversion-null
|
||||||
|
|
||||||
ifeq ($(OS),lnx)
|
ifeq ($(OS),lnx)
|
||||||
CXXLIBS += -lGL -lGLU -lX11 -lpthread -ldl -lrt
|
CXXLIBS += -lGL -lGLU -lX11 -lpthread -ldl -lrt -lxcb
|
||||||
CXXFLAGS += -DFREEGLUT_STATIC
|
CXXFLAGS += -DFREEGLUT_STATIC
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS),win)
|
ifeq ($(OS),win)
|
||||||
CXXLIBS += -static-libgcc -static-libstdc++ -lcomdlg32 -lole32
|
CXXLIBS += -static-libgcc -static-libstdc++ -lcomdlg32 -lole32 -lshlwapi -lwindowscodecs
|
||||||
CXXFLAGS += -DGLEW_STATIC -DFREEGLUT_STATIC
|
CXXFLAGS += -DGLEW_STATIC -DFREEGLUT_STATIC
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS),osx)
|
ifeq ($(OS),osx)
|
||||||
CXXLIBS += -framework OpenGL -framework IOKit -framework GLUT -framework Cocoa -framework ApplicationServices
|
CXXLIBS += -framework OpenGL -framework IOKit -framework GLUT -framework Cocoa -framework ApplicationServices -framework CoreFoundation
|
||||||
|
|
||||||
# OSX doesn't strip using objcopy, so we're using `-s` instead
|
# OSX doesn't strip using objcopy, so we're using `-s` instead
|
||||||
ifneq ($(STRIP_SYMBOLS),n)
|
ifneq ($(STRIP_SYMBOLS),n)
|
||||||
|
@ -204,6 +204,7 @@ include $(PATH_INTERNAL_C)/parts/video/image/build.mk
|
||||||
include $(PATH_INTERNAL_C)/parts/gui/build.mk
|
include $(PATH_INTERNAL_C)/parts/gui/build.mk
|
||||||
include $(PATH_INTERNAL_C)/parts/network/http/build.mk
|
include $(PATH_INTERNAL_C)/parts/network/http/build.mk
|
||||||
include $(PATH_INTERNAL_C)/parts/compression/build.mk
|
include $(PATH_INTERNAL_C)/parts/compression/build.mk
|
||||||
|
include $(PATH_INTERNAL_C)/parts/os/clipboard/build.mk
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
|
@ -267,6 +268,8 @@ endif
|
||||||
ifneq ($(filter y,$(DEP_SCREENIMAGE)),)
|
ifneq ($(filter y,$(DEP_SCREENIMAGE)),)
|
||||||
CXXFLAGS += -DDEPENDENCY_SCREENIMAGE
|
CXXFLAGS += -DDEPENDENCY_SCREENIMAGE
|
||||||
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
||||||
|
|
||||||
|
LICENSE_IN_USE += clip
|
||||||
else
|
else
|
||||||
CXXFLAGS += -DDEPENDENCY_NO_SCREENIMAGE
|
CXXFLAGS += -DDEPENDENCY_NO_SCREENIMAGE
|
||||||
QBLIB_NAME := $(addsuffix 0,$(QBLIB_NAME))
|
QBLIB_NAME := $(addsuffix 0,$(QBLIB_NAME))
|
||||||
|
@ -287,9 +290,7 @@ ifneq ($(filter y,$(DEP_DEVICEINPUT)),)
|
||||||
ifeq ($(OS),win)
|
ifeq ($(OS),win)
|
||||||
CXXLIBS += -lwinmm -lxinput -ldinput8 -ldxguid -lwbemuuid -lole32 -loleaut32
|
CXXLIBS += -lwinmm -lxinput -ldinput8 -ldxguid -lwbemuuid -lole32 -loleaut32
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS),osx)
|
|
||||||
CXXLIBS += -framework CoreFoundation -framework IOKit
|
|
||||||
endif
|
|
||||||
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
||||||
|
|
||||||
LICENSE_IN_USE += libstem_gamepad
|
LICENSE_IN_USE += libstem_gamepad
|
||||||
|
@ -308,7 +309,7 @@ ifneq ($(filter y,$(DEP_AUDIO_MINIAUDIO)),)
|
||||||
CXXLIBS += -lwinmm -lksguid -ldxguid -lole32
|
CXXLIBS += -lwinmm -lksguid -ldxguid -lole32
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS),osx)
|
ifeq ($(OS),osx)
|
||||||
CXXLIBS += -lpthread -lm -framework CoreFoundation -framework CoreAudio -framework CoreMIDI -framework AudioUnit -framework AudioToolbox
|
CXXLIBS += -lpthread -lm -framework CoreAudio -framework CoreMIDI -framework AudioUnit -framework AudioToolbox
|
||||||
endif
|
endif
|
||||||
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
|
||||||
|
|
||||||
|
|
|
@ -188,15 +188,6 @@ extern "C" int QB64_Resizable() { return ScreenResize; }
|
||||||
|
|
||||||
int32 sub_gl_called = 0;
|
int32 sub_gl_called = 0;
|
||||||
|
|
||||||
extern "C" int qb64_custom_event(int event, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, void *p1, void *p2);
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
extern "C" LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(QB64_LINUX) && defined(QB64_GUI)
|
|
||||||
extern "C" void qb64_os_event_linux(XEvent *event, Display *display, int *qb64_os_event_info);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define QB64_EVENT_CLOSE 1
|
#define QB64_EVENT_CLOSE 1
|
||||||
#define QB64_EVENT_KEY 2
|
#define QB64_EVENT_KEY 2
|
||||||
#define QB64_EVENT_RELATIVE_MOUSE_MOVEMENT 3
|
#define QB64_EVENT_RELATIVE_MOUSE_MOVEMENT 3
|
||||||
|
@ -23910,448 +23901,6 @@ int32 func__exit() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(QB64_LINUX) && defined(QB64_GUI)
|
|
||||||
|
|
||||||
// X11 clipboard interface for Linux
|
|
||||||
// SDL_SysWMinfo syswminfo;
|
|
||||||
Atom targets, utf8string, compoundtext, clipboard;
|
|
||||||
|
|
||||||
int x11filter(XEvent *x11event) {
|
|
||||||
static int i;
|
|
||||||
static char *cp;
|
|
||||||
static XSelectionRequestEvent *x11request;
|
|
||||||
static XSelectionEvent x11selectionevent;
|
|
||||||
static Atom mytargets[] = {XA_STRING, utf8string, compoundtext};
|
|
||||||
if (x11event->type == SelectionRequest) {
|
|
||||||
x11request = &x11event->xselectionrequest;
|
|
||||||
x11selectionevent.type = SelectionNotify;
|
|
||||||
x11selectionevent.serial = x11event->xany.send_event;
|
|
||||||
x11selectionevent.send_event = True;
|
|
||||||
x11selectionevent.display = X11_display;
|
|
||||||
x11selectionevent.requestor = x11request->requestor;
|
|
||||||
x11selectionevent.selection = x11request->selection;
|
|
||||||
x11selectionevent.target = None;
|
|
||||||
x11selectionevent.property = x11request->property;
|
|
||||||
x11selectionevent.time = x11request->time;
|
|
||||||
if (x11request->target == targets) {
|
|
||||||
XChangeProperty(X11_display, x11request->requestor, x11request->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)mytargets, 3);
|
|
||||||
} else {
|
|
||||||
if (x11request->target == compoundtext || x11request->target == utf8string || x11request->target == XA_STRING) {
|
|
||||||
cp = XFetchBytes(X11_display, &i);
|
|
||||||
XChangeProperty(X11_display, x11request->requestor, x11request->property, x11request->target, 8, PropModeReplace, (unsigned char *)cp, i);
|
|
||||||
XFree(cp);
|
|
||||||
} else {
|
|
||||||
x11selectionevent.property = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XSendEvent(x11request->display, x11request->requestor, 0, NoEventMask, (XEvent *)&x11selectionevent);
|
|
||||||
XSync(X11_display, False);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupx11clipboard() {
|
|
||||||
static int32 setup = 0;
|
|
||||||
if (!setup) {
|
|
||||||
setup = 1;
|
|
||||||
// SDL_GetWMInfo(&syswminfo);
|
|
||||||
// SDL_EventState(SDL_SYSWMEVENT,SDL_ENABLE);
|
|
||||||
// SDL_SetEventFilter(x11filter);
|
|
||||||
x11_lock();
|
|
||||||
targets = XInternAtom(X11_display, "TARGETS", False);
|
|
||||||
utf8string = XInternAtom(X11_display, "UTF8_STRING", False);
|
|
||||||
compoundtext = XInternAtom(X11_display, "COMPOUND_TEXT", False);
|
|
||||||
clipboard = XInternAtom(X11_display, "CLIPBOARD", False);
|
|
||||||
x11_unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void x11clipboardcopy(const char *text) {
|
|
||||||
setupx11clipboard();
|
|
||||||
x11_lock();
|
|
||||||
XStoreBytes(X11_display, text, strlen(text) + 1);
|
|
||||||
XSetSelectionOwner(X11_display, clipboard, X11_window, CurrentTime);
|
|
||||||
x11_unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *x11clipboardpaste() {
|
|
||||||
static int32 i;
|
|
||||||
static char *cp;
|
|
||||||
static unsigned char *cp2;
|
|
||||||
static Window x11selectionowner;
|
|
||||||
static XEvent x11event;
|
|
||||||
static unsigned long data_items, bytes_remaining, ignore;
|
|
||||||
static int format;
|
|
||||||
static Atom type;
|
|
||||||
cp = NULL;
|
|
||||||
cp2 = NULL;
|
|
||||||
setupx11clipboard();
|
|
||||||
// syswminfo.info.x11.lock_func();
|
|
||||||
x11_lock();
|
|
||||||
x11selectionowner = XGetSelectionOwner(X11_display, clipboard);
|
|
||||||
if (x11selectionowner != None) {
|
|
||||||
// The XGetSelectionOwner() function returns the window ID associated with the window
|
|
||||||
if (x11selectionowner == X11_window) { // we are the provider, so just return buffered content
|
|
||||||
x11_unlock();
|
|
||||||
int bytes;
|
|
||||||
cp = XFetchBytes(X11_display, &bytes);
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
XConvertSelection(X11_display, clipboard, utf8string, clipboard, X11_window, CurrentTime);
|
|
||||||
XFlush(X11_display);
|
|
||||||
bool gotReply = false;
|
|
||||||
int timeoutMs = 10000; // 10sec
|
|
||||||
do {
|
|
||||||
XEvent event;
|
|
||||||
gotReply = XCheckTypedWindowEvent(X11_display, X11_window, SelectionNotify, &event);
|
|
||||||
if (gotReply) {
|
|
||||||
if (event.xselection.property == clipboard) {
|
|
||||||
XGetWindowProperty(X11_display, X11_window, clipboard, 0, 0, False, AnyPropertyType, &type, &format, &data_items, &bytes_remaining, &cp2);
|
|
||||||
if (cp2) {
|
|
||||||
XFree(cp2);
|
|
||||||
cp2 = NULL;
|
|
||||||
}
|
|
||||||
if (bytes_remaining) {
|
|
||||||
if (XGetWindowProperty(X11_display, X11_window, clipboard, 0, bytes_remaining, False, AnyPropertyType, &type, &format, &data_items,
|
|
||||||
&ignore, &cp2) == Success) {
|
|
||||||
cp = strdup((char *)cp2);
|
|
||||||
XFree(cp2);
|
|
||||||
XDeleteProperty(X11_display, X11_window, clipboard);
|
|
||||||
x11_unlock();
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x11_unlock();
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
x11_unlock();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Sleep(1);
|
|
||||||
timeoutMs -= 1;
|
|
||||||
} while (timeoutMs > 0);
|
|
||||||
} // x11selectionowner!=None
|
|
||||||
x11_unlock();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(QB64_LINUX)
|
|
||||||
void x11clipboardcopy(const char *text) {}
|
|
||||||
char *x11clipboardpaste() { return NULL; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
qbs *internal_clipboard = NULL; // used only if clipboard services unavailable
|
|
||||||
int32 linux_clipboard_init = 0;
|
|
||||||
|
|
||||||
void sub__clipboard(qbs *text) {
|
|
||||||
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static uint8 *textz;
|
|
||||||
static HGLOBAL h;
|
|
||||||
if (OpenClipboard(NULL)) {
|
|
||||||
EmptyClipboard();
|
|
||||||
h = GlobalAlloc(GMEM_MOVEABLE, text->len + 1);
|
|
||||||
if (h) {
|
|
||||||
textz = (uint8 *)GlobalLock(h);
|
|
||||||
if (textz) {
|
|
||||||
memcpy(textz, text->chr, text->len);
|
|
||||||
textz[text->len] = 0;
|
|
||||||
GlobalUnlock(h);
|
|
||||||
SetClipboardData(CF_TEXT, h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloseClipboard();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef QB64_MACOSX
|
|
||||||
PasteboardRef clipboard;
|
|
||||||
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (PasteboardClear(clipboard) != noErr) {
|
|
||||||
CFRelease(clipboard);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, text->chr, text->len, kCFAllocatorNull);
|
|
||||||
if (data == NULL) {
|
|
||||||
CFRelease(clipboard);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OSStatus err;
|
|
||||||
err = PasteboardPutItemFlavor(clipboard, NULL, kUTTypeUTF8PlainText, data, 0);
|
|
||||||
CFRelease(clipboard);
|
|
||||||
CFRelease(data);
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(QB64_LINUX)
|
|
||||||
static qbs *textz = NULL;
|
|
||||||
if (!textz)
|
|
||||||
textz = qbs_new(0, 0);
|
|
||||||
qbs_set(textz, qbs_add(text, qbs_new_txt_len("\0", 1)));
|
|
||||||
x11clipboardcopy((char *)textz->chr);
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (internal_clipboard == NULL)
|
|
||||||
internal_clipboard = qbs_new(0, 0);
|
|
||||||
qbs_set(internal_clipboard, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEPENDENCY_SCREENIMAGE
|
|
||||||
void sub__clipboardimage(int32 src) {
|
|
||||||
# ifdef QB64_WINDOWS
|
|
||||||
|
|
||||||
if (is_error_pending())
|
|
||||||
return;
|
|
||||||
|
|
||||||
static int32 i, i2, ii, w, h;
|
|
||||||
static uint32 *o, *o2;
|
|
||||||
static int32 x, y, n, c, i3, c2;
|
|
||||||
|
|
||||||
// validation
|
|
||||||
i = src;
|
|
||||||
if (i >= 0) { // validate i
|
|
||||||
validatepage(i);
|
|
||||||
i = page[i];
|
|
||||||
} else {
|
|
||||||
i = -i;
|
|
||||||
if (i >= nextimg) {
|
|
||||||
error(258);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!img[i].valid) {
|
|
||||||
error(258);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (img[i].text) {
|
|
||||||
error(5);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// end of validation
|
|
||||||
|
|
||||||
w = img[i].width;
|
|
||||||
h = img[i].height;
|
|
||||||
|
|
||||||
// source[http://support.microsoft.com/kb/318876]
|
|
||||||
HDC hdc;
|
|
||||||
BITMAPV5HEADER bi;
|
|
||||||
HBITMAP hBitmap;
|
|
||||||
void *lpBits;
|
|
||||||
ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
|
|
||||||
bi.bV5Size = sizeof(BITMAPV5HEADER);
|
|
||||||
bi.bV5Width = w;
|
|
||||||
bi.bV5Height = h;
|
|
||||||
bi.bV5Planes = 1;
|
|
||||||
bi.bV5BitCount = 32;
|
|
||||||
bi.bV5Compression = BI_RGB;
|
|
||||||
|
|
||||||
hdc = GetDC(NULL);
|
|
||||||
// Create the DIB section with an alpha channel.
|
|
||||||
hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, NULL, (DWORD)0);
|
|
||||||
|
|
||||||
// Transfer the source image to a new 32-bit image to avoid incompatible formats)
|
|
||||||
i2 = func__newimage(w, h, 32, 1);
|
|
||||||
sub__putimage(NULL, NULL, NULL, NULL, -i, i2, NULL, NULL, NULL, NULL, 8 + 32);
|
|
||||||
|
|
||||||
o = img[-i2].offset32;
|
|
||||||
o2 = (uint32 *)lpBits;
|
|
||||||
for (y = 0; y < h; y++) {
|
|
||||||
for (x = 0; x < w; x++) {
|
|
||||||
c = o[(h - 1 - y) * w + x];
|
|
||||||
o2[y * w + x] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub__freeimage(i2, 1);
|
|
||||||
|
|
||||||
// Create copy of hBitmap to send to the clipboard
|
|
||||||
HBITMAP bitmapCopy;
|
|
||||||
HDC hdc2, hdc3;
|
|
||||||
bitmapCopy = CreateCompatibleBitmap(hdc, w, h);
|
|
||||||
hdc2 = CreateCompatibleDC(hdc);
|
|
||||||
hdc3 = CreateCompatibleDC(hdc);
|
|
||||||
SelectObject(hdc2, bitmapCopy);
|
|
||||||
SelectObject(hdc3, hBitmap);
|
|
||||||
BitBlt(hdc2, 0, 0, w, h, hdc3, 0, 0, SRCCOPY);
|
|
||||||
|
|
||||||
ReleaseDC(NULL, hdc);
|
|
||||||
ReleaseDC(NULL, hdc2);
|
|
||||||
ReleaseDC(NULL, hdc3);
|
|
||||||
|
|
||||||
// Send bitmapCopy to the clipboard
|
|
||||||
if (OpenClipboard(NULL)) {
|
|
||||||
EmptyClipboard();
|
|
||||||
SetClipboardData(CF_BITMAP, bitmapCopy);
|
|
||||||
CloseClipboard();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteObject(hBitmap);
|
|
||||||
DeleteObject(bitmapCopy);
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEPENDENCY_SCREENIMAGE
|
|
||||||
int32 func__clipboardimage() {
|
|
||||||
# ifdef QB64_WINDOWS
|
|
||||||
|
|
||||||
if (is_error_pending())
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
static HBITMAP bitmap;
|
|
||||||
static BITMAP bitmapInfo;
|
|
||||||
static HDC hdc;
|
|
||||||
static int32 w, h;
|
|
||||||
|
|
||||||
if (OpenClipboard(NULL)) {
|
|
||||||
if (IsClipboardFormatAvailable(CF_BITMAP) == 0) {
|
|
||||||
CloseClipboard();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
|
|
||||||
CloseClipboard();
|
|
||||||
GetObject(bitmap, sizeof(BITMAP), &bitmapInfo);
|
|
||||||
h = bitmapInfo.bmHeight;
|
|
||||||
w = bitmapInfo.bmWidth;
|
|
||||||
|
|
||||||
static BITMAPFILEHEADER bmfHeader;
|
|
||||||
static BITMAPINFOHEADER bi;
|
|
||||||
bi.biSize = sizeof(BITMAPINFOHEADER);
|
|
||||||
bi.biWidth = w;
|
|
||||||
bi.biHeight = -h;
|
|
||||||
bi.biPlanes = 1;
|
|
||||||
bi.biBitCount = 32;
|
|
||||||
bi.biCompression = BI_RGB;
|
|
||||||
bi.biSizeImage = 0;
|
|
||||||
bi.biXPelsPerMeter = 0;
|
|
||||||
bi.biYPelsPerMeter = 0;
|
|
||||||
bi.biClrUsed = 0;
|
|
||||||
bi.biClrImportant = 0;
|
|
||||||
|
|
||||||
static int32 i, i2;
|
|
||||||
i2 = func__dest();
|
|
||||||
i = func__newimage(w, h, 32, 1);
|
|
||||||
sub__dest(i);
|
|
||||||
|
|
||||||
hdc = GetDC(NULL);
|
|
||||||
|
|
||||||
GetDIBits(hdc, bitmap, 0, h, write_page->offset, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
|
|
||||||
sub__setalpha(255, NULL, NULL, NULL, 0); // required as some images come
|
|
||||||
// with alpha 0 from the clipboard
|
|
||||||
sub__dest(i2);
|
|
||||||
|
|
||||||
ReleaseDC(NULL, hdc);
|
|
||||||
DeleteObject(bitmap);
|
|
||||||
return i;
|
|
||||||
|
|
||||||
} else
|
|
||||||
return -1;
|
|
||||||
# endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
qbs *func__clipboard() {
|
|
||||||
#ifdef QB64_WINDOWS
|
|
||||||
static qbs *text;
|
|
||||||
static uint8 *textz;
|
|
||||||
static HGLOBAL h;
|
|
||||||
if (OpenClipboard(NULL)) {
|
|
||||||
if (IsClipboardFormatAvailable(CF_TEXT)) {
|
|
||||||
h = GetClipboardData(CF_TEXT);
|
|
||||||
if (h) {
|
|
||||||
textz = (uint8 *)GlobalLock(h);
|
|
||||||
if (textz) {
|
|
||||||
text = qbs_new(strlen((char *)textz), 1);
|
|
||||||
memcpy(text->chr, textz, text->len);
|
|
||||||
GlobalUnlock(h);
|
|
||||||
CloseClipboard();
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloseClipboard();
|
|
||||||
}
|
|
||||||
text = qbs_new(0, 1);
|
|
||||||
return text;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef QB64_MACOSX
|
|
||||||
static qbs *text;
|
|
||||||
OSStatus err = noErr;
|
|
||||||
ItemCount itemCount;
|
|
||||||
PasteboardSyncFlags syncFlags;
|
|
||||||
static PasteboardRef inPasteboard = NULL;
|
|
||||||
PasteboardCreate(kPasteboardClipboard, &inPasteboard);
|
|
||||||
char *data;
|
|
||||||
data = "";
|
|
||||||
syncFlags = PasteboardSynchronize(inPasteboard);
|
|
||||||
err = badPasteboardSyncErr;
|
|
||||||
|
|
||||||
err = PasteboardGetItemCount(inPasteboard, &itemCount);
|
|
||||||
if ((err) != noErr)
|
|
||||||
goto CantGetPasteboardItemCount;
|
|
||||||
|
|
||||||
for (int itemIndex = 1; itemIndex <= itemCount; itemIndex++) {
|
|
||||||
PasteboardItemID itemID;
|
|
||||||
CFDataRef flavorData;
|
|
||||||
|
|
||||||
err = PasteboardGetItemIdentifier(inPasteboard, itemIndex, &itemID);
|
|
||||||
if ((err) != noErr)
|
|
||||||
goto CantGetPasteboardItemIdentifier;
|
|
||||||
|
|
||||||
err = PasteboardCopyItemFlavorData(inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
|
|
||||||
if ((err) != noErr)
|
|
||||||
goto CantGetPasteboardItemCount;
|
|
||||||
data = (char *)CFDataGetBytePtr(flavorData);
|
|
||||||
|
|
||||||
uint32 size;
|
|
||||||
size = CFDataGetLength(flavorData);
|
|
||||||
|
|
||||||
text = qbs_new(size, 1);
|
|
||||||
memcpy(text->chr, data, text->len);
|
|
||||||
// CFRelease (flavorData);
|
|
||||||
// CFRelease (flavorTypeArray);
|
|
||||||
// CFRelease(inPasteboard);
|
|
||||||
return text;
|
|
||||||
|
|
||||||
CantGetPasteboardItemIdentifier:;
|
|
||||||
}
|
|
||||||
|
|
||||||
CantGetPasteboardItemCount:
|
|
||||||
text = qbs_new(0, 1);
|
|
||||||
return text;
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(QB64_LINUX)
|
|
||||||
qbs *text;
|
|
||||||
char *cp = x11clipboardpaste();
|
|
||||||
cp = x11clipboardpaste();
|
|
||||||
if (!cp) {
|
|
||||||
text = qbs_new(0, 1);
|
|
||||||
} else {
|
|
||||||
text = qbs_new(strlen(cp), 1);
|
|
||||||
memcpy(text->chr, cp, text->len);
|
|
||||||
free(cp);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (internal_clipboard == NULL)
|
|
||||||
internal_clipboard = qbs_new(0, 0);
|
|
||||||
return internal_clipboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 display_called = 0;
|
int32 display_called = 0;
|
||||||
void display_now() {
|
void display_now() {
|
||||||
if (autodisplay) {
|
if (autodisplay) {
|
||||||
|
@ -32397,8 +31946,6 @@ extern "C" void qb64_os_event_linux(XEvent *event, Display *display, int *qb64_o
|
||||||
X11_display = display;
|
X11_display = display;
|
||||||
X11_window = event->xexpose.window;
|
X11_window = event->xexpose.window;
|
||||||
}
|
}
|
||||||
|
|
||||||
x11filter(event); // handles clipboard request events from other applications
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*qb64_os_event_info == OS_EVENT_POST_PROCESSING) {
|
if (*qb64_os_event_info == OS_EVENT_POST_PROCESSING) {
|
||||||
|
|
15
internal/c/libqb/include/clipboard.h
Normal file
15
internal/c/libqb/include/clipboard.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
// QB64-PE cross-platform clipboard support
|
||||||
|
// Powered by clip (https://github.com/dacap/clip)
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct qbs;
|
||||||
|
|
||||||
|
qbs *func__clipboard();
|
||||||
|
void sub__clipboard(const qbs *qbsText);
|
||||||
|
int32_t func__clipboardimage();
|
||||||
|
void sub__clipboardimage(int32_t src);
|
|
@ -1,4 +1,4 @@
|
||||||
//----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
// ___ ___ __ _ _ ___ ___ ___ _ _ _
|
// ___ ___ __ _ _ ___ ___ ___ _ _ _
|
||||||
// / _ \| _ ) / /| | || _ \ __| |_ _|_ __ __ _ __ _ ___ | | (_) |__ _ _ __ _ _ _ _ _
|
// / _ \| _ ) / /| | || _ \ __| |_ _|_ __ __ _ __ _ ___ | | (_) |__ _ _ __ _ _ _ _ _
|
||||||
// | (_) | _ \/ _ \_ _| _/ _| | || ' \/ _` / _` / -_) | |__| | '_ \ '_/ _` | '_| || |
|
// | (_) | _ \/ _ \_ _| _/ _| | || ' \/ _` / _` / -_) | |__| | '_ \ '_/ _` | '_| || |
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
@ -38,16 +39,42 @@
|
||||||
# define IMAGE_DEBUG_CHECK(_exp_) // Don't do anything in release builds
|
# define IMAGE_DEBUG_CHECK(_exp_) // Don't do anything in release builds
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The byte ordering here are straight from libqb.cpp. So, if libqb.cpp is wrong, then we are wrong! ;)
|
// This is returned to the caller if something goes wrong while loading the image
|
||||||
#define IMAGE_GET_BGRA_RED(c) ((uint8_t)((uint32_t)(c) >> 16 & 0xFFu))
|
#define INVALID_IMAGE_HANDLE -1
|
||||||
#define IMAGE_GET_BGRA_GREEN(c) ((uint8_t)((uint32_t)(c) >> 8 & 0xFFu))
|
|
||||||
#define IMAGE_GET_BGRA_BLUE(c) ((uint8_t)((uint32_t)(c) & 0xFFu))
|
|
||||||
#define IMAGE_GET_BGRA_ALPHA(c) ((uint8_t)((uint32_t)(c) >> 24))
|
|
||||||
#define IMAGE_GET_BGRA_BGR(c) ((uint32_t)(c) & 0xFFFFFFu)
|
|
||||||
#define IMAGE_MAKE_BGRA(r, g, b, a) \
|
|
||||||
((uint32_t)((uint8_t)(b) | ((uint32_t)((uint8_t)(g)) << 8) | ((uint32_t)((uint8_t)(r)) << 16) | ((uint32_t)((uint8_t)(a)) << 24)))
|
|
||||||
|
|
||||||
struct qbs;
|
struct qbs;
|
||||||
|
|
||||||
int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int32_t passed);
|
int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int32_t passed);
|
||||||
void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, int32_t passed);
|
void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, int32_t passed);
|
||||||
|
|
||||||
|
static inline constexpr uint8_t image_get_bgra_red(const uint32_t c) { return (uint8_t)((c >> 16) & 0xFFu); }
|
||||||
|
|
||||||
|
static inline constexpr uint8_t image_get_bgra_green(const uint32_t c) { return (uint8_t)((c >> 8) & 0xFFu); }
|
||||||
|
|
||||||
|
static inline constexpr uint8_t image_get_bgra_blue(const uint32_t c) { return (uint8_t)(c & 0xFFu); }
|
||||||
|
|
||||||
|
static inline constexpr uint8_t image_get_bgra_alpha(const uint32_t c) { return (uint8_t)(c >> 24); }
|
||||||
|
|
||||||
|
static inline constexpr uint32_t image_get_bgra_bgr(const uint32_t c) { return (uint32_t)(c & 0xFFFFFFu); }
|
||||||
|
|
||||||
|
static inline constexpr uint32_t image_make_bgra(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) {
|
||||||
|
return (uint32_t)(b) | ((uint32_t)(g) << 8) | ((uint32_t)(r) << 16) | ((uint32_t)(a) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr int image_scale_5bits_to_8bits(const int v) { return (v << 3) | (v >> 2); }
|
||||||
|
|
||||||
|
static inline constexpr int image_scale_6bits_to_8bits(const int v) { return (v << 2) | (v >> 4); }
|
||||||
|
|
||||||
|
static inline constexpr uint32_t image_swap_red_blue(const uint32_t clr) {
|
||||||
|
return ((clr & 0xFF00FF00u) | ((clr & 0x00FF0000u) >> 16) | ((clr & 0x000000FFu) << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr uint8_t image_clamp_color_component(const int n) { return n < 0 ? 0 : n > 255 ? 255 : n; }
|
||||||
|
|
||||||
|
static inline float image_calculate_rgb_distance(const uint8_t r1, const uint8_t g1, const uint8_t b1, const uint8_t r2, const uint8_t g2, const uint8_t b2) {
|
||||||
|
auto delta_r = (float)r2 - (float)r1;
|
||||||
|
auto delta_g = (float)g2 - (float)g1;
|
||||||
|
auto delta_b = (float)b2 - (float)b1;
|
||||||
|
|
||||||
|
return sqrtf(delta_r * delta_r + delta_g * delta_g + delta_b * delta_b);
|
||||||
|
}
|
||||||
|
|
|
@ -1047,9 +1047,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// QB64-PE: custom code begin
|
// QB64-PE: custom code begin
|
||||||
int qb64_os_event_info = 0;
|
int qb64_os_event_info = 1;
|
||||||
|
|
||||||
qb64_os_event_info = 1;
|
|
||||||
qb64_os_event_linux(&event, fgDisplay.Display, &qb64_os_event_info);
|
qb64_os_event_linux(&event, fgDisplay.Display, &qb64_os_event_info);
|
||||||
if (qb64_os_event_info == 3)
|
if (qb64_os_event_info == 3)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -229,10 +229,10 @@ uint32_t func__guiColorChooserDialog(qbs *qbsTitle, uint32_t nDefaultRGB, int32_
|
||||||
nDefaultRGB = 0;
|
nDefaultRGB = 0;
|
||||||
|
|
||||||
// Break the color into RGB components
|
// Break the color into RGB components
|
||||||
uint8_t lRGB[3] = {IMAGE_GET_BGRA_RED(nDefaultRGB), IMAGE_GET_BGRA_GREEN(nDefaultRGB), IMAGE_GET_BGRA_BLUE(nDefaultRGB)};
|
uint8_t lRGB[3] = {image_get_bgra_red(nDefaultRGB), image_get_bgra_green(nDefaultRGB), image_get_bgra_blue(nDefaultRGB)};
|
||||||
|
|
||||||
// On cancel, return 0 (i.e. no color, no alpha, nothing). Else, return color with alpha set to 255
|
// On cancel, return 0 (i.e. no color, no alpha, nothing). Else, return color with alpha set to 255
|
||||||
return !tinyfd_colorChooser(aTitle.c_str(), nullptr, lRGB, lRGB) ? 0 : IMAGE_MAKE_BGRA(lRGB[0], lRGB[1], lRGB[2], 0xFF);
|
return !tinyfd_colorChooser(aTitle.c_str(), nullptr, lRGB, lRGB) ? 0 : image_make_bgra(lRGB[0], lRGB[1], lRGB[2], 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Shows the system file open dialog box
|
/// @brief Shows the system file open dialog box
|
||||||
|
|
56
internal/c/parts/os/clipboard/build.mk
Normal file
56
internal/c/parts/os/clipboard/build.mk
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
# clip Setup:
|
||||||
|
# Download the latest release from https://github.com/dacap/clip
|
||||||
|
# Copy all source files except clip_none.cpp to internal/c/parts/os/clipboard/clip
|
||||||
|
# Compile the source using -DCLIP_ENABLE_IMAGE=1, -DHAVE_XCB_XLIB_H (Linux) and DHAVE_PNG_H (Linux)
|
||||||
|
|
||||||
|
CLIP_DEFS := -DCLIP_ENABLE_IMAGE=1
|
||||||
|
|
||||||
|
CLIP_SRCS := \
|
||||||
|
clip.cpp \
|
||||||
|
image.cpp
|
||||||
|
|
||||||
|
ifeq ($(OS),lnx)
|
||||||
|
CLIP_SRCS += clip_x11.cpp
|
||||||
|
CLIP_DEFS += -DHAVE_XCB_XLIB_H -DHAVE_PNG_H
|
||||||
|
CXXLIBS += -lpng
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(OS),win)
|
||||||
|
CLIP_SRCS += clip_win.cpp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(OS),osx)
|
||||||
|
CLIP_OSX_SRCS := clip_osx.mm
|
||||||
|
endif
|
||||||
|
|
||||||
|
CLIPBOARD_SRCS := clipboard.cpp
|
||||||
|
|
||||||
|
CLIP_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.o,$(CLIP_SRCS))
|
||||||
|
|
||||||
|
ifeq ($(OS),osx)
|
||||||
|
CLIP_OBJS += $(patsubst %.mm,$(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.o,$(CLIP_OSX_SRCS))
|
||||||
|
endif
|
||||||
|
|
||||||
|
CLIPBOARD_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/os/clipboard/%.o,$(CLIPBOARD_SRCS))
|
||||||
|
|
||||||
|
$(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.o: $(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.cpp
|
||||||
|
$(CXX) -O2 $(CXXFLAGS) $(CLIP_DEFS) -w $< -c -o $@
|
||||||
|
|
||||||
|
ifeq ($(OS),osx)
|
||||||
|
$(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.o: $(PATH_INTERNAL_C)/parts/os/clipboard/clip/%.mm
|
||||||
|
$(CXX) -O2 $(CXXFLAGS) $(CLIP_DEFS) -w $< -c -o $@
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(PATH_INTERNAL_C)/parts/os/clipboard/%.o: $(PATH_INTERNAL_C)/parts/os/clipboard/%.cpp
|
||||||
|
$(CXX) -O2 $(CXXFLAGS) $(CLIP_DEFS) -Wall -Wextra $< -c -o $@
|
||||||
|
|
||||||
|
CLIPBOARD_LIB := $(PATH_INTERNAL_C)/parts/os/clipboard/clipboard.a
|
||||||
|
|
||||||
|
$(CLIPBOARD_LIB): $(CLIP_OBJS) $(CLIPBOARD_OBJS)
|
||||||
|
$(AR) rcs $@ $(CLIP_OBJS) $(CLIPBOARD_OBJS)
|
||||||
|
|
||||||
|
EXE_LIBS += $(CLIPBOARD_LIB)
|
||||||
|
|
||||||
|
CLEAN_LIST += $(CLIPBOARD_LIB) $(CLIP_OBJS) $(CLIPBOARD_OBJS)
|
||||||
|
|
184
internal/c/parts/os/clipboard/clip/clip.cpp
Normal file
184
internal/c/parts/os/clipboard/clip/clip.cpp
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// 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 <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
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<int>(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
|
||||||
|
|
||||||
|
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<char> 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
|
190
internal/c/parts/os/clipboard/clip/clip.h
Normal file
190
internal/c/parts/os/clipboard/clip/clip.h
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
// 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 <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clip {
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
// Low-level API to lock the clipboard/pasteboard and modify it
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
// Clipboard format identifier.
|
||||||
|
typedef size_t format;
|
||||||
|
|
||||||
|
class image;
|
||||||
|
struct image_spec;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
private:
|
||||||
|
class impl;
|
||||||
|
std::unique_ptr<impl> 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
|
80
internal/c/parts/os/clipboard/clip/clip_common.h
Normal file
80
internal/c/parts/os/clipboard/clip/clip_common.h
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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.height; ++y) {
|
||||||
|
const uint32_t* dst = (uint32_t*)(img.data()+y*spec.bytes_per_row);
|
||||||
|
for (unsigned long x=0; x<spec.width; ++x, ++dst) {
|
||||||
|
const uint32_t c = *dst;
|
||||||
|
const int r = ((c & spec.red_mask ) >> 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.height; ++y) {
|
||||||
|
uint32_t* dst = (uint32_t*)(img.data()+y*spec.bytes_per_row);
|
||||||
|
for (unsigned long x=0; x<spec.width; ++x, ++dst) {
|
||||||
|
const uint32_t c = *dst;
|
||||||
|
int r = ((c & spec.red_mask ) >> 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
|
33
internal/c/parts/os/clipboard/clip/clip_lock_impl.h
Normal file
33
internal/c/parts/os/clipboard/clip/clip_lock_impl.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#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;
|
||||||
|
bool set_image(const image& image);
|
||||||
|
bool get_image(image& image) const;
|
||||||
|
bool get_image_spec(image_spec& spec) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_locked;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace clip
|
||||||
|
|
||||||
|
#endif
|
371
internal/c/parts/os/clipboard/clip/clip_osx.mm
Normal file
371
internal/c/parts/os/clipboard/clip/clip_osx.mm
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
// 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 <cassert>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
|
||||||
|
namespace clip {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
format g_last_format = 100;
|
||||||
|
std::map<std::string, format> g_name_to_format;
|
||||||
|
std::map<format, std::string> g_format_to_name;
|
||||||
|
|
||||||
|
#if CLIP_ENABLE_IMAGE
|
||||||
|
|
||||||
|
bool get_image_from_clipboard(image* output_img,
|
||||||
|
image_spec* output_spec)
|
||||||
|
{
|
||||||
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||||
|
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<shifts+3; ++shift) {
|
||||||
|
*shift = bits_shift;
|
||||||
|
bits_shift += bits_per_sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
// With alpha
|
||||||
|
if (bitmap.alpha) {
|
||||||
|
if (bitmap.bitmapFormat & NSBitmapFormatSixteenBitBigEndian ||
|
||||||
|
bitmap.bitmapFormat & NSBitmapFormatThirtyTwoBitBigEndian) {
|
||||||
|
std::swap(spec.red_shift, spec.alpha_shift);
|
||||||
|
std::swap(spec.green_shift, spec.blue_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Without alpha
|
||||||
|
else {
|
||||||
|
if (bitmap.bitmapFormat & NSBitmapFormatSixteenBitBigEndian ||
|
||||||
|
bitmap.bitmapFormat & NSBitmapFormatThirtyTwoBitBigEndian) {
|
||||||
|
std::swap(spec.red_shift, spec.blue_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate all masks
|
||||||
|
for (unsigned long* shift=shifts, *mask=masks; shift<shifts+4; ++shift, ++mask)
|
||||||
|
*mask = ((1ul<<bits_per_sample)-1ul) << (*shift);
|
||||||
|
|
||||||
|
// Without alpha
|
||||||
|
if (!bitmap.alpha)
|
||||||
|
spec.alpha_mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_spec) {
|
||||||
|
*output_spec = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_img) {
|
||||||
|
unsigned long size = spec.bytes_per_row*spec.height;
|
||||||
|
image img(spec);
|
||||||
|
|
||||||
|
std::copy(bitmap.bitmapData,
|
||||||
|
bitmap.bitmapData+size, img.data());
|
||||||
|
|
||||||
|
// Convert premultiplied data to unpremultiplied if needed.
|
||||||
|
if (bitmap.alpha &&
|
||||||
|
bitmap.samplesPerPixel >= 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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<unsigned char*> 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 {
|
||||||
|
return get_image_from_clipboard(&img, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock::impl::get_image_spec(image_spec& spec) const {
|
||||||
|
return get_image_from_clipboard(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
|
659
internal/c/parts/os/clipboard/clip/clip_win.cpp
Normal file
659
internal/c/parts/os/clipboard/clip/clip_win.cpp
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
// Clip Library
|
||||||
|
// Copyright (C) 2015-2020 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 <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#if CLIP_ENABLE_IMAGE
|
||||||
|
#include "clip_win_wic.h"
|
||||||
|
#endif // CLIP_ENABLE_IMAGE
|
||||||
|
|
||||||
|
#ifndef LCS_WINDOWS_COLOR_SPACE
|
||||||
|
#define LCS_WINDOWS_COLOR_SPACE 'Win '
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CF_DIBV5
|
||||||
|
#define CF_DIBV5 17
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
unsigned long get_shift_from_mask(unsigned long mask) {
|
||||||
|
unsigned long shift = 0;
|
||||||
|
for (shift=0; shift<sizeof(unsigned long)*8; ++shift)
|
||||||
|
if (mask & (1 << shift))
|
||||||
|
return shift;
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CLIP_ENABLE_IMAGE
|
||||||
|
|
||||||
|
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() {
|
||||||
|
// Use DIBV5 only for 32 bpp uncompressed bitmaps and when all
|
||||||
|
// masks are valid.
|
||||||
|
if (IsClipboardFormatAvailable(CF_DIBV5)) {
|
||||||
|
b5 = (BITMAPV5HEADER*)GetClipboardData(CF_DIBV5);
|
||||||
|
if (b5 &&
|
||||||
|
b5->bV5BitCount == 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsClipboardFormatAvailable(CF_DIB))
|
||||||
|
bi = (BITMAPINFO*)GetClipboardData(CF_DIB);
|
||||||
|
if (!bi)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid() const {
|
||||||
|
return (b5 || bi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_spec(image_spec& spec) {
|
||||||
|
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; shift<shifts+4; ++shift, ++mask) {
|
||||||
|
if (*mask)
|
||||||
|
*shift = get_shift_from_mask(*mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIP_ENABLE_IMAGE
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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) ? true: false);
|
||||||
|
}
|
||||||
|
#endif // CLIP_ENABLE_IMAGE
|
||||||
|
else if (IsClipboardFormatAvailable(f))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<LPWSTR>(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<LPWSTR>(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<LPSTR>(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<LPWSTR>(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_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 false;
|
||||||
|
|
||||||
|
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.width; ++x, ++src_x, ++dst_x) {
|
||||||
|
uint32_t c = *src_x;
|
||||||
|
int r = ((c & spec.red_mask ) >> 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:
|
||||||
|
error_handler e = get_error_handler();
|
||||||
|
if (e)
|
||||||
|
e(ErrorCode::ImageNotSupported);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalUnlock(hmem);
|
||||||
|
SetClipboardData(CF_DIBV5, hmem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock::impl::get_image(image& output_img) const {
|
||||||
|
// Get the "PNG" clipboard format (this is useful only for 32bpp
|
||||||
|
// images with alpha channel, in other case we can use the regular
|
||||||
|
// DIB format)
|
||||||
|
UINT png_format = RegisterClipboardFormatA("PNG");
|
||||||
|
if (png_format && IsClipboardFormatAvailable(png_format)) {
|
||||||
|
HANDLE png_handle = GetClipboardData(png_format);
|
||||||
|
if (png_handle) {
|
||||||
|
size_t png_size = GlobalSize(png_handle);
|
||||||
|
uint8_t* png_data = (uint8_t*)GlobalLock(png_handle);
|
||||||
|
bool result = win::read_png(png_data, png_size, &output_img, nullptr);
|
||||||
|
GlobalUnlock(png_handle);
|
||||||
|
if (result)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapInfo bi;
|
||||||
|
if (!bi.is_valid()) {
|
||||||
|
// There is no image at all in the clipboard, no need to report
|
||||||
|
// this as an error, just return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
image_spec spec;
|
||||||
|
bi.fill_spec(spec);
|
||||||
|
image img(spec);
|
||||||
|
|
||||||
|
switch (bi.bit_count) {
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
case 24:
|
||||||
|
case 16: {
|
||||||
|
const uint8_t* src = nullptr;
|
||||||
|
|
||||||
|
if (bi.compression == BI_RGB ||
|
||||||
|
bi.compression == BI_BITFIELDS) {
|
||||||
|
if (bi.b5)
|
||||||
|
src = ((uint8_t*)bi.b5) + bi.b5->bV5Size;
|
||||||
|
else
|
||||||
|
src = ((uint8_t*)bi.bi) + bi.bi->bmiHeader.biSize;
|
||||||
|
if (bi.compression == BI_BITFIELDS)
|
||||||
|
src += sizeof(RGBQUAD)*3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
const int src_bytes_per_row = spec.width*((bi.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 (bi.bit_count == 32 && spec.alpha_mask) {
|
||||||
|
details::divide_rgb_by_alpha(img);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 8: {
|
||||||
|
assert(bi.bi);
|
||||||
|
|
||||||
|
const int colors = (bi.bi->bmiHeader.biClrUsed > 0 ? bi.bi->bmiHeader.biClrUsed: 256);
|
||||||
|
std::vector<uint32_t> palette(colors);
|
||||||
|
for (int c=0; c<colors; ++c) {
|
||||||
|
palette[c] =
|
||||||
|
(bi.bi->bmiColors[c].rgbRed << spec.red_shift) |
|
||||||
|
(bi.bi->bmiColors[c].rgbGreen << spec.green_shift) |
|
||||||
|
(bi.bi->bmiColors[c].rgbBlue << spec.blue_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* src = (((uint8_t*)bi.bi) + 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<spec.width; ++x, ++src, dst+=3) {
|
||||||
|
int idx = *src;
|
||||||
|
if (idx < 0)
|
||||||
|
idx = 0;
|
||||||
|
else if (idx >= colors)
|
||||||
|
idx = colors-1;
|
||||||
|
|
||||||
|
*((uint32_t*)dst) = palette[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::swap(output_img, img);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock::impl::get_image_spec(image_spec& spec) const {
|
||||||
|
UINT png_format = RegisterClipboardFormatA("PNG");
|
||||||
|
if (png_format && IsClipboardFormatAvailable(png_format)) {
|
||||||
|
HANDLE png_handle = GetClipboardData(png_format);
|
||||||
|
if (png_handle) {
|
||||||
|
size_t png_size = GlobalSize(png_handle);
|
||||||
|
uint8_t* png_data = (uint8_t*)GlobalLock(png_handle);
|
||||||
|
bool result = win::read_png(png_data, png_size, nullptr, &spec);
|
||||||
|
GlobalUnlock(png_handle);
|
||||||
|
if (result)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<WCHAR> 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
|
286
internal/c/parts/os/clipboard/clip/clip_win_wic.h
Normal file
286
internal/c/parts/os/clipboard/clip/clip_win_wic.h
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
// Clip Library
|
||||||
|
// Copyright (c) 2020-2022 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#include "clip.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <wincodec.h>
|
||||||
|
|
||||||
|
namespace clip {
|
||||||
|
namespace win {
|
||||||
|
|
||||||
|
// 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 T>
|
||||||
|
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
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Encode the image as PNG format
|
||||||
|
|
||||||
|
bool write_png_on_stream(const image& image,
|
||||||
|
IStream* stream) {
|
||||||
|
const image_spec& spec = image.spec();
|
||||||
|
|
||||||
|
comptr<IWICBitmapEncoder> 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<IWICBitmapFrameEncode> frame;
|
||||||
|
comptr<IPropertyBag2> 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<uint32_t> 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.height; ++y) {
|
||||||
|
auto src_line_start = src;
|
||||||
|
for (int x=0; x<spec.width; ++x) {
|
||||||
|
uint32_t c = *src;
|
||||||
|
*dst = ((((c & spec.red_mask ) >> 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<IStream> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Decode the clipboard data from PNG format
|
||||||
|
|
||||||
|
bool read_png(const uint8_t* buf,
|
||||||
|
const UINT len,
|
||||||
|
image* output_image,
|
||||||
|
image_spec* output_spec) {
|
||||||
|
coinit com;
|
||||||
|
|
||||||
|
#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<SHCreateMemStreamPtr>(GetProcAddress(shlwapiDll, (LPCSTR)12));
|
||||||
|
if (!SHCreateMemStream)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
comptr<IStream> stream(SHCreateMemStream(buf, len));
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
comptr<IWICBitmapDecoder> decoder;
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_WICPngDecoder2,
|
||||||
|
nullptr, CLSCTX_INPROC_SERVER,
|
||||||
|
IID_PPV_ARGS(&decoder));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
hr = CoCreateInstance(CLSID_WICPngDecoder1,
|
||||||
|
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;
|
||||||
|
|
||||||
|
hr = decoder->Initialize(stream.get(), WICDecodeMetadataCacheOnDemand);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
comptr<IWICBitmapFrameDecode> frame;
|
||||||
|
hr = decoder->GetFrame(0, &frame);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WICPixelFormatGUID pixelFormat;
|
||||||
|
hr = frame->GetPixelFormat(&pixelFormat);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only support this pixel format
|
||||||
|
// TODO add support for more pixel formats
|
||||||
|
if (pixelFormat != GUID_WICPixelFormat32bppBGRA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UINT width = 0, height = 0;
|
||||||
|
hr = frame->GetSize(&width, &height);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
image_spec spec;
|
||||||
|
spec.width = width;
|
||||||
|
spec.height = height;
|
||||||
|
spec.bits_per_pixel = 32;
|
||||||
|
spec.bytes_per_row = 4 * width;
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (output_spec)
|
||||||
|
*output_spec = spec;
|
||||||
|
|
||||||
|
if (output_image) {
|
||||||
|
image img(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace win
|
||||||
|
} // namespace clip
|
1123
internal/c/parts/os/clipboard/clip/clip_x11.cpp
Normal file
1123
internal/c/parts/os/clipboard/clip/clip_x11.cpp
Normal file
File diff suppressed because it is too large
Load diff
230
internal/c/parts/os/clipboard/clip/clip_x11_png.h
Normal file
230
internal/c/parts/os/clipboard/clip/clip_x11_png.h
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
// 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 <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint8_t>& output = *(std::vector<uint8_t>*)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<uint8_t>& 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.height; ++y) {
|
||||||
|
const uint32_t* src =
|
||||||
|
(const uint32_t*)(((const uint8_t*)image.data())
|
||||||
|
+ y*spec.bytes_per_row);
|
||||||
|
uint8_t* dst = row;
|
||||||
|
unsigned int x, c;
|
||||||
|
|
||||||
|
for (x=0; x<spec.width; x++) {
|
||||||
|
c = *(src++);
|
||||||
|
*(dst++) = (c & spec.red_mask ) >> 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<height; ++y)
|
||||||
|
rows[y] = (png_bytep)png_malloc(png, src_bytes_per_row);
|
||||||
|
|
||||||
|
for (int pass=0; pass<number_passes; ++pass)
|
||||||
|
for (y=0; y<height; ++y)
|
||||||
|
png_read_rows(png, rows+y, nullptr, 1);
|
||||||
|
|
||||||
|
for (y=0; y<height; ++y) {
|
||||||
|
const uint8_t* src = rows[y];
|
||||||
|
uint32_t* dst = (uint32_t*)(img.data() + y*spec.bytes_per_row);
|
||||||
|
unsigned int x, r, g, b, a = 0;
|
||||||
|
|
||||||
|
for (x=0; x<width; x++) {
|
||||||
|
r = *(src++);
|
||||||
|
g = *(src++);
|
||||||
|
b = *(src++);
|
||||||
|
if (spec.alpha_mask)
|
||||||
|
a = *(src++);
|
||||||
|
*(dst++) =
|
||||||
|
(r << spec.red_shift) |
|
||||||
|
(g << spec.green_shift) |
|
||||||
|
(b << spec.blue_shift) |
|
||||||
|
(a << spec.alpha_shift);
|
||||||
|
}
|
||||||
|
png_free(png, rows[y]);
|
||||||
|
}
|
||||||
|
png_free(png, rows);
|
||||||
|
|
||||||
|
std::swap(*output_image, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png, &info, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace x11
|
||||||
|
} // namespace clip
|
99
internal/c/parts/os/clipboard/clip/image.cpp
Normal file
99
internal/c/parts/os/clipboard/clip/image.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Clip Library
|
||||||
|
// Copyright (c) 2015-2022 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#include "clip.h"
|
||||||
|
|
||||||
|
namespace clip {
|
||||||
|
|
||||||
|
unsigned long image_spec::required_data_size() const
|
||||||
|
{
|
||||||
|
unsigned long n = (bytes_per_row * height);
|
||||||
|
|
||||||
|
// For 24bpp we add some extra space to access the last pixel (3
|
||||||
|
// bytes) as an uint32_t
|
||||||
|
if (bits_per_pixel == 24) {
|
||||||
|
if ((n % 4) > 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
|
392
internal/c/parts/os/clipboard/clipboard.cpp
Normal file
392
internal/c/parts/os/clipboard/clipboard.cpp
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
// QB64-PE cross-platform clipboard support
|
||||||
|
// Powered by clip (https://github.com/dacap/clip)
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "libqb-common.h"
|
||||||
|
|
||||||
|
// We need 'qbs' and 'image' structs stuff from here. Stop using this when image and friends are refactored
|
||||||
|
#include "../../../libqb.h"
|
||||||
|
|
||||||
|
// This is not strictly needed. But we'll leave it here for VSCode to do it's magic
|
||||||
|
#define CLIP_ENABLE_IMAGE 1
|
||||||
|
#include "clip/clip.h"
|
||||||
|
#include "clipboard.h"
|
||||||
|
#include "error_handle.h"
|
||||||
|
#define IMAGE_DEBUG 0
|
||||||
|
#include "image.h"
|
||||||
|
#include "qbs.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef QB64_MACOSX
|
||||||
|
# include <ApplicationServices/ApplicationServices.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const img_struct *img; // used by sub__clipboardimage()
|
||||||
|
extern const img_struct *write_page; // used by func__clipboardimage()
|
||||||
|
extern const int32_t *page; // used by sub__clipboardimage()
|
||||||
|
extern const int32_t nextimg; // used by sub__clipboardimage()
|
||||||
|
extern const uint8_t charset8x8[256][8][8]; // used by sub__clipboardimage()
|
||||||
|
extern const uint8_t charset8x16[256][16][8]; // used by sub__clipboardimage()
|
||||||
|
|
||||||
|
// This is used as a fallback internal clipboard should any text clipboard functions below fail
|
||||||
|
static std::string g_InternalClipboard;
|
||||||
|
|
||||||
|
/// @brief Gets text (if present) in the OS clipboard.
|
||||||
|
/// @return A qbs string.
|
||||||
|
qbs *func__clipboard() {
|
||||||
|
#if defined(QB64_MACOSX)
|
||||||
|
|
||||||
|
// We'll use our own clipboard get code on macOS since our requirements are different than what clip supports
|
||||||
|
PasteboardRef clipboard = nullptr;
|
||||||
|
OSStatus err = PasteboardCreate(kPasteboardClipboard, &clipboard);
|
||||||
|
|
||||||
|
if (err == noErr) {
|
||||||
|
PasteboardSynchronize(clipboard);
|
||||||
|
ItemCount itemCount = 0;
|
||||||
|
|
||||||
|
err = PasteboardGetItemCount(clipboard, &itemCount);
|
||||||
|
if (err == noErr) {
|
||||||
|
for (ItemCount itemIndex = 1; itemIndex <= itemCount; itemIndex++) {
|
||||||
|
PasteboardItemID itemID = nullptr;
|
||||||
|
err = PasteboardGetItemIdentifier(clipboard, itemIndex, &itemID);
|
||||||
|
if (err != noErr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CFDataRef flavorData = nullptr;
|
||||||
|
err = PasteboardCopyItemFlavorData(clipboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
|
||||||
|
if (err == noErr) {
|
||||||
|
g_InternalClipboard.assign(reinterpret_cast<const char *>(CFDataGetBytePtr(flavorData)), CFDataGetLength(flavorData));
|
||||||
|
|
||||||
|
CFRelease(flavorData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(QB64_WINDOWS)
|
||||||
|
|
||||||
|
// We'll need custom code for Windows because clip does automatic UTF-8 conversions that leads to some undesired behavior when copying extended ASCII
|
||||||
|
if (OpenClipboard(NULL)) {
|
||||||
|
if (IsClipboardFormatAvailable(CF_TEXT)) {
|
||||||
|
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
|
||||||
|
|
||||||
|
if (hClipboardData) {
|
||||||
|
auto pchData = reinterpret_cast<const char *>(GlobalLock(hClipboardData));
|
||||||
|
|
||||||
|
if (pchData) {
|
||||||
|
g_InternalClipboard.assign(pchData, strlen(pchData));
|
||||||
|
|
||||||
|
GlobalUnlock(hClipboardData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// clip works like we want on Linux
|
||||||
|
if (clip::has(clip::text_format()))
|
||||||
|
clip::get_text(g_InternalClipboard);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto qbsText = qbs_new(g_InternalClipboard.length(), 1);
|
||||||
|
if (qbsText->len)
|
||||||
|
memcpy(qbsText->chr, g_InternalClipboard.data(), qbsText->len);
|
||||||
|
|
||||||
|
return qbsText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Sets text to the OS clipboard.
|
||||||
|
/// @param qbsText A qbs string.
|
||||||
|
void sub__clipboard(const qbs *qbsText) {
|
||||||
|
g_InternalClipboard.assign(reinterpret_cast<const char *>(qbsText->chr), qbsText->len);
|
||||||
|
|
||||||
|
if (qbsText->len) {
|
||||||
|
#if defined(QB64_MACOSX)
|
||||||
|
|
||||||
|
// We'll use our own clipboard set code on macOS since our requirements are different than what clip supports
|
||||||
|
PasteboardRef clipboard;
|
||||||
|
if (PasteboardCreate(kPasteboardClipboard, &clipboard) == noErr) {
|
||||||
|
if (PasteboardClear(clipboard) == noErr) {
|
||||||
|
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, qbsText->chr, qbsText->len, kCFAllocatorNull);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
PasteboardPutItemFlavor(clipboard, nullptr, kUTTypeUTF8PlainText, data, 0);
|
||||||
|
|
||||||
|
CFRelease(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(QB64_WINDOWS)
|
||||||
|
|
||||||
|
// We'll need custom code for Windows because clip does automatic UTF-8 conversions that leads to some undesired behavior when copying extended ASCII
|
||||||
|
if (OpenClipboard(NULL)) {
|
||||||
|
if (EmptyClipboard()) {
|
||||||
|
HGLOBAL hClipboardData = GlobalAlloc(GMEM_MOVEABLE, qbsText->len + 1);
|
||||||
|
|
||||||
|
if (hClipboardData) {
|
||||||
|
auto pchData = reinterpret_cast<uint8_t *>(GlobalLock(hClipboardData));
|
||||||
|
|
||||||
|
if (pchData) {
|
||||||
|
memcpy(pchData, qbsText->chr, qbsText->len);
|
||||||
|
pchData[qbsText->len] = '\0'; // null terminate
|
||||||
|
|
||||||
|
GlobalUnlock(hClipboardData);
|
||||||
|
|
||||||
|
SetClipboardData(CF_TEXT, hClipboardData);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalFree(hClipboardData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// clip works like we want on Linux
|
||||||
|
clip::set_text(g_InternalClipboard);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Retuns an image handle of an image from the clipboard (if present).
|
||||||
|
/// @return A valid image handle. Returns -1 if clipboard format is not supported or if there is nothing.
|
||||||
|
int32_t func__clipboardimage() {
|
||||||
|
int32_t qb64Img = INVALID_IMAGE_HANDLE; // assume failure
|
||||||
|
|
||||||
|
if (is_error_pending())
|
||||||
|
return qb64Img;
|
||||||
|
|
||||||
|
if (clip::has(clip::image_format())) {
|
||||||
|
clip::image clipImg;
|
||||||
|
|
||||||
|
IMAGE_DEBUG_PRINT("Clipboard image found");
|
||||||
|
|
||||||
|
if (clip::get_image(clipImg)) {
|
||||||
|
auto spec = clipImg.spec();
|
||||||
|
|
||||||
|
IMAGE_DEBUG_PRINT("Image (%lu x %lu) @ %lubpp", spec.width, spec.height, spec.bits_per_pixel);
|
||||||
|
|
||||||
|
if (spec.width && spec.height) {
|
||||||
|
auto oldDest = func__dest();
|
||||||
|
|
||||||
|
// We only support 32bpp images. Images in other formats are converted to 32bpp BGRA
|
||||||
|
qb64Img = func__newimage(spec.width, spec.height, 32, 1);
|
||||||
|
|
||||||
|
if (qb64Img < INVALID_IMAGE_HANDLE) {
|
||||||
|
sub__dest(qb64Img);
|
||||||
|
auto dst = write_page->offset32;
|
||||||
|
|
||||||
|
IMAGE_DEBUG_PRINT("Converting and copying image");
|
||||||
|
|
||||||
|
// Convert and copy the image based on the bpp
|
||||||
|
switch (spec.bits_per_pixel) {
|
||||||
|
case 64:
|
||||||
|
for (uint32_t y = 0; y < spec.height; y++) {
|
||||||
|
auto src = reinterpret_cast<const uint64_t *>(clipImg.data() + spec.bytes_per_row * y);
|
||||||
|
for (uint32_t x = 0; x < spec.width; x++, src++) {
|
||||||
|
auto c = *src;
|
||||||
|
*dst = image_make_bgra((c & spec.red_mask) >> spec.red_shift >> 8, (c & spec.green_mask) >> spec.green_shift >> 8,
|
||||||
|
(c & spec.blue_mask) >> spec.blue_shift >> 8, (c & spec.alpha_mask) >> spec.alpha_shift >> 8);
|
||||||
|
|
||||||
|
++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
// The alpha mask can be zero (which means that the image is just RGB)
|
||||||
|
if (spec.alpha_mask) {
|
||||||
|
for (uint32_t y = 0; y < spec.height; y++) {
|
||||||
|
auto src = reinterpret_cast<const uint32_t *>(clipImg.data() + spec.bytes_per_row * y);
|
||||||
|
for (uint32_t x = 0; x < spec.width; x++, src++) {
|
||||||
|
auto c = *src;
|
||||||
|
*dst = image_make_bgra((c & spec.red_mask) >> spec.red_shift, (c & spec.green_mask) >> spec.green_shift,
|
||||||
|
(c & spec.blue_mask) >> spec.blue_shift, (c & spec.alpha_mask) >> spec.alpha_shift);
|
||||||
|
|
||||||
|
++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint32_t y = 0; y < spec.height; y++) {
|
||||||
|
auto src = reinterpret_cast<const uint32_t *>(clipImg.data() + spec.bytes_per_row * y);
|
||||||
|
for (uint32_t x = 0; x < spec.width; x++, src++) {
|
||||||
|
auto c = *src;
|
||||||
|
*dst = image_make_bgra((c & spec.red_mask) >> spec.red_shift, (c & spec.green_mask) >> spec.green_shift,
|
||||||
|
(c & spec.blue_mask) >> spec.blue_shift, 255u);
|
||||||
|
|
||||||
|
++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 24:
|
||||||
|
for (uint32_t y = 0; y < spec.height; y++) {
|
||||||
|
auto src = reinterpret_cast<const uint8_t *>(clipImg.data() + spec.bytes_per_row * y);
|
||||||
|
for (uint32_t x = 0; x < spec.width; x++, src += 3) {
|
||||||
|
auto c = *reinterpret_cast<const uint32_t *>(src);
|
||||||
|
*dst = image_make_bgra((c & spec.red_mask) >> spec.red_shift, (c & spec.green_mask) >> spec.green_shift,
|
||||||
|
(c & spec.blue_mask) >> spec.blue_shift, 255u);
|
||||||
|
|
||||||
|
++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
for (uint32_t y = 0; y < spec.height; y++) {
|
||||||
|
auto src = reinterpret_cast<const uint16_t *>(clipImg.data() + spec.bytes_per_row * y);
|
||||||
|
for (uint32_t x = 0; x < spec.width; x++, src++) {
|
||||||
|
auto c = *src;
|
||||||
|
*dst = image_make_bgra(image_scale_5bits_to_8bits((c & spec.red_mask) >> spec.red_shift),
|
||||||
|
image_scale_6bits_to_8bits((c & spec.green_mask) >> spec.green_shift),
|
||||||
|
image_scale_5bits_to_8bits((c & spec.blue_mask) >> spec.blue_shift), 255u);
|
||||||
|
|
||||||
|
++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub__dest(oldDest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qb64Img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the clipboard image using a QB64 image handle.
|
||||||
|
/// @param src A valid QB64 image handle.
|
||||||
|
void sub__clipboardimage(int32_t src) {
|
||||||
|
if (is_error_pending())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (src >= 0) {
|
||||||
|
validatepage(src);
|
||||||
|
src = page[src];
|
||||||
|
} else {
|
||||||
|
src = -src;
|
||||||
|
if (src >= nextimg) {
|
||||||
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!img[src].valid) {
|
||||||
|
error(QB_ERROR_INVALID_HANDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End of validation
|
||||||
|
|
||||||
|
// Even though we have color mask and shift support, clip needs the RGBA order :(
|
||||||
|
clip::image_spec spec;
|
||||||
|
spec.bits_per_pixel = 32;
|
||||||
|
spec.red_mask = 0x000000ff;
|
||||||
|
spec.green_mask = 0x0000ff00;
|
||||||
|
spec.blue_mask = 0x00ff0000;
|
||||||
|
spec.alpha_mask = 0xff000000;
|
||||||
|
spec.red_shift = 0;
|
||||||
|
spec.green_shift = 8;
|
||||||
|
spec.blue_shift = 16;
|
||||||
|
spec.alpha_shift = 24;
|
||||||
|
|
||||||
|
std::vector<uint32_t> pixels; // this will hold our converted BGRA32 pixel data
|
||||||
|
auto srcImg = img[src];
|
||||||
|
|
||||||
|
if (srcImg.text) {
|
||||||
|
IMAGE_DEBUG_PRINT("Rendering text surface to BGRA32");
|
||||||
|
|
||||||
|
uint32_t const fontWidth = 8;
|
||||||
|
uint32_t fontHeight = 16;
|
||||||
|
if (srcImg.font == 8 || srcImg.font == 14)
|
||||||
|
fontHeight = srcImg.font;
|
||||||
|
|
||||||
|
spec.width = fontWidth * srcImg.width;
|
||||||
|
spec.height = fontHeight * srcImg.height;
|
||||||
|
spec.bytes_per_row = spec.width * sizeof(uint32_t);
|
||||||
|
pixels.resize(spec.width * spec.height);
|
||||||
|
|
||||||
|
uint8_t fc, bc, *c = srcImg.offset; // set to the first codepoint
|
||||||
|
uint8_t const *builtinFont = nullptr;
|
||||||
|
|
||||||
|
// Render all text to the raw pixel array
|
||||||
|
for (uint32_t y = 0; y < spec.height; y += fontHeight) {
|
||||||
|
for (uint32_t x = 0; x < spec.width; x += fontWidth) {
|
||||||
|
switch (fontHeight) {
|
||||||
|
case 8:
|
||||||
|
builtinFont = &charset8x8[*c][0][0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
builtinFont = &charset8x16[*c][1][0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // 16
|
||||||
|
builtinFont = &charset8x16[*c][0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
++c; // move to the attribute
|
||||||
|
fc = *c & 0x0F;
|
||||||
|
bc = ((*c >> 4) & 7) + ((*c >> 7) << 3);
|
||||||
|
|
||||||
|
// Inner codepoint rendering loop
|
||||||
|
for (uint32_t dy = y, py = 0; py < fontHeight; dy++, py++) {
|
||||||
|
for (uint32_t dx = x, px = 0; px < fontWidth; dx++, px++) {
|
||||||
|
pixels[spec.width * dy + dx] = image_swap_red_blue(*builtinFont ? srcImg.pal[fc] : srcImg.pal[bc]);
|
||||||
|
++builtinFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++c; // move to the next codepoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spec.width = srcImg.width;
|
||||||
|
spec.height = srcImg.height;
|
||||||
|
spec.bytes_per_row = spec.width * sizeof(uint32_t);
|
||||||
|
pixels.resize(spec.width * spec.height);
|
||||||
|
|
||||||
|
if (srcImg.bits_per_pixel == 32) {
|
||||||
|
// BGRA32 pixels
|
||||||
|
IMAGE_DEBUG_PRINT("Converting BGRA32 image to RGBA32");
|
||||||
|
|
||||||
|
auto p = srcImg.offset32;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pixels.size(); i++) {
|
||||||
|
pixels[i] = image_swap_red_blue(*p);
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Indexed pixels
|
||||||
|
IMAGE_DEBUG_PRINT("Converting BGRA32 indexed image to RGBA32");
|
||||||
|
|
||||||
|
auto p = srcImg.offset;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pixels.size(); i++) {
|
||||||
|
pixels[i] = image_swap_red_blue(srcImg.pal[*p]);
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_DEBUG_PRINT("Setting clipboard image");
|
||||||
|
|
||||||
|
// Send the image off to the OS clipboard
|
||||||
|
clip::image clipImg(pixels.data(), spec);
|
||||||
|
clip::set_image(clipImg);
|
||||||
|
}
|
|
@ -1013,21 +1013,21 @@ bool FontRenderTextASCII(int32_t fh, const uint8_t *codepoint, int32_t codepoint
|
||||||
/// @param text The message to build the MD5 hash of
|
/// @param text The message to build the MD5 hash of
|
||||||
/// @return The generated MD5 hash as hexadecimal string
|
/// @return The generated MD5 hash as hexadecimal string
|
||||||
qbs *func__md5(qbs *text) {
|
qbs *func__md5(qbs *text) {
|
||||||
{
|
MD5_CTX ctx;
|
||||||
MD5_CTX ctx;
|
unsigned char md5[16];
|
||||||
unsigned char md5[16];
|
qbs *res;
|
||||||
qbs *res;
|
int i;
|
||||||
int i;
|
|
||||||
|
|
||||||
MD5_Init(&ctx);
|
MD5_Init(&ctx);
|
||||||
if (text->len) MD5_Update(&ctx, text->chr, text->len);
|
if (text->len)
|
||||||
MD5_Final(md5,&ctx);
|
MD5_Update(&ctx, text->chr, text->len);
|
||||||
|
MD5_Final(md5, &ctx);
|
||||||
|
|
||||||
res = qbs_new(32, 1);
|
res = qbs_new(32, 1);
|
||||||
for (i = 0; i < 16; i++) sprintf((char*)&res->chr[i*2], "%02X", md5[i]);
|
for (i = 0; i < 16; i++)
|
||||||
|
sprintf((char *)&res->chr[i * 2], "%02X", md5[i]);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Return the true font height in pixel
|
/// @brief Return the true font height in pixel
|
||||||
|
@ -1425,21 +1425,21 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint32_t a = IMAGE_GET_BGRA_ALPHA(write_page->color) + 1;
|
uint32_t a = image_get_bgra_alpha(write_page->color) + 1;
|
||||||
uint32_t a2 = IMAGE_GET_BGRA_ALPHA(write_page->background_color) + 1;
|
uint32_t a2 = image_get_bgra_alpha(write_page->background_color) + 1;
|
||||||
uint32_t z = IMAGE_GET_BGRA_BGR(write_page->color);
|
uint32_t z = image_get_bgra_bgr(write_page->color);
|
||||||
uint32_t z2 = IMAGE_GET_BGRA_BGR(write_page->background_color);
|
uint32_t z2 = image_get_bgra_bgr(write_page->background_color);
|
||||||
|
|
||||||
switch (write_page->print_mode) {
|
switch (write_page->print_mode) {
|
||||||
case 3: {
|
case 3: {
|
||||||
float alpha1 = IMAGE_GET_BGRA_ALPHA(write_page->color);
|
float alpha1 = image_get_bgra_alpha(write_page->color);
|
||||||
float r1 = IMAGE_GET_BGRA_RED(write_page->color);
|
float r1 = image_get_bgra_red(write_page->color);
|
||||||
float g1 = IMAGE_GET_BGRA_GREEN(write_page->color);
|
float g1 = image_get_bgra_green(write_page->color);
|
||||||
float b1 = IMAGE_GET_BGRA_BLUE(write_page->color);
|
float b1 = image_get_bgra_blue(write_page->color);
|
||||||
float alpha2 = IMAGE_GET_BGRA_ALPHA(write_page->background_color);
|
float alpha2 = image_get_bgra_alpha(write_page->background_color);
|
||||||
float r2 = IMAGE_GET_BGRA_RED(write_page->background_color);
|
float r2 = image_get_bgra_red(write_page->background_color);
|
||||||
float g2 = IMAGE_GET_BGRA_GREEN(write_page->background_color);
|
float g2 = image_get_bgra_green(write_page->background_color);
|
||||||
float b2 = IMAGE_GET_BGRA_BLUE(write_page->background_color);
|
float b2 = image_get_bgra_blue(write_page->background_color);
|
||||||
float dr = r2 - r1;
|
float dr = r2 - r1;
|
||||||
float dg = g2 - g1;
|
float dg = g2 - g1;
|
||||||
float db = b2 - b1;
|
float db = b2 - b1;
|
||||||
|
@ -1460,7 +1460,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
|
||||||
float g3 = g1 + dg * d;
|
float g3 = g1 + dg * d;
|
||||||
float b3 = b1 + db * d;
|
float b3 = b1 + db * d;
|
||||||
pset_and_clip(start_x + pen.x, start_y + pen.y,
|
pset_and_clip(start_x + pen.x, start_y + pen.y,
|
||||||
IMAGE_MAKE_BGRA(qbr_float_to_long(r3), qbr_float_to_long(g3), qbr_float_to_long(b3), qbr_float_to_long(alpha3)));
|
image_make_bgra(qbr_float_to_long(r3), qbr_float_to_long(g3), qbr_float_to_long(b3), qbr_float_to_long(alpha3)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
// ___ ___ __ _ _ ___ ___ ___ _ _ _
|
// ___ ___ __ _ _ ___ ___ ___ _ _ _
|
||||||
// / _ \| _ ) / /| | || _ \ __| |_ _|_ __ __ _ __ _ ___ | | (_) |__ _ _ __ _ _ _ _ _
|
// / _ \| _ ) / /| | || _ \ __| |_ _|_ __ __ _ __ _ ___ | | (_) |__ _ _ __ _ _ _ _ _
|
||||||
// | (_) | _ \/ _ \_ _| _/ _| | || ' \/ _` / _` / -_) | |__| | '_ \ '_/ _` | '_| || |
|
// | (_) | _ \/ _ \_ _| _/ _| | || ' \/ _` / _` / -_) | |__| | '_ \ '_/ _` | '_| || |
|
||||||
|
@ -46,9 +46,6 @@
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb/stb_image_write.h"
|
#include "stb/stb_image_write.h"
|
||||||
|
|
||||||
// This is returned to the caller if something goes wrong while loading the image
|
|
||||||
#define INVALID_IMAGE_HANDLE -1
|
|
||||||
|
|
||||||
#ifdef QB64_WINDOWS
|
#ifdef QB64_WINDOWS
|
||||||
# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_))
|
# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_))
|
||||||
#else
|
#else
|
||||||
|
@ -372,11 +369,6 @@ static uint32_t *image_decode_from_memory(const uint8_t *data, size_t size, int3
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Clamps a color channel to the range 0 - 255
|
|
||||||
/// @param n The color component
|
|
||||||
/// @return The clamped value
|
|
||||||
static inline uint8_t image_clamp_component(int32_t n) { return n < 0 ? 0 : n > 255 ? 255 : n; }
|
|
||||||
|
|
||||||
/// @brief This takes in a 32bpp (BGRA) image raw data and spits out an 8bpp raw image along with it's 256 color (BGRA) palette.
|
/// @brief This takes in a 32bpp (BGRA) image raw data and spits out an 8bpp raw image along with it's 256 color (BGRA) palette.
|
||||||
/// @param src32 The source raw image data. This must be in BGRA format and not NULL
|
/// @param src32 The source raw image data. This must be in BGRA format and not NULL
|
||||||
/// @param w The width of the image in pixels
|
/// @param w The width of the image in pixels
|
||||||
|
@ -408,9 +400,9 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h,
|
||||||
for (auto y = 0; y < h; y++) {
|
for (auto y = 0; y < h; y++) {
|
||||||
for (auto x = 0; x < w; x++) {
|
for (auto x = 0; x < w; x++) {
|
||||||
int32_t t = bayerMatrix[((y & 3) << 2) + (x & 3)];
|
int32_t t = bayerMatrix[((y & 3) << 2) + (x & 3)];
|
||||||
int32_t b = image_clamp_component((*src++) + (t << 1));
|
int32_t b = image_clamp_color_component((*src++) + (t << 1));
|
||||||
int32_t g = image_clamp_component((*src++) + (t << 1));
|
int32_t g = image_clamp_color_component((*src++) + (t << 1));
|
||||||
int32_t r = image_clamp_component((*src++) + (t << 1));
|
int32_t r = image_clamp_color_component((*src++) + (t << 1));
|
||||||
++src; // Ignore alpha
|
++src; // Ignore alpha
|
||||||
|
|
||||||
// Quantize
|
// Quantize
|
||||||
|
@ -428,9 +420,9 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h,
|
||||||
// Generate a uniform CLUT based on the quantized colors
|
// Generate a uniform CLUT based on the quantized colors
|
||||||
for (auto i = 0; i < 256; i++) {
|
for (auto i = 0; i < 256; i++) {
|
||||||
if (cubes[i].count) {
|
if (cubes[i].count) {
|
||||||
paletteOut[i] = IMAGE_MAKE_BGRA(cubes[i].r / cubes[i].count, cubes[i].g / cubes[i].count, cubes[i].b / cubes[i].count, 0xFF);
|
paletteOut[i] = image_make_bgra(cubes[i].r / cubes[i].count, cubes[i].g / cubes[i].count, cubes[i].b / cubes[i].count, 0xFF);
|
||||||
} else {
|
} else {
|
||||||
paletteOut[i] = IMAGE_MAKE_BGRA(0, 0, 0, 0xFF);
|
paletteOut[i] = image_make_bgra(0, 0, 0, 0xFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,19 +476,6 @@ static uint8_t *image_extract_8bpp(const uint32_t *src, int32_t w, int32_t h, ui
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Calculates the distance between 2 RBG points in the RGB color cube
|
|
||||||
/// @param r1 R1
|
|
||||||
/// @param g1 G1
|
|
||||||
/// @param b1 B1
|
|
||||||
/// @param r2 R2
|
|
||||||
/// @param g2 G2
|
|
||||||
/// @param b2 B2
|
|
||||||
/// @return The distance in floating point
|
|
||||||
static inline float image_calculate_rgb_distance(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2) {
|
|
||||||
return sqrt(((float(r2) - float(r1)) * (float(r2) - float(r1))) + ((float(g2) - float(g1)) * (float(g2) - float(g1))) +
|
|
||||||
((float(b2) - float(b1)) * (float(b2) - float(b1))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief This modifies an *8bpp* image 'src' to use 'dst_pal' instead of 'src_pal'
|
/// @brief This modifies an *8bpp* image 'src' to use 'dst_pal' instead of 'src_pal'
|
||||||
/// @param src A pointer to the 8bpp image pixel data. This modifies data 'src' points to and cannot be NULL
|
/// @param src A pointer to the 8bpp image pixel data. This modifies data 'src' points to and cannot be NULL
|
||||||
/// @param w The width of the image in pixels
|
/// @param w The width of the image in pixels
|
||||||
|
@ -515,8 +494,8 @@ static void image_remap_palette(uint8_t *src, int32_t w, int32_t h, const uint32
|
||||||
auto oldDist = maxRGBDist;
|
auto oldDist = maxRGBDist;
|
||||||
|
|
||||||
for (auto y = 0; y < 256; y++) {
|
for (auto y = 0; y < 256; y++) {
|
||||||
auto newDist = image_calculate_rgb_distance(IMAGE_GET_BGRA_RED(src_pal[x]), IMAGE_GET_BGRA_GREEN(src_pal[x]), IMAGE_GET_BGRA_BLUE(src_pal[x]),
|
auto newDist = image_calculate_rgb_distance(image_get_bgra_red(src_pal[x]), image_get_bgra_green(src_pal[x]), image_get_bgra_blue(src_pal[x]),
|
||||||
IMAGE_GET_BGRA_RED(dst_pal[y]), IMAGE_GET_BGRA_GREEN(dst_pal[y]), IMAGE_GET_BGRA_BLUE(dst_pal[y]));
|
image_get_bgra_red(dst_pal[y]), image_get_bgra_green(dst_pal[y]), image_get_bgra_blue(dst_pal[y]));
|
||||||
|
|
||||||
if (oldDist > newDist) {
|
if (oldDist > newDist) {
|
||||||
oldDist = newDist;
|
oldDist = newDist;
|
||||||
|
@ -531,11 +510,6 @@ static void image_remap_palette(uint8_t *src, int32_t w, int32_t h, const uint32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Helps convert a BGRA color to an RGBA color and back
|
|
||||||
/// @param clr A BGRA color or an RGBA color
|
|
||||||
/// @return An RGBA color or a BGRA color
|
|
||||||
static inline uint32_t image_swap_red_blue(uint32_t clr) { return ((clr & 0xFF00FF00u) | ((clr & 0x00FF0000u) >> 16) | ((clr & 0x000000FFu) << 16)); }
|
|
||||||
|
|
||||||
/// @brief This function loads an image into memory and returns valid LONG image handle values that are less than -1
|
/// @brief This function loads an image into memory and returns valid LONG image handle values that are less than -1
|
||||||
/// @param qbsFileName The filename or memory buffer (see requirements below) of the image
|
/// @param qbsFileName The filename or memory buffer (see requirements below) of the image
|
||||||
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware accelerated), 256=8bpp or 257=8bpp (without palette remap)
|
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware accelerated), 256=8bpp or 257=8bpp (without palette remap)
|
||||||
|
@ -866,7 +840,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
|
||||||
// Inner codepoint rendering loop
|
// Inner codepoint rendering loop
|
||||||
for (auto dy = y, py = 0; py < fontHeight; dy++, py++) {
|
for (auto dy = y, py = 0; py < fontHeight; dy++, py++) {
|
||||||
for (auto dx = x, px = 0; px < fontWidth; dx++, px++) {
|
for (auto dx = x, px = 0; px < fontWidth; dx++, px++) {
|
||||||
pixels[width * dy + dx] = image_swap_red_blue(*builtinFont ? palette_256[fc] : palette_256[bc]);
|
pixels[width * dy + dx] = image_swap_red_blue(*builtinFont ? img[imageHandle].pal[fc] : img[imageHandle].pal[bc]);
|
||||||
++builtinFont;
|
++builtinFont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "bitops.h"
|
#include "bitops.h"
|
||||||
|
#include "clipboard.h"
|
||||||
|
#include "command.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "command.h"
|
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "environ.h"
|
#include "environ.h"
|
||||||
|
#include "error_handle.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "extended_math.h"
|
#include "extended_math.h"
|
||||||
#include "filepath.h"
|
|
||||||
#include "file-fields.h"
|
#include "file-fields.h"
|
||||||
|
#include "filepath.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "hexoctbin.h"
|
#include "hexoctbin.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include "qbmath.h"
|
|
||||||
#include "qbs.h"
|
|
||||||
#include "qbs-mk-cv.h"
|
|
||||||
#include "shell.h"
|
|
||||||
#include "error_handle.h"
|
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "qbmath.h"
|
||||||
|
#include "qbs-mk-cv.h"
|
||||||
|
#include "qbs.h"
|
||||||
#include "rounding.h"
|
#include "rounding.h"
|
||||||
|
#include "shell.h"
|
||||||
|
|
||||||
extern int32 func__cinp(int32 toggle,
|
extern int32 func__cinp(int32 toggle,
|
||||||
int32 passed); // Console INP scan code reader
|
int32 passed); // Console INP scan code reader
|
||||||
|
@ -194,10 +195,6 @@ extern void sub__fullscreen(int32 method, int32 passed);
|
||||||
extern void sub__allowfullscreen(int32 method, int32 smooth);
|
extern void sub__allowfullscreen(int32 method, int32 smooth);
|
||||||
extern int32 func__fullscreen();
|
extern int32 func__fullscreen();
|
||||||
extern int32 func__fullscreensmooth();
|
extern int32 func__fullscreensmooth();
|
||||||
extern void sub__clipboard(qbs *);
|
|
||||||
extern qbs *func__clipboard();
|
|
||||||
extern int32 func__clipboardimage();
|
|
||||||
extern void sub__clipboardimage(int32 src);
|
|
||||||
extern int32 func__exit();
|
extern int32 func__exit();
|
||||||
extern void revert_input_check();
|
extern void revert_input_check();
|
||||||
extern int32 func__openhost(qbs *);
|
extern int32 func__openhost(qbs *);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
g++ -no-pie -std=gnu++17 -fno-strict-aliasing -Wno-conversion-null -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/freeglut/include -I./internal/c/parts/core/glew/include -DDEPENDENCY_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_ICON -DDEPENDENCY_NO_SCREENIMAGE internal/c/qbx.cpp -c -o internal/c/qbx.o
|
g++ -no-pie -std=gnu++17 -fno-strict-aliasing -Wno-conversion-null -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/freeglut/include -I./internal/c/parts/core/glew/include -DDEPENDENCY_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_ICON -DDEPENDENCY_NO_SCREENIMAGE internal/c/qbx.cpp -c -o internal/c/qbx.o
|
||||||
g++ -no-pie -std=gnu++17 -fno-strict-aliasing -Wno-conversion-null -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/freeglut/include -I./internal/c/parts/core/glew/include -DDEPENDENCY_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_ICON -DDEPENDENCY_NO_SCREENIMAGE ./internal/c/libqb_make_000101000.o ./internal/c/qbx.o -o "/home/runner/work/QB64pe/QB64pe/qb64pe" ./internal/c/libqb/src/threading.o ./internal/c/libqb/src/buffer.o ./internal/c/libqb/src/bitops.o ./internal/c/libqb/src/command.o ./internal/c/libqb/src/environ.o ./internal/c/libqb/src/file-fields.o ./internal/c/libqb/src/filepath.o ./internal/c/libqb/src/filesystem.o ./internal/c/libqb/src/datetime.o ./internal/c/libqb/src/error_handle.o ./internal/c/libqb/src/gfs.o ./internal/c/libqb/src/qblist.o ./internal/c/libqb/src/hexoctbin.o ./internal/c/libqb/src/mem.o ./internal/c/libqb/src/math.o ./internal/c/libqb/src/rounding.o ./internal/c/libqb/src/shell.o ./internal/c/libqb/src/qbs.o ./internal/c/libqb/src/qbs_str.o ./internal/c/libqb/src/qbs_cmem.o ./internal/c/libqb/src/qbs_mk_cv.o ./internal/c/libqb/src/string_functions.o ./internal/c/libqb/src/http.o ./internal/c/libqb/src/threading-posix.o ./internal/c/libqb/src/glut-main-thread.o ./internal/c/libqb/src/glut-message.o ./internal/c/libqb/src/glut-msg-queue.o ./internal/c/parts/gui/tinyfiledialogs.o ./internal/c/parts/gui/gui.o ./internal/c/parts/video/font/font.o ./internal/c/parts/video/font/freetype/freetype.a ./internal/c/parts/audio/stub_audio.o ./internal/c/parts/compression/miniz.o ./internal/c/parts/compression/compression.o ./internal/c/parts/core/freeglut.a ./internal/c/parts/core/glew/glew.o -lGL -lGLU -lX11 -lpthread -ldl -lrt -lcurl
|
g++ -no-pie -std=gnu++17 -fno-strict-aliasing -Wno-conversion-null -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/freeglut/include -I./internal/c/parts/core/glew/include -DDEPENDENCY_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_ICON -DDEPENDENCY_NO_SCREENIMAGE ./internal/c/libqb_make_000101000.o ./internal/c/qbx.o -o "/home/runner/work/QB64pe/QB64pe/qb64pe" ./internal/c/libqb/src/threading.o ./internal/c/libqb/src/buffer.o ./internal/c/libqb/src/bitops.o ./internal/c/libqb/src/command.o ./internal/c/libqb/src/environ.o ./internal/c/libqb/src/file-fields.o ./internal/c/libqb/src/filepath.o ./internal/c/libqb/src/filesystem.o ./internal/c/libqb/src/datetime.o ./internal/c/libqb/src/error_handle.o ./internal/c/libqb/src/gfs.o ./internal/c/libqb/src/qblist.o ./internal/c/libqb/src/hexoctbin.o ./internal/c/libqb/src/mem.o ./internal/c/libqb/src/math.o ./internal/c/libqb/src/rounding.o ./internal/c/libqb/src/shell.o ./internal/c/libqb/src/qbs.o ./internal/c/libqb/src/qbs_str.o ./internal/c/libqb/src/qbs_cmem.o ./internal/c/libqb/src/qbs_mk_cv.o ./internal/c/libqb/src/string_functions.o ./internal/c/libqb/src/http.o ./internal/c/libqb/src/threading-posix.o ./internal/c/libqb/src/glut-main-thread.o ./internal/c/libqb/src/glut-message.o ./internal/c/libqb/src/glut-msg-queue.o ./internal/c/parts/gui/tinyfiledialogs.o ./internal/c/parts/gui/gui.o ./internal/c/parts/os/clipboard/clipboard.a ./internal/c/parts/video/font/font.o ./internal/c/parts/video/font/freetype/freetype.a ./internal/c/parts/audio/stub_audio.o ./internal/c/parts/compression/miniz.o ./internal/c/parts/compression/compression.o ./internal/c/parts/core/freeglut.a ./internal/c/parts/core/glew/glew.o -lGL -lGLU -lX11 -lpthread -ldl -lrt -lxcb -lpng -lcurl
|
||||||
objcopy --only-keep-debug "/home/runner/work/QB64pe/QB64pe/qb64pe" "./internal/temp/qb64pe.sym"
|
objcopy --only-keep-debug "/home/runner/work/QB64pe/QB64pe/qb64pe" "./internal/temp/qb64pe.sym"
|
||||||
objcopy --strip-unneeded "/home/runner/work/QB64pe/QB64pe/qb64pe"
|
objcopy --strip-unneeded "/home/runner/work/QB64pe/QB64pe/qb64pe"
|
||||||
|
|
92458
internal/source/main.txt
92458
internal/source/main.txt
File diff suppressed because it is too large
Load diff
|
@ -122,3 +122,11 @@ This is used by libqb to show alerts and also by the common dialog functions and
|
||||||
| Library | License | License file | Location |
|
| Library | License | License file | Location |
|
||||||
| :------ | :-----: | :----------- | :------- |
|
| :------ | :-----: | :----------- | :------- |
|
||||||
| tiny file dialogs | ZLIB | license_tinyfiledialogs.txt | internal/c/parts/gui/ |
|
| tiny file dialogs | ZLIB | license_tinyfiledialogs.txt | internal/c/parts/gui/ |
|
||||||
|
|
||||||
|
## Clipboard Image Support
|
||||||
|
|
||||||
|
This is used if you make use of the `_CLIPBOARDIMAGE` function or statement.
|
||||||
|
|
||||||
|
| Library | License | License file | Location |
|
||||||
|
| :------ | :-----: | :----------- | :------- |
|
||||||
|
| Clip Library | MIT | license_clip.txt | internal/c/parts/os/clipboard/clip/ |
|
||||||
|
|
23
licenses/license_clip.txt
Normal file
23
licenses/license_clip.txt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
License of Clip Library:
|
||||||
|
|
||||||
|
Copyright (c) 2015-2024 David Capello
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -81,19 +81,19 @@ if [ "$DISTRO" == "arch" ]; then
|
||||||
pkg_install
|
pkg_install
|
||||||
elif [ "$DISTRO" == "linuxmint" ] || [ "$DISTRO" == "ubuntu" ] || [ "$DISTRO" == "debian" ] || [ "$DISTRO" == "zorin" ]; then
|
elif [ "$DISTRO" == "linuxmint" ] || [ "$DISTRO" == "ubuntu" ] || [ "$DISTRO" == "debian" ] || [ "$DISTRO" == "zorin" ]; then
|
||||||
echo "Debian based distro detected."
|
echo "Debian based distro detected."
|
||||||
pkg_list="build-essential x11-utils mesa-common-dev libglu1-mesa-dev libasound2-dev zlib1g-dev libcurl4-openssl-dev $GET_WGET"
|
pkg_list="build-essential x11-utils mesa-common-dev libglu1-mesa-dev libasound2-dev libpng-dev libcurl4-openssl-dev $GET_WGET"
|
||||||
installed_packages=`dpkg -l`
|
installed_packages=`dpkg -l`
|
||||||
installer_command="sudo apt-get -y install "
|
installer_command="sudo apt-get -y install "
|
||||||
pkg_install
|
pkg_install
|
||||||
elif [ "$DISTRO" == "fedora" ] || [ "$DISTRO" == "redhat" ] || [ "$DISTRO" == "centos" ]; then
|
elif [ "$DISTRO" == "fedora" ] || [ "$DISTRO" == "redhat" ] || [ "$DISTRO" == "centos" ]; then
|
||||||
echo "Fedora/Redhat based distro detected."
|
echo "Fedora/Redhat based distro detected."
|
||||||
pkg_list="gcc-c++ make mesa-libGLU-devel alsa-lib-devel zlib-devel libcurl-devel $GET_WGET"
|
pkg_list="gcc-c++ make mesa-libGLU-devel alsa-lib-devel libpng-devel libcurl-devel $GET_WGET"
|
||||||
installed_packages=`yum list installed`
|
installed_packages=`yum list installed`
|
||||||
installer_command="sudo yum install "
|
installer_command="sudo yum install "
|
||||||
pkg_install
|
pkg_install
|
||||||
elif [ "$DISTRO" == "voidlinux" ]; then
|
elif [ "$DISTRO" == "voidlinux" ]; then
|
||||||
echo "VoidLinux detected."
|
echo "VoidLinux detected."
|
||||||
pkg_list="gcc make glu-devel zlib-devel alsa-lib-devel libcurl-devel $GET_WGET"
|
pkg_list="gcc make glu-devel libpng-devel alsa-lib-devel libcurl-devel $GET_WGET"
|
||||||
installed_packages=`xbps-query -l |grep -v libgcc`
|
installed_packages=`xbps-query -l |grep -v libgcc`
|
||||||
installer_command="sudo xbps-install -Sy "
|
installer_command="sudo xbps-install -Sy "
|
||||||
pkg_install
|
pkg_install
|
||||||
|
@ -104,7 +104,7 @@ elif [ -z "$DISTRO" ]; then
|
||||||
echo " OpenGL development libraries"
|
echo " OpenGL development libraries"
|
||||||
echo " ALSA development libraries"
|
echo " ALSA development libraries"
|
||||||
echo " GNU C++ Compiler (g++)"
|
echo " GNU C++ Compiler (g++)"
|
||||||
echo " zlib"
|
echo " libpng"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Compiling and installing QB64-PE..."
|
echo "Compiling and installing QB64-PE..."
|
||||||
|
|
|
@ -3463,8 +3463,9 @@ FUNCTION ide2 (ignore)
|
||||||
IF KCONTROL AND UCASE$(K$) = "S" THEN 'File -> #Save
|
IF KCONTROL AND UCASE$(K$) = "S" THEN 'File -> #Save
|
||||||
IF ideprogname = "" THEN
|
IF ideprogname = "" THEN
|
||||||
ProposedTitle$ = FindProposedTitle$
|
ProposedTitle$ = FindProposedTitle$
|
||||||
IF ProposedTitle$ = "" THEN
|
IF ProposedTitle$ = "" THEN ProposedTitle$ = "untitled" + tempfolderindexstr$
|
||||||
a$ = idefiledialog$("untitled" + tempfolderindexstr$ + ".bas", 2)
|
IF UseGuiDialogs THEN
|
||||||
|
a$ = Savefile$(ProposedTitle$ + ".bas")
|
||||||
ELSE
|
ELSE
|
||||||
a$ = idefiledialog$(ProposedTitle$ + ".bas", 2)
|
a$ = idefiledialog$(ProposedTitle$ + ".bas", 2)
|
||||||
END IF
|
END IF
|
||||||
|
|
|
@ -12582,7 +12582,6 @@ localpath$ = "internal\c\"
|
||||||
|
|
||||||
|
|
||||||
IF DEPENDENCY(DEPENDENCY_GL) THEN makedeps$ = makedeps$ + " DEP_GL=y"
|
IF DEPENDENCY(DEPENDENCY_GL) THEN makedeps$ = makedeps$ + " DEP_GL=y"
|
||||||
IF DEPENDENCY(DEPENDENCY_SCREENIMAGE) THEN makedeps$ = makedeps$ + " DEP_SCREENIMAGE=y"
|
|
||||||
IF DEPENDENCY(DEPENDENCY_IMAGE_CODEC) THEN makedeps$ = makedeps$ + " DEP_IMAGE_CODEC=y"
|
IF DEPENDENCY(DEPENDENCY_IMAGE_CODEC) THEN makedeps$ = makedeps$ + " DEP_IMAGE_CODEC=y"
|
||||||
IF DEPENDENCY(DEPENDENCY_CONSOLE_ONLY) THEN makedeps$ = makedeps$ + " DEP_CONSOLE_ONLY=y"
|
IF DEPENDENCY(DEPENDENCY_CONSOLE_ONLY) THEN makedeps$ = makedeps$ + " DEP_CONSOLE_ONLY=y"
|
||||||
IF DEPENDENCY(DEPENDENCY_SOCKETS) THEN makedeps$ = makedeps$ + " DEP_SOCKETS=y"
|
IF DEPENDENCY(DEPENDENCY_SOCKETS) THEN makedeps$ = makedeps$ + " DEP_SOCKETS=y"
|
||||||
|
|
24
tests/compile_tests/clipboard/clipboard_test.bas
Normal file
24
tests/compile_tests/clipboard/clipboard_test.bas
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
$CONSOLE:ONLY
|
||||||
|
|
||||||
|
OPTION _EXPLICIT
|
||||||
|
|
||||||
|
CONST TEXT_HELLO = "Hello"
|
||||||
|
CONST TEXT_WORLD = "world"
|
||||||
|
CONST TEXT_ASCII = CHR$(176) + CHR$(177) + CHR$(178) + " " + TEXT_HELLO + " " + TEXT_WORLD + " " + CHR$(178) + CHR$(177) + CHR$(176)
|
||||||
|
|
||||||
|
|
||||||
|
_CLIPBOARD$ = TEXT_HELLO
|
||||||
|
_CLIPBOARD$ = TEXT_WORLD
|
||||||
|
|
||||||
|
PRINT "Set: "; TEXT_WORLD
|
||||||
|
PRINT "Got: "; _CLIPBOARD$
|
||||||
|
|
||||||
|
_CLIPBOARD$ = TEXT_ASCII
|
||||||
|
|
||||||
|
IF TEXT_ASCII = _CLIPBOARD$ THEN
|
||||||
|
PRINT "Test passed"
|
||||||
|
ELSE
|
||||||
|
PRINT "Test failed"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
SYSTEM
|
3
tests/compile_tests/clipboard/clipboard_test.output
Normal file
3
tests/compile_tests/clipboard/clipboard_test.output
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Set: world
|
||||||
|
Got: world
|
||||||
|
Test passed
|
Loading…
Reference in a new issue