mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
Add system clipboard support to Windows
This commit is contained in:
@@ -109,7 +109,8 @@ namespace PortabilityLayer
|
||||
uint8_t *bytes = static_cast<uint8_t*>(buf);
|
||||
const MMBlock *mmBlock = reinterpret_cast<const MMBlock*>(bytes - MMBlock::AlignedSize());
|
||||
|
||||
free(bytes - MMBlock::AlignedSize() - mmBlock->m_offsetFromAllocLocation);
|
||||
void *freeLoc = bytes - MMBlock::AlignedSize() - mmBlock->m_offsetFromAllocLocation;
|
||||
free(freeLoc);
|
||||
}
|
||||
|
||||
MMHandleBlock *MemoryManagerImpl::AllocHandle(size_t size)
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "IGpDirectoryCursor.h"
|
||||
#include "HostSuspendCallArgument.h"
|
||||
#include "HostSuspendHook.h"
|
||||
#include "IGpClipboardContents.h"
|
||||
#include "IGpCursor.h"
|
||||
#include "IGpDisplayDriver.h"
|
||||
#include "IGpFileSystem.h"
|
||||
@@ -45,6 +46,8 @@
|
||||
#include "PLTimeTaggedVOSEvent.h"
|
||||
#include "PLWidgets.h"
|
||||
|
||||
#include "UTF8.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -688,6 +691,141 @@ WindowPtr PL_GetPutInFrontWindowPtr()
|
||||
return PortabilityLayer::WindowManager::GetInstance()->GetPutInFrontSentinel();
|
||||
}
|
||||
|
||||
class PLClipboardContentsText : public IGpClipboardContentsText
|
||||
{
|
||||
public:
|
||||
static PLClipboardContentsText *CreateFromMacRomanStr(const uint8_t *chars, size_t size);
|
||||
|
||||
GpClipboardContentsType_t GetContentsType() const override;
|
||||
void Destroy() override;
|
||||
IGpClipboardContents *Clone() const override;
|
||||
const uint8_t *GetBytes() const override;
|
||||
size_t GetSize() const override;
|
||||
|
||||
private:
|
||||
PLClipboardContentsText(uint8_t *utf8Bytes, size_t size);
|
||||
~PLClipboardContentsText();
|
||||
|
||||
uint8_t *m_utf8Bytes;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
|
||||
PLClipboardContentsText *PLClipboardContentsText::CreateFromMacRomanStr(const uint8_t *chars, size_t length)
|
||||
{
|
||||
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
||||
|
||||
size_t numUTF8Bytes = 0;
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
uint8_t utf8Bytes[PortabilityLayer::UTF8Processor::kMaxEncodedBytes];
|
||||
|
||||
uint16_t codePoint = MacRoman::ToUnicode(chars[i]);
|
||||
|
||||
size_t numBytesEmitted = 0;
|
||||
PortabilityLayer::UTF8Processor::EncodeCodePoint(utf8Bytes, numBytesEmitted, codePoint);
|
||||
|
||||
numUTF8Bytes += numBytesEmitted;
|
||||
}
|
||||
|
||||
uint8_t *utf8Bytes = nullptr;
|
||||
|
||||
if (numUTF8Bytes)
|
||||
{
|
||||
utf8Bytes = static_cast<uint8_t*>(mm->Alloc(numUTF8Bytes));
|
||||
if (!utf8Bytes)
|
||||
return nullptr;
|
||||
|
||||
numUTF8Bytes = 0;
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
uint16_t codePoint = MacRoman::ToUnicode(chars[i]);
|
||||
|
||||
size_t numBytesEmitted = 0;
|
||||
PortabilityLayer::UTF8Processor::EncodeCodePoint(utf8Bytes + numUTF8Bytes, numBytesEmitted, codePoint);
|
||||
|
||||
numUTF8Bytes += numBytesEmitted;
|
||||
}
|
||||
}
|
||||
|
||||
void *storage = mm->Alloc(sizeof(PLClipboardContentsText));
|
||||
if (!storage)
|
||||
{
|
||||
mm->Release(utf8Bytes);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (storage) PLClipboardContentsText(utf8Bytes, numUTF8Bytes);
|
||||
}
|
||||
|
||||
PLClipboardContentsText::PLClipboardContentsText(uint8_t *utf8Bytes, size_t size)
|
||||
: m_utf8Bytes(utf8Bytes)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
PLClipboardContentsText::~PLClipboardContentsText()
|
||||
{
|
||||
PortabilityLayer::MemoryManager::GetInstance()->Release(m_utf8Bytes);
|
||||
}
|
||||
|
||||
GpClipboardContentsType_t PLClipboardContentsText::GetContentsType() const
|
||||
{
|
||||
return GpClipboardContentsTypes::kText;
|
||||
}
|
||||
|
||||
void PLClipboardContentsText::Destroy()
|
||||
{
|
||||
this->~PLClipboardContentsText();
|
||||
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
|
||||
}
|
||||
|
||||
IGpClipboardContents *PLClipboardContentsText::Clone() const
|
||||
{
|
||||
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
||||
uint8_t *bytesCopy = nullptr;
|
||||
if (m_size)
|
||||
{
|
||||
bytesCopy = static_cast<uint8_t*>(mm->Alloc(m_size));
|
||||
if (!bytesCopy)
|
||||
return nullptr;
|
||||
|
||||
memcpy(bytesCopy, m_utf8Bytes, m_size);
|
||||
}
|
||||
|
||||
void *storage = mm->Alloc(sizeof(PLClipboardContentsText));
|
||||
if (!storage)
|
||||
{
|
||||
if (bytesCopy)
|
||||
mm->Release(bytesCopy);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (storage) PLClipboardContentsText(bytesCopy, m_size);
|
||||
}
|
||||
|
||||
const uint8_t *PLClipboardContentsText::GetBytes() const
|
||||
{
|
||||
return m_utf8Bytes;
|
||||
}
|
||||
|
||||
size_t PLClipboardContentsText::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
|
||||
void PL_CopyStringToClipboard(const uint8_t *chars, size_t length)
|
||||
{
|
||||
if (IGpClipboardContentsText *clipboardText = PLClipboardContentsText::CreateFromMacRomanStr(chars, length))
|
||||
{
|
||||
PLDrivers::GetSystemServices()->SetClipboardContents(clipboardText);
|
||||
clipboardText->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Window::Window()
|
||||
: m_surface(PortabilityLayer::QDPortType_Window)
|
||||
, m_wmX(0)
|
||||
|
@@ -279,3 +279,5 @@ void PL_NotYetImplemented();
|
||||
void PL_NotYetImplemented_Minor();
|
||||
void PL_NotYetImplemented_TODO(const char *category);
|
||||
void PL_Init();
|
||||
|
||||
void PL_CopyStringToClipboard(const uint8_t *chars, size_t length);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "FontFamily.h"
|
||||
#include "FontManager.h"
|
||||
#include "IGpClipboardContents.h"
|
||||
#include "IGpSystemServices.h"
|
||||
#include "InputManager.h"
|
||||
#include "MacRomanConversion.h"
|
||||
@@ -9,14 +10,16 @@
|
||||
#include "RenderedFont.h"
|
||||
#include "GpRenderedFontMetrics.h"
|
||||
#include "ResolveCachingColor.h"
|
||||
#include "TextPlacer.h"
|
||||
#include "Rect2i.h"
|
||||
#include "TextPlacer.h"
|
||||
#include "UTF8.h"
|
||||
|
||||
#include "PLDrivers.h"
|
||||
#include "PLKeyEncoding.h"
|
||||
#include "PLQDraw.h"
|
||||
#include "PLStandardColors.h"
|
||||
#include "PLTimeTaggedVOSEvent.h"
|
||||
#include "PLCore.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -237,6 +240,7 @@ namespace PortabilityLayer
|
||||
const KeyDownStates *downStates = PortabilityLayer::InputManager::GetInstance()->GetKeys();
|
||||
const bool isShiftHeld = downStates->m_special.Get(GpKeySpecials::kLeftShift) || downStates->m_special.Get(GpKeySpecials::kRightShift);
|
||||
const bool isWords = downStates->m_special.Get(GpKeySpecials::kLeftCtrl) || downStates->m_special.Get(GpKeySpecials::kRightCtrl);
|
||||
const bool isCtrlHeld = downStates->m_special.Get(GpKeySpecials::kLeftCtrl) || downStates->m_special.Get(GpKeySpecials::kRightCtrl);
|
||||
|
||||
if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kSpecial)
|
||||
{
|
||||
@@ -281,6 +285,39 @@ namespace PortabilityLayer
|
||||
return WidgetHandleStates::kDigested;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCtrlHeld && keyEvent.m_keyIDSubset == GpKeyIDSubsets::kASCII)
|
||||
{
|
||||
if (keyEvent.m_key.m_asciiChar == kCopyShortcutKey)
|
||||
{
|
||||
if (m_selStartChar != m_selEndChar)
|
||||
HandleCopy();
|
||||
return WidgetHandleStates::kDigested;
|
||||
}
|
||||
if (keyEvent.m_key.m_asciiChar == kCutShortcutKey)
|
||||
{
|
||||
if (m_selStartChar != m_selEndChar)
|
||||
HandleCut();
|
||||
return WidgetHandleStates::kDigested;
|
||||
}
|
||||
else if (keyEvent.m_key.m_asciiChar == kPasteShortcutKey)
|
||||
{
|
||||
HandlePaste(keyEvent.m_repeatCount);
|
||||
return WidgetHandleStates::kDigested;
|
||||
}
|
||||
else if (keyEvent.m_key.m_asciiChar == kSelectAllShortcutKey)
|
||||
{
|
||||
m_selStartChar = 0;
|
||||
m_selEndChar = m_length;
|
||||
|
||||
m_caratSelectionAnchor = CaratSelectionAnchor_Start;
|
||||
m_caratScrollLocked = false;
|
||||
AdjustScrollToCarat();
|
||||
|
||||
m_caratTimer = 0;
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput)
|
||||
@@ -642,7 +679,91 @@ namespace PortabilityLayer
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void EditboxWidget::HandleCopy()
|
||||
{
|
||||
if (m_selStartChar == m_selEndChar)
|
||||
return;
|
||||
|
||||
PL_CopyStringToClipboard(m_chars + m_selStartChar, m_selEndChar - m_selStartChar);
|
||||
}
|
||||
|
||||
void EditboxWidget::HandleCut()
|
||||
{
|
||||
HandleCopy();
|
||||
HandleBackspace(1);
|
||||
}
|
||||
|
||||
void EditboxWidget::HandlePaste(size_t repeatCount)
|
||||
{
|
||||
IGpClipboardContents *clipboardContents = PLDrivers::GetSystemServices()->GetClipboardContents();
|
||||
|
||||
if (clipboardContents == nullptr)
|
||||
return;
|
||||
|
||||
if (clipboardContents->GetContentsType() != GpClipboardContentsTypes::kText)
|
||||
{
|
||||
clipboardContents->Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
IGpClipboardContentsText *textContents = static_cast<IGpClipboardContentsText*>(clipboardContents);
|
||||
const size_t utf8Size = textContents->GetSize();
|
||||
const uint8_t *utf8Bytes = textContents->GetBytes();
|
||||
|
||||
size_t numCodePoints = 0;
|
||||
for (size_t i = 0; i < utf8Size; )
|
||||
{
|
||||
uint32_t codePoint = 0;
|
||||
size_t numDigested = 0;
|
||||
if (!UTF8Processor::DecodeCodePoint(utf8Bytes + i, utf8Size - i, numDigested, codePoint))
|
||||
{
|
||||
clipboardContents->Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
i += numDigested;
|
||||
numCodePoints++;
|
||||
}
|
||||
|
||||
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
||||
uint8_t *decodedChars = static_cast<uint8_t*>(mm->Alloc(numCodePoints));
|
||||
|
||||
if (!decodedChars)
|
||||
{
|
||||
clipboardContents->Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
numCodePoints = 0;
|
||||
for (size_t i = 0; i < utf8Size; )
|
||||
{
|
||||
uint32_t codePoint = 0;
|
||||
size_t numDigested = 0;
|
||||
if (!UTF8Processor::DecodeCodePoint(utf8Bytes + i, utf8Size - i, numDigested, codePoint))
|
||||
{
|
||||
clipboardContents->Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (codePoint > 0xffff || !MacRoman::FromUnicode(decodedChars[numCodePoints], static_cast<uint16_t>(codePoint)))
|
||||
decodedChars[numCodePoints] = '?';
|
||||
|
||||
numCodePoints++;
|
||||
|
||||
i += numDigested;
|
||||
}
|
||||
|
||||
// This is extremely suboptimal due to the embedded redraw...
|
||||
for (size_t i = 0; i < repeatCount; i++)
|
||||
{
|
||||
for (size_t j = 0; j < numCodePoints; j++)
|
||||
HandleCharacter(decodedChars[j], 1);
|
||||
}
|
||||
|
||||
mm->Release(decodedChars);
|
||||
clipboardContents->Destroy();
|
||||
}
|
||||
|
||||
void EditboxWidget::HandleRightArrow(const uint32_t numRepeatsRequested, bool shiftHeld, bool wholeWords)
|
||||
{
|
||||
|
@@ -90,6 +90,9 @@ namespace PortabilityLayer
|
||||
|
||||
void HandleHome(bool shiftHeld);
|
||||
void HandleEnd(bool shiftHeld);
|
||||
void HandleCopy();
|
||||
void HandleCut();
|
||||
void HandlePaste(size_t repeatCount);
|
||||
|
||||
size_t FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange, CaratCharacterAlignment *optOutAlignment) const;
|
||||
void ExpandSelectionToWords(size_t caratPos, size_t &outStartChar, size_t &outEndChar);
|
||||
@@ -137,5 +140,10 @@ namespace PortabilityLayer
|
||||
void *m_characterFilterContext;
|
||||
|
||||
static const CharacterCategorySpan gs_characterCategorySpans[];
|
||||
|
||||
static const int kCopyShortcutKey = 'C';
|
||||
static const int kCutShortcutKey = 'X';
|
||||
static const int kPasteShortcutKey = 'V';
|
||||
static const int kSelectAllShortcutKey = 'A';
|
||||
};
|
||||
}
|
||||
|
@@ -11,5 +11,7 @@ namespace PortabilityLayer
|
||||
static void EncodeCodePoint(uint8_t *characters, size_t &outCharactersEmitted, uint32_t codePoint);
|
||||
|
||||
static bool DecodeToMacRomanPascalStr(const uint8_t *inChars, size_t inSize, uint8_t *outChars, size_t maxOutSize, size_t &outSize);
|
||||
|
||||
static const unsigned int kMaxEncodedBytes = 4;
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user