diff options
Diffstat (limited to 'gfx/skia/skia/src/views')
31 files changed, 6938 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/views/SkEvent.cpp b/gfx/skia/skia/src/views/SkEvent.cpp new file mode 100644 index 000000000..7b658c839 --- /dev/null +++ b/gfx/skia/skia/src/views/SkEvent.cpp @@ -0,0 +1,512 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "SkEvent.h" + +void SkEvent::initialize(const char* type, size_t typeLen, + SkEventSinkID targetID) { + fType = nullptr; + setType(type, typeLen); + f32 = 0; + fTargetID = targetID; + fTargetProc = nullptr; +#ifdef SK_DEBUG + fTime = 0; + fNextEvent = nullptr; +#endif +} + +SkEvent::SkEvent() +{ + initialize("", 0, 0); +} + +SkEvent::SkEvent(const SkEvent& src) +{ + *this = src; + if (((size_t) fType & 1) == 0) + setType(src.fType); +} + +SkEvent::SkEvent(const SkString& type, SkEventSinkID targetID) +{ + initialize(type.c_str(), type.size(), targetID); +} + +SkEvent::SkEvent(const char type[], SkEventSinkID targetID) +{ + SkASSERT(type); + initialize(type, strlen(type), targetID); +} + +SkEvent::~SkEvent() +{ + if (((size_t) fType & 1) == 0) + sk_free((void*) fType); +} + +static size_t makeCharArray(char* buffer, size_t compact) +{ + size_t bits = (size_t) compact >> 1; + memcpy(buffer, &bits, sizeof(compact)); + buffer[sizeof(compact)] = 0; + return strlen(buffer); +} + +void SkEvent::getType(SkString* str) const +{ + if (str) + { + if ((size_t) fType & 1) // not a pointer + { + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + str->set(chars, len); + } + else + str->set(fType); + } +} + +bool SkEvent::isType(const SkString& str) const +{ + return this->isType(str.c_str(), str.size()); +} + +bool SkEvent::isType(const char type[], size_t typeLen) const +{ + if (typeLen == 0) + typeLen = strlen(type); + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + return len == typeLen && strncmp(chars, type, typeLen) == 0; + } + return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; +} + +void SkEvent::setType(const char type[], size_t typeLen) +{ + if (typeLen == 0) + typeLen = strlen(type); + if (typeLen <= sizeof(fType)) { + size_t slot = 0; + memcpy(&slot, type, typeLen); + if (slot << 1 >> 1 != slot) + goto useCharStar; + slot <<= 1; + slot |= 1; + fType = (char*) slot; + } else { +useCharStar: + fType = (char*) sk_malloc_throw(typeLen + 1); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, type, typeLen); + fType[typeLen] = 0; + } +} + +void SkEvent::setType(const SkString& type) +{ + setType(type.c_str()); +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkParse.h" + +void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + const char* name = dom.findAttr(node, "type"); + if (name) + this->setType(name); + + const char* value; + if ((value = dom.findAttr(node, "fast32")) != nullptr) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setFast32(n); + } + + for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node)) + { + if (strcmp(dom.getName(node), "data")) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));) + continue; + } + + name = dom.findAttr(node, "name"); + if (name == nullptr) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");) + continue; + } + + if ((value = dom.findAttr(node, "s32")) != nullptr) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setS32(name, n); + } + else if ((value = dom.findAttr(node, "scalar")) != nullptr) + { + SkScalar x; + if (SkParse::FindScalar(value, &x)) + this->setScalar(name, x); + } + else if ((value = dom.findAttr(node, "string")) != nullptr) + this->setString(name, value); +#ifdef SK_DEBUG + else + { + SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name); + } +#endif + } +} + +#ifdef SK_DEBUG + + #ifndef SkScalarToFloat + #define SkScalarToFloat(x) ((x) / 65536.f) + #endif + + void SkEvent::dump(const char title[]) + { + if (title) + SkDebugf("%s ", title); + + SkString etype; + this->getType(&etype); + SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32()); + + const SkMetaData& md = this->getMetaData(); + SkMetaData::Iter iter(md); + SkMetaData::Type mtype; + int count; + const char* name; + + while ((name = iter.next(&mtype, &count)) != nullptr) + { + SkASSERT(count > 0); + + SkDebugf(" <%s>=", name); + switch (mtype) { + case SkMetaData::kS32_Type: // vector version??? + { + int32_t value; + md.findS32(name, &value); + SkDebugf("%d ", value); + } + break; + case SkMetaData::kScalar_Type: + { + const SkScalar* values = md.findScalars(name, &count, nullptr); + SkDebugf("%f", SkScalarToFloat(values[0])); + for (int i = 1; i < count; i++) + SkDebugf(", %f", SkScalarToFloat(values[i])); + SkDebugf(" "); + } + break; + case SkMetaData::kString_Type: + { + const char* value = md.findString(name); + SkASSERT(value); + SkDebugf("<%s> ", value); + } + break; + case SkMetaData::kPtr_Type: // vector version??? + { + void* value; + md.findPtr(name, &value); + SkDebugf("%p ", value); + } + break; + case SkMetaData::kBool_Type: // vector version??? + { + bool value; + md.findBool(name, &value); + SkDebugf("%s ", value ? "true" : "false"); + } + break; + default: + SkDEBUGFAIL("unknown metadata type returned from iterator"); + break; + } + } + SkDebugf("\n"); + } +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +// #define SK_TRACE_EVENTSx +#endif + +#ifdef SK_TRACE_EVENTS + static void event_log(const char s[]) + { + SkDEBUGF(("%s\n", s)); + } + + #define EVENT_LOG(s) event_log(s) + #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0) +#else + #define EVENT_LOG(s) + #define EVENT_LOGN(s, n) +#endif + +#include "SkMutex.h" +#include "SkTime.h" + +class SkEvent_Globals { +public: + SkEvent_Globals() { + fEventQHead = nullptr; + fEventQTail = nullptr; + fDelayQHead = nullptr; + SkDEBUGCODE(fEventCounter = 0;) + } + + SkMutex fEventMutex; + SkEvent* fEventQHead, *fEventQTail; + SkEvent* fDelayQHead; + SkDEBUGCODE(int fEventCounter;) +}; + +static SkEvent_Globals& getGlobals() { + // leak this, so we don't incure any shutdown perf hit + static SkEvent_Globals* gGlobals = new SkEvent_Globals; + return *gGlobals; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkEvent::postDelay(SkMSec delay) { + if (!fTargetID && !fTargetProc) { + delete this; + return; + } + + if (delay) { + this->postTime(GetMSecsSinceStartup() + delay); + return; + } + + SkEvent_Globals& globals = getGlobals(); + + globals.fEventMutex.acquire(); + bool wasEmpty = SkEvent::Enqueue(this); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if (wasEmpty) { + SkEvent::SignalNonEmptyQueue(); + } +} + +void SkEvent::postTime(SkMSec time) { + if (!fTargetID && !fTargetProc) { + delete this; + return; + } + + SkEvent_Globals& globals = getGlobals(); + + globals.fEventMutex.acquire(); + SkMSec queueDelay = SkEvent::EnqueueTime(this, time); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if ((int32_t)queueDelay != ~0) { + SkEvent::SignalQueueTimer(queueDelay); + } +} + +bool SkEvent::Enqueue(SkEvent* evt) { + SkEvent_Globals& globals = getGlobals(); + // gEventMutex acquired by caller + + SkASSERT(evt); + + bool wasEmpty = globals.fEventQHead == nullptr; + + if (globals.fEventQTail) + globals.fEventQTail->fNextEvent = evt; + globals.fEventQTail = evt; + if (globals.fEventQHead == nullptr) + globals.fEventQHead = evt; + evt->fNextEvent = nullptr; + + SkDEBUGCODE(++globals.fEventCounter); + + return wasEmpty; +} + +SkEvent* SkEvent::Dequeue() { + SkEvent_Globals& globals = getGlobals(); + globals.fEventMutex.acquire(); + + SkEvent* evt = globals.fEventQHead; + if (evt) { + SkDEBUGCODE(--globals.fEventCounter); + + globals.fEventQHead = evt->fNextEvent; + if (globals.fEventQHead == nullptr) { + globals.fEventQTail = nullptr; + } + } + globals.fEventMutex.release(); + + return evt; +} + +bool SkEvent::QHasEvents() { + SkEvent_Globals& globals = getGlobals(); + + // this is not thread accurate, need a semaphore for that + return globals.fEventQHead != nullptr; +} + +#ifdef SK_TRACE_EVENTS + static int gDelayDepth; +#endif + +SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) { + SkEvent_Globals& globals = getGlobals(); + // gEventMutex acquired by caller + + SkEvent* curr = globals.fDelayQHead; + SkEvent* prev = nullptr; + + while (curr) { + if (SkMSec_LT(time, curr->fTime)) { + break; + } + prev = curr; + curr = curr->fNextEvent; + } + + evt->fTime = time; + evt->fNextEvent = curr; + if (prev == nullptr) { + globals.fDelayQHead = evt; + } else { + prev->fNextEvent = evt; + } + + SkMSec delay = globals.fDelayQHead->fTime - GetMSecsSinceStartup(); + if ((int32_t)delay <= 0) { + delay = 1; + } + return delay; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkEventSink.h" + +bool SkEvent::ProcessEvent() { + SkEvent* evt = SkEvent::Dequeue(); + SkAutoTDelete<SkEvent> autoDelete(evt); + bool again = false; + + EVENT_LOGN("ProcessEvent", (int32_t)evt); + + if (evt) { + (void)SkEventSink::DoEvent(*evt); + again = SkEvent::QHasEvents(); + } + return again; +} + +void SkEvent::ServiceQueueTimer() +{ + SkEvent_Globals& globals = getGlobals(); + + globals.fEventMutex.acquire(); + + bool wasEmpty = false; + SkMSec now = GetMSecsSinceStartup(); + SkEvent* evt = globals.fDelayQHead; + + while (evt) + { + if (SkMSec_LT(now, evt->fTime)) + break; + +#ifdef SK_TRACE_EVENTS + --gDelayDepth; + SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); +#endif + + SkEvent* next = evt->fNextEvent; + if (SkEvent::Enqueue(evt)) + wasEmpty = true; + evt = next; + } + globals.fDelayQHead = evt; + + SkMSec time = evt ? evt->fTime - now : 0; + + globals.fEventMutex.release(); + + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + + SkEvent::SignalQueueTimer(time); +} + +int SkEvent::CountEventsOnQueue() { + SkEvent_Globals& globals = getGlobals(); + globals.fEventMutex.acquire(); + + int count = 0; + const SkEvent* evt = globals.fEventQHead; + while (evt) { + count += 1; + evt = evt->fNextEvent; + } + globals.fEventMutex.release(); + + return count; +} + +SkMSec SkEvent::GetMSecsSinceStartup() { + static const double kEpoch = SkTime::GetMSecs(); + return static_cast<SkMSec>(SkTime::GetMSecs() - kEpoch); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkEvent::Init() {} + +void SkEvent::Term() { + SkEvent_Globals& globals = getGlobals(); + + SkEvent* evt = globals.fEventQHead; + while (evt) { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } + + evt = globals.fDelayQHead; + while (evt) { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } +} diff --git a/gfx/skia/skia/src/views/SkEventSink.cpp b/gfx/skia/skia/src/views/SkEventSink.cpp new file mode 100644 index 000000000..1464fa0fa --- /dev/null +++ b/gfx/skia/skia/src/views/SkEventSink.cpp @@ -0,0 +1,300 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "SkEventSink.h" +#include "SkMutex.h" +#include "SkTagList.h" +#include "SkTime.h" + +class SkEventSink_Globals { +public: + SkEventSink_Globals() { + fNextSinkID = 0; + fSinkHead = nullptr; + } + + SkMutex fSinkMutex; + SkEventSinkID fNextSinkID; + SkEventSink* fSinkHead; +}; + +static SkEventSink_Globals& getGlobals() { + // leak this, so we don't incur any shutdown perf hit + static SkEventSink_Globals* gGlobals = new SkEventSink_Globals; + return *gGlobals; +} + +SkEventSink::SkEventSink() : fTagHead(nullptr) { + SkEventSink_Globals& globals = getGlobals(); + + globals.fSinkMutex.acquire(); + + fID = ++globals.fNextSinkID; + fNextSink = globals.fSinkHead; + globals.fSinkHead = this; + + globals.fSinkMutex.release(); +} + +SkEventSink::~SkEventSink() { + SkEventSink_Globals& globals = getGlobals(); + + if (fTagHead) + SkTagList::DeleteAll(fTagHead); + + globals.fSinkMutex.acquire(); + + SkEventSink* sink = globals.fSinkHead; + SkEventSink* prev = nullptr; + + for (;;) { + SkEventSink* next = sink->fNextSink; + if (sink == this) { + if (prev) { + prev->fNextSink = next; + } else { + globals.fSinkHead = next; + } + break; + } + prev = sink; + sink = next; + } + globals.fSinkMutex.release(); +} + +bool SkEventSink::doEvent(const SkEvent& evt) { + return this->onEvent(evt); +} + +bool SkEventSink::doQuery(SkEvent* evt) { + SkASSERT(evt); + return this->onQuery(evt); +} + +bool SkEventSink::onEvent(const SkEvent&) { + return false; +} + +bool SkEventSink::onQuery(SkEvent*) { + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTagList* SkEventSink::findTagList(U8CPU tag) const { + return fTagHead ? SkTagList::Find(fTagHead, tag) : nullptr; +} + +void SkEventSink::addTagList(SkTagList* rec) { + SkASSERT(rec); + SkASSERT(fTagHead == nullptr || SkTagList::Find(fTagHead, rec->fTag) == nullptr); + + rec->fNext = fTagHead; + fTagHead = rec; +} + +void SkEventSink::removeTagList(U8CPU tag) { + if (fTagHead) { + SkTagList::DeleteTag(&fTagHead, tag); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkListenersTagList : SkTagList { + SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList) + { + fExtra16 = SkToU16(count); + fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID)); + } + virtual ~SkListenersTagList() + { + sk_free(fIDs); + } + + int countListners() const { return fExtra16; } + + int find(SkEventSinkID id) const + { + const SkEventSinkID* idptr = fIDs; + for (int i = fExtra16 - 1; i >= 0; --i) + if (idptr[i] == id) + return i; + return -1; + } + + SkEventSinkID* fIDs; +}; + +void SkEventSink::addListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + int count = 0; + + if (prev) + { + if (prev->find(id) >= 0) + return; + count = prev->countListners(); + } + + SkListenersTagList* next = new SkListenersTagList(count + 1); + + if (prev) + { + memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID)); + this->removeTagList(kListeners_SkTagList); + } + next->fIDs[count] = id; + this->addTagList(next); +} + +void SkEventSink::copyListeners(const SkEventSink& sink) +{ + SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList); + if (sinkList == nullptr) + return; + SkASSERT(sinkList->countListners() > 0); + const SkEventSinkID* iter = sinkList->fIDs; + const SkEventSinkID* stop = iter + sinkList->countListners(); + while (iter < stop) + addListenerID(*iter++); +} + +void SkEventSink::removeListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + + if (list == nullptr) + return; + + int index = list->find(id); + if (index >= 0) + { + int count = list->countListners(); + SkASSERT(count > 0); + if (count == 1) + this->removeTagList(kListeners_SkTagList); + else + { + // overwrite without resize/reallocating our struct (for speed) + list->fIDs[index] = list->fIDs[count - 1]; + list->fExtra16 = SkToU16(count - 1); + } + } +} + +bool SkEventSink::hasListeners() const +{ + return this->findTagList(kListeners_SkTagList) != nullptr; +} + +void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) { + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + if (list) { + SkASSERT(list->countListners() > 0); + const SkEventSinkID* iter = list->fIDs; + const SkEventSinkID* stop = iter + list->countListners(); + while (iter < stop) { + SkEvent* copy = new SkEvent(evt); + copy->setTargetID(*iter++)->postDelay(delay); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt) { + SkEvent::Proc proc = evt.getTargetProc(); + if (proc) { + return proc(evt) ? kHandled_EventResult : kNotHandled_EventResult; + } + + SkEventSink* sink = SkEventSink::FindSink(evt.getTargetID()); + if (sink) { + return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult; + } + + return kSinkNotFound_EventResult; +} + +SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID) +{ + if (sinkID == 0) + return 0; + + SkEventSink_Globals& globals = getGlobals(); + SkAutoMutexAcquire ac(globals.fSinkMutex); + SkEventSink* sink = globals.fSinkHead; + + while (sink) + { + if (sink->getSinkID() == sinkID) + return sink; + sink = sink->fNextSink; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // experimental, not tested + +#include "SkMutex.h" +#include "SkTDict.h" + +#define kMinStringBufferSize 128 +SK_DECLARE_STATIC_MUTEX(gNamedSinkMutex); +static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize); + +/** Register a name/id pair with the system. If the name already exists, + replace its ID with the new id. This pair will persist until UnregisterNamedSink() + is called. +*/ +void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id) +{ + if (id && name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + gNamedSinkIDs.set(name, id); + } +} + +/** Return the id that matches the specified name (from a previous call to + RegisterNamedSinkID(). If no match is found, return 0 +*/ +SkEventSinkID SkEventSink::FindNamedSinkID(const char name[]) +{ + SkEventSinkID id = 0; + + if (name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.find(name, &id); + } + return id; +} + +/** Remove all name/id pairs from the system. This is call internally + on shutdown, to ensure no memory leaks. It should not be called + before shutdown. +*/ +void SkEventSink::RemoveAllNamedSinkIDs() +{ + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.reset(); +} +#endif diff --git a/gfx/skia/skia/src/views/SkOSMenu.cpp b/gfx/skia/skia/src/views/SkOSMenu.cpp new file mode 100644 index 000000000..ec92a7b4a --- /dev/null +++ b/gfx/skia/skia/src/views/SkOSMenu.cpp @@ -0,0 +1,263 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkAtomics.h" +#include "SkOSMenu.h" +#include <stdarg.h> + +static int gOSMenuCmd = 7000; + +SkOSMenu::SkOSMenu(const char title[]) { + fTitle.set(title); +} + +SkOSMenu::~SkOSMenu() { + this->reset(); +} + +void SkOSMenu::reset() { + fItems.deleteAll(); + fTitle.reset(); +} + +const SkOSMenu::Item* SkOSMenu::getItemByID(int itemID) const { + for (int i = 0; i < fItems.count(); ++i) { + if (itemID == fItems[i]->getID()) + return fItems[i]; + } + return nullptr; +} + +void SkOSMenu::getItems(const SkOSMenu::Item* items[]) const { + if (items) { + for (int i = 0; i < fItems.count(); ++i) { + items[i] = fItems[i]; + } + } +} + +void SkOSMenu::assignKeyEquivalentToItem(int itemID, SkUnichar key) { + for (int i = 0; i < fItems.count(); ++i) { + if (itemID == fItems[i]->getID()) + fItems[i]->setKeyEquivalent(key); + } +} + +bool SkOSMenu::handleKeyEquivalent(SkUnichar key) { + int value = 0, size = 0; + bool state; + SkOSMenu::TriState tristate; + for (int i = 0; i < fItems.count(); ++i) { + Item* item = fItems[i]; + if (item->getKeyEquivalent()== key) { + SkString list; + switch (item->getType()) { + case kList_Type: + SkOSMenu::FindListItemCount(*item->getEvent(), &size); + SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &value); + value = (value + 1) % size; + item->setInt(value); + break; + case kSwitch_Type: + SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state); + item->setBool(!state); + break; + case kTriState_Type: + SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate); + if (kOnState == tristate) + tristate = kMixedState; + else + tristate = (SkOSMenu::TriState)((int)tristate + 1); + item->setTriState(tristate); + break; + case kAction_Type: + case kCustom_Type: + case kSlider_Type: + case kTextField_Type: + default: + break; + } + item->postEvent(); + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + +SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type, + const char slotName[], SkEvent* evt) { + fLabel.set(label); + fSlotName.set(slotName); + fType = type; + fEvent = evt; + fKey = 0; + fID = sk_atomic_inc(&gOSMenuCmd); +} + +void SkOSMenu::Item::setBool(bool value) const { + SkASSERT(SkOSMenu::kSwitch_Type == fType); + fEvent->setBool(fSlotName.c_str(), value); +} + +void SkOSMenu::Item::setScalar(SkScalar value) const { + SkASSERT(SkOSMenu::kSlider_Type == fType); + fEvent->setScalar(fSlotName.c_str(), value); +} + +void SkOSMenu::Item::setInt(int value) const { + SkASSERT(SkOSMenu::kList_Type == fType); + fEvent->setS32(fSlotName.c_str(), value); +} + +void SkOSMenu::Item::setTriState(TriState value) const { + SkASSERT(SkOSMenu::kTriState_Type == fType); + fEvent->setS32(fSlotName.c_str(), value); +} + +void SkOSMenu::Item::setString(const char value[]) const { + SkASSERT(SkOSMenu::kTextField_Type == fType); + fEvent->setString(fSlotName.c_str(), value); +} + +//////////////////////////////////////////////////////////////////////////////// + +static const char* gMenuEventType = "SkOSMenuEventType"; +static const char* gSlider_Min_Scalar = "SkOSMenuSlider_Min"; +static const char* gSlider_Max_Scalar = "SkOSMenuSlider_Max"; +static const char* gDelimiter = "|"; +static const char* gList_Items_Str = "SkOSMenuList_Items"; +static const char* gList_ItemCount_S32 = "SkOSMenuList_ItemCount"; + +int SkOSMenu::appendItem(const char label[], Type type, const char slotName[], + SkEvent* evt) { + SkOSMenu::Item* item = new Item(label, type, slotName, evt); + fItems.append(1, &item); + return item->getID(); +} + +int SkOSMenu::appendAction(const char label[], SkEventSinkID target) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + //Store label in event so it can be used to identify the action later + evt->setString(label, label); + return appendItem(label, SkOSMenu::kAction_Type, "", evt); +} + +int SkOSMenu::appendList(const char label[], const char slotName[], + SkEventSinkID target, int index, const char option[], ...) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + va_list args; + if (option) { + SkString str(option); + va_start(args, option); + int count = 1; + for (const char* arg = va_arg(args, const char*); arg != nullptr; arg = va_arg(args, const char*)) { + str += gDelimiter; + str += arg; + ++count; + } + va_end(args); + evt->setString(gList_Items_Str, str); + evt->setS32(gList_ItemCount_S32, count); + evt->setS32(slotName, index); + } + return appendItem(label, SkOSMenu::kList_Type, slotName, evt); +} + +int SkOSMenu::appendSlider(const char label[], const char slotName[], + SkEventSinkID target, SkScalar min, SkScalar max, + SkScalar defaultValue) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + evt->setScalar(gSlider_Min_Scalar, min); + evt->setScalar(gSlider_Max_Scalar, max); + evt->setScalar(slotName, defaultValue); + return appendItem(label, SkOSMenu::kSlider_Type, slotName, evt); +} + +int SkOSMenu::appendSwitch(const char label[], const char slotName[], + SkEventSinkID target, bool defaultState) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + evt->setBool(slotName, defaultState); + return appendItem(label, SkOSMenu::kSwitch_Type, slotName, evt); +} + +int SkOSMenu::appendTriState(const char label[], const char slotName[], + SkEventSinkID target, SkOSMenu::TriState defaultState) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + evt->setS32(slotName, defaultState); + return appendItem(label, SkOSMenu::kTriState_Type, slotName, evt); +} + +int SkOSMenu::appendTextField(const char label[], const char slotName[], + SkEventSinkID target, const char placeholder[]) { + SkEvent* evt = new SkEvent(gMenuEventType, target); + evt->setString(slotName, placeholder); + return appendItem(label, SkOSMenu::kTextField_Type, slotName, evt); +} + +bool SkOSMenu::FindListItemCount(const SkEvent& evt, int* count) { + return evt.isType(gMenuEventType) && evt.findS32(gList_ItemCount_S32, count); +} + +bool SkOSMenu::FindListItems(const SkEvent& evt, SkString items[]) { + if (evt.isType(gMenuEventType) && items) { + const char* text = evt.findString(gList_Items_Str); + if (text != nullptr) { + SkString temp(text); + char* token = strtok((char*)temp.c_str(), gDelimiter); + int index = 0; + while (token != nullptr) { + items[index].set(token, strlen(token)); + token = strtok (nullptr, gDelimiter); + ++index; + } + } + return true; + } + return false; +} + +bool SkOSMenu::FindSliderMin(const SkEvent& evt, SkScalar* min) { + return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Min_Scalar, min); +} + +bool SkOSMenu::FindSliderMax(const SkEvent& evt, SkScalar* max) { + return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Max_Scalar, max); +} + +bool SkOSMenu::FindAction(const SkEvent& evt, const char label[]) { + return evt.isType(gMenuEventType) && evt.findString(label); +} + +bool SkOSMenu::FindListIndex(const SkEvent& evt, const char slotName[], int* value) { + return evt.isType(gMenuEventType) && evt.findS32(slotName, value); +} + +bool SkOSMenu::FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value) { + return evt.isType(gMenuEventType) && evt.findScalar(slotName, value); +} + +bool SkOSMenu::FindSwitchState(const SkEvent& evt, const char slotName[], bool* value) { + return evt.isType(gMenuEventType) && evt.findBool(slotName, value); +} + +bool SkOSMenu::FindTriState(const SkEvent& evt, const char slotName[], SkOSMenu::TriState* value) { + return evt.isType(gMenuEventType) && evt.findS32(slotName, (int*)value); +} + +bool SkOSMenu::FindText(const SkEvent& evt, const char slotName[], SkString* value) { + if (evt.isType(gMenuEventType)) { + const char* text = evt.findString(slotName); + if (!text || !*text) + return false; + else { + value->set(text); + return true; + } + } + return false; +} diff --git a/gfx/skia/skia/src/views/SkTagList.cpp b/gfx/skia/skia/src/views/SkTagList.cpp new file mode 100644 index 000000000..27f3916ec --- /dev/null +++ b/gfx/skia/skia/src/views/SkTagList.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "SkTagList.h" + +SkTagList::~SkTagList() +{ +} + +SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + while (rec != nullptr) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + return rec; +} + +void SkTagList::DeleteTag(SkTagList** head, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + SkTagList* rec = *head; + SkTagList* prev = nullptr; + + while (rec != nullptr) + { + SkTagList* next = rec->fNext; + + if (rec->fTag == tag) + { + if (prev) + prev->fNext = next; + else + *head = next; + delete rec; + break; + } + prev = rec; + rec = next; + } +} + +void SkTagList::DeleteAll(SkTagList* rec) +{ + while (rec) + { + SkTagList* next = rec->fNext; + delete rec; + rec = next; + } +} diff --git a/gfx/skia/skia/src/views/SkTagList.h b/gfx/skia/skia/src/views/SkTagList.h new file mode 100644 index 000000000..0b158e1ab --- /dev/null +++ b/gfx/skia/skia/src/views/SkTagList.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkTagList_DEFINED +#define SkTagList_DEFINED + +#include "SkTypes.h" + +enum SkTagListEnum { + kListeners_SkTagList, + kViewLayout_SkTagList, + kViewArtist_SkTagList, + + kSkTagListCount +}; + +struct SkTagList { + SkTagList* fNext; + uint16_t fExtra16; + uint8_t fExtra8; + uint8_t fTag; + + SkTagList(U8CPU tag) : fTag(SkToU8(tag)) + { + SkASSERT(tag < kSkTagListCount); + fNext = nullptr; + fExtra16 = 0; + fExtra8 = 0; + } + virtual ~SkTagList(); + + static SkTagList* Find(SkTagList* head, U8CPU tag); + static void DeleteTag(SkTagList** headptr, U8CPU tag); + static void DeleteAll(SkTagList* head); +}; + +#endif diff --git a/gfx/skia/skia/src/views/SkTouchGesture.cpp b/gfx/skia/skia/src/views/SkTouchGesture.cpp new file mode 100644 index 000000000..752828e37 --- /dev/null +++ b/gfx/skia/skia/src/views/SkTouchGesture.cpp @@ -0,0 +1,353 @@ +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <algorithm> + +#include "SkTouchGesture.h" +#include "SkMatrix.h" +#include "SkTime.h" + +#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true + +static const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500); + +static SkScalar pin_max_fling(SkScalar speed) { + if (speed > MAX_FLING_SPEED) { + speed = MAX_FLING_SPEED; + } + return speed; +} + +static double getseconds() { + return SkTime::GetMSecs() * 0.001; +} + +// returns +1 or -1, depending on the sign of x +// returns +1 if z is zero +static SkScalar SkScalarSignNonZero(SkScalar x) { + SkScalar sign = SK_Scalar1; + if (x < 0) { + sign = -sign; + } + return sign; +} + +static void unit_axis_align(SkVector* unit) { + const SkScalar TOLERANCE = SkDoubleToScalar(0.15); + if (SkScalarAbs(unit->fX) < TOLERANCE) { + unit->fX = 0; + unit->fY = SkScalarSignNonZero(unit->fY); + } else if (SkScalarAbs(unit->fY) < TOLERANCE) { + unit->fX = SkScalarSignNonZero(unit->fX); + unit->fY = 0; + } +} + +void SkFlingState::reset(float sx, float sy) { + fActive = true; + fDirection.set(sx, sy); + fSpeed0 = SkPoint::Normalize(&fDirection); + fSpeed0 = pin_max_fling(fSpeed0); + fTime0 = getseconds(); + + unit_axis_align(&fDirection); +// printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY); +} + +bool SkFlingState::evaluateMatrix(SkMatrix* matrix) { + if (!fActive) { + return false; + } + + const float t = (float)(getseconds() - fTime0); + const float MIN_SPEED = 2; + const float K0 = 5; + const float K1 = 0.02f; + const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1); + if (speed <= MIN_SPEED) { + fActive = false; + return false; + } + float dist = (fSpeed0 - speed) / K0; + +// printf("---- time %g speed %g dist %g\n", t, speed, dist); + float tx = fDirection.fX * dist; + float ty = fDirection.fY * dist; + if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) { + tx = (float)sk_float_round2int(tx); + ty = (float)sk_float_round2int(ty); + } + matrix->setTranslate(tx, ty); +// printf("---- evaluate (%g %g)\n", tx, ty); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const SkMSec MAX_DBL_TAP_INTERVAL = 300; +static const float MAX_DBL_TAP_DISTANCE = 100; +static const float MAX_JITTER_RADIUS = 2; + +// if true, then ignore the touch-move, 'cause its probably just jitter +static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) { + return sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS && + sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTouchGesture::SkTouchGesture() { + this->reset(); +} + +SkTouchGesture::~SkTouchGesture() { +} + +void SkTouchGesture::reset() { + fIsTransLimited = false; + fTouches.reset(); + fState = kEmpty_State; + fLocalM.reset(); + fGlobalM.reset(); + + fLastUpMillis = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL; + fLastUpP.set(0, 0); +} + +void SkTouchGesture::flushLocalM() { + fGlobalM.postConcat(fLocalM); + fLocalM.reset(); +} + +const SkMatrix& SkTouchGesture::localM() { + if (fFlinger.isActive()) { + if (!fFlinger.evaluateMatrix(&fLocalM)) { + this->flushLocalM(); + } + } + return fLocalM; +} + +void SkTouchGesture::appendNewRec(void* owner, float x, float y) { + Rec* rec = fTouches.append(); + rec->fOwner = owner; + rec->fStartX = rec->fPrevX = rec->fLastX = x; + rec->fStartY = rec->fPrevY = rec->fLastY = y; + rec->fLastT = rec->fPrevT = static_cast<float>(SkTime::GetSecs()); +} + +void SkTouchGesture::touchBegin(void* owner, float x, float y) { +// SkDebugf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y); + + int index = this->findRec(owner); + if (index >= 0) { + this->flushLocalM(); + fTouches.removeShuffle(index); + SkDebugf("---- already exists, removing\n"); + } + + if (fTouches.count() == 2) { + return; + } + + this->flushLocalM(); + fFlinger.stop(); + + this->appendNewRec(owner, x, y); + + switch (fTouches.count()) { + case 1: + fState = kTranslate_State; + break; + case 2: + fState = kZoom_State; + break; + default: + break; + } +} + +int SkTouchGesture::findRec(void* owner) const { + for (int i = 0; i < fTouches.count(); i++) { + if (owner == fTouches[i].fOwner) { + return i; + } + } + return -1; +} + +static SkScalar center(float pos0, float pos1) { + return (pos0 + pos1) * 0.5f; +} + +static const float MAX_ZOOM_SCALE = 4; +static const float MIN_ZOOM_SCALE = 0.25f; + +float SkTouchGesture::limitTotalZoom(float scale) const { + // this query works 'cause we know that we're square-scale w/ no skew/rotation + const float curr = SkScalarToFloat(fGlobalM[0]); + + if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) { + scale = MAX_ZOOM_SCALE / curr; + } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) { + scale = MIN_ZOOM_SCALE / curr; + } + return scale; +} + +void SkTouchGesture::touchMoved(void* owner, float x, float y) { +// SkDebugf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y); + + if (kEmpty_State == fState) { + return; + } + + int index = this->findRec(owner); + if (index < 0) { + // not found, so I guess we should add it... + SkDebugf("---- add missing begin\n"); + this->appendNewRec(owner, x, y); + index = fTouches.count() - 1; + } + + Rec& rec = fTouches[index]; + + // not sure how valuable this is + if (fTouches.count() == 2) { + if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) { +// SkDebugf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y); + return; + } + } + + rec.fPrevX = rec.fLastX; rec.fLastX = x; + rec.fPrevY = rec.fLastY; rec.fLastY = y; + rec.fPrevT = rec.fLastT; + rec.fLastT = static_cast<float>(SkTime::GetSecs()); + + switch (fTouches.count()) { + case 1: { + float dx = rec.fLastX - rec.fStartX; + float dy = rec.fLastY - rec.fStartY; + dx = (float)sk_float_round2int(dx); + dy = (float)sk_float_round2int(dy); + fLocalM.setTranslate(dx, dy); + } break; + case 2: { + SkASSERT(kZoom_State == fState); + const Rec& rec0 = fTouches[0]; + const Rec& rec1 = fTouches[1]; + + float scale = this->computePinch(rec0, rec1); + scale = this->limitTotalZoom(scale); + + fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX), + -center(rec0.fStartY, rec1.fStartY)); + fLocalM.postScale(scale, scale); + fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX), + center(rec0.fLastY, rec1.fLastY)); + } break; + default: + break; + } +} + +void SkTouchGesture::touchEnd(void* owner) { +// SkDebugf("--- %d touchEnd %p\n", fTouches.count(), owner); + + int index = this->findRec(owner); + if (index < 0) { + SkDebugf("--- not found\n"); + return; + } + + const Rec& rec = fTouches[index]; + if (this->handleDblTap(rec.fLastX, rec.fLastY)) { + return; + } + + // count() reflects the number before we removed the owner + switch (fTouches.count()) { + case 1: { + this->flushLocalM(); + float dx = rec.fLastX - rec.fPrevX; + float dy = rec.fLastY - rec.fPrevY; + float dur = rec.fLastT - rec.fPrevT; + if (dur > 0) { + fFlinger.reset(dx / dur, dy / dur); + } + fState = kEmpty_State; + } break; + case 2: + this->flushLocalM(); + SkASSERT(kZoom_State == fState); + fState = kEmpty_State; + break; + default: + SkASSERT(kZoom_State == fState); + break; + } + + fTouches.removeShuffle(index); + + limitTrans(); +} + +float SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) { + double dx = rec0.fStartX - rec1.fStartX; + double dy = rec0.fStartY - rec1.fStartY; + double dist0 = sqrt(dx*dx + dy*dy); + + dx = rec0.fLastX - rec1.fLastX; + dy = rec0.fLastY - rec1.fLastY; + double dist1 = sqrt(dx*dx + dy*dy); + + double scale = dist1 / dist0; + return (float)scale; +} + +bool SkTouchGesture::handleDblTap(float x, float y) { + bool found = false; + double now = SkTime::GetMSecs(); + if (now - fLastUpMillis <= MAX_DBL_TAP_INTERVAL) { + if (SkPoint::Length(fLastUpP.fX - x, + fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) { + fFlinger.stop(); + fLocalM.reset(); + fGlobalM.reset(); + fTouches.reset(); + fState = kEmpty_State; + found = true; + } + } + + fLastUpMillis = now; + fLastUpP.set(x, y); + return found; +} + +void SkTouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect) { + fIsTransLimited = true; + fContentRect = contentRect; + fWindowRect = windowRect; +} + +void SkTouchGesture::limitTrans() { + if (!fIsTransLimited) { + return; + } + + SkRect scaledContent = fContentRect; + fGlobalM.mapRect(&scaledContent); + const SkScalar ZERO = 0; + + fGlobalM.postTranslate(ZERO, std::min(ZERO, fWindowRect.fBottom - scaledContent.fTop)); + fGlobalM.postTranslate(ZERO, std::max(ZERO, fWindowRect.fTop - scaledContent.fBottom)); + fGlobalM.postTranslate(std::min(ZERO, fWindowRect.fRight - scaledContent.fLeft), ZERO); + fGlobalM.postTranslate(std::max(ZERO, fWindowRect.fLeft - scaledContent.fRight), ZERO); +} diff --git a/gfx/skia/skia/src/views/SkView.cpp b/gfx/skia/skia/src/views/SkView.cpp new file mode 100644 index 000000000..492b2cdac --- /dev/null +++ b/gfx/skia/skia/src/views/SkView.cpp @@ -0,0 +1,810 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkView.h" +#include "SkCanvas.h" + +static inline uint32_t SkSetClearShift(uint32_t bits, bool cond, unsigned shift) { + SkASSERT((int)cond == 0 || (int)cond == 1); + return (bits & ~(1 << shift)) | ((int)cond << shift); +} + +//////////////////////////////////////////////////////////////////////// + +SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) { + fWidth = fHeight = 0; + fLoc.set(0, 0); + fParent = fFirstChild = fNextSibling = fPrevSibling = nullptr; + fMatrix.setIdentity(); + fContainsFocus = 0; +} + +SkView::~SkView() { + this->detachAllChildren(); +} + +void SkView::setFlags(uint32_t flags) { + SkASSERT((flags & ~kAllFlagMasks) == 0); + + uint32_t diff = fFlags ^ flags; + + if (diff & kVisible_Mask) + this->inval(nullptr); + + fFlags = SkToU8(flags); + + if (diff & kVisible_Mask) { + this->inval(nullptr); + } +} + +void SkView::setVisibleP(bool pred) { + this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift)); +} + +void SkView::setEnabledP(bool pred) { + this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift)); +} + +void SkView::setFocusableP(bool pred) { + this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift)); +} + +void SkView::setClipToBounds(bool pred) { + this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift)); +} + +void SkView::setSize(SkScalar width, SkScalar height) { + width = SkMaxScalar(0, width); + height = SkMaxScalar(0, height); + + if (fWidth != width || fHeight != height) + { + this->inval(nullptr); + fWidth = width; + fHeight = height; + this->inval(nullptr); + this->onSizeChange(); + this->invokeLayout(); + } +} + +void SkView::setLoc(SkScalar x, SkScalar y) { + if (fLoc.fX != x || fLoc.fY != y) { + this->inval(nullptr); + fLoc.set(x, y); + this->inval(nullptr); + } +} + +void SkView::offset(SkScalar dx, SkScalar dy) { + if (dx || dy) + this->setLoc(fLoc.fX + dx, fLoc.fY + dy); +} + +void SkView::setLocalMatrix(const SkMatrix& matrix) { + this->inval(nullptr); + fMatrix = matrix; + this->inval(nullptr); +} + +void SkView::draw(SkCanvas* canvas) { + if (fWidth && fHeight && this->isVisible()) { + SkRect r; + r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); + if (this->isClipToBounds() && canvas->quickReject(r)) { + return; + } + + SkAutoCanvasRestore as(canvas, true); + + if (this->isClipToBounds()) { + canvas->clipRect(r); + } + + canvas->translate(fLoc.fX, fLoc.fY); + canvas->concat(fMatrix); + + if (fParent) { + fParent->beforeChild(this, canvas); + } + + int sc = canvas->save(); + this->onDraw(canvas); + canvas->restoreToCount(sc); + + if (fParent) { + fParent->afterChild(this, canvas); + } + + B2FIter iter(this); + SkView* child; + + SkCanvas* childCanvas = this->beforeChildren(canvas); + + while ((child = iter.next()) != nullptr) + child->draw(childCanvas); + + this->afterChildren(canvas); + } +} + +void SkView::inval(SkRect* rect) { + SkView* view = this; + SkRect storage; + + for (;;) { + if (!view->isVisible()) { + return; + } + if (view->isClipToBounds()) { + SkRect bounds; + view->getLocalBounds(&bounds); + if (rect && !bounds.intersect(*rect)) { + return; + } + storage = bounds; + rect = &storage; + } + if (view->handleInval(rect)) { + return; + } + + SkView* parent = view->fParent; + if (parent == nullptr) { + return; + } + + if (rect) { + rect->offset(view->fLoc.fX, view->fLoc.fY); + } + view = parent; + } +} + +//////////////////////////////////////////////////////////////////////////// + +bool SkView::setFocusView(SkView* fv) { + SkView* view = this; + + do { + if (view->onSetFocusView(fv)) { + return true; + } + } while ((view = view->fParent) != nullptr); + return false; +} + +SkView* SkView::getFocusView() const { + SkView* focus = nullptr; + const SkView* view = this; + do { + if (view->onGetFocusView(&focus)) { + break; + } + } while ((view = view->fParent) != nullptr); + return focus; +} + +bool SkView::hasFocus() const { + return this == this->getFocusView(); +} + +bool SkView::acceptFocus() { + return this->isFocusable() && this->setFocusView(this); +} + +/* + Try to give focus to this view, or its children +*/ +SkView* SkView::acceptFocus(FocusDirection dir) { + if (dir == kNext_FocusDirection) { + if (this->acceptFocus()) { + return this; + } + B2FIter iter(this); + SkView* child, *focus; + while ((child = iter.next()) != nullptr) { + if ((focus = child->acceptFocus(dir)) != nullptr) { + return focus; + } + } + } else { // prev + F2BIter iter(this); + SkView* child, *focus; + while ((child = iter.next()) != nullptr) { + if ((focus = child->acceptFocus(dir)) != nullptr) { + return focus; + } + } + if (this->acceptFocus()) { + return this; + } + } + return nullptr; +} + +SkView* SkView::moveFocus(FocusDirection dir) { + SkView* focus = this->getFocusView(); + + if (focus == nullptr) { // start with the root + focus = this; + while (focus->fParent) { + focus = focus->fParent; + } + } + + SkView* child, *parent; + + if (dir == kNext_FocusDirection) { + parent = focus; + child = focus->fFirstChild; + if (child) + goto FIRST_CHILD; + else + goto NEXT_SIB; + + do { + while (child != parent->fFirstChild) { + FIRST_CHILD: + if ((focus = child->acceptFocus(dir)) != nullptr) + return focus; + child = child->fNextSibling; + } + NEXT_SIB: + child = parent->fNextSibling; + parent = parent->fParent; + } while (parent != nullptr); + } else { // prevfocus + parent = focus->fParent; + if (parent == nullptr) { // we're the root + return focus->acceptFocus(dir); + } else { + child = focus; + while (parent) { + while (child != parent->fFirstChild) { + child = child->fPrevSibling; + if ((focus = child->acceptFocus(dir)) != nullptr) { + return focus; + } + } + if (parent->acceptFocus()) { + return parent; + } + child = parent; + parent = parent->fParent; + } + } + } + return nullptr; +} + +void SkView::onFocusChange(bool gainFocusP) { + this->inval(nullptr); +} + +//////////////////////////////////////////////////////////////////////////// + +SkView::Click::Click(SkView* target) { + SkASSERT(target); + fTargetID = target->getSinkID(); + fType = nullptr; + fWeOwnTheType = false; + fOwner = nullptr; +} + +SkView::Click::~Click() { + this->resetType(); +} + +void SkView::Click::resetType() { + if (fWeOwnTheType) { + sk_free(fType); + fWeOwnTheType = false; + } + fType = nullptr; +} + +bool SkView::Click::isType(const char type[]) const { + const char* t = fType; + + if (type == t) { + return true; + } + if (type == nullptr) { + type = ""; + } + if (t == nullptr) { + t = ""; + } + return !strcmp(t, type); +} + +void SkView::Click::setType(const char type[]) { + this->resetType(); + fType = (char*)type; +} + +void SkView::Click::copyType(const char type[]) { + if (fType != type) { + this->resetType(); + if (type) { + size_t len = strlen(type) + 1; + fType = (char*)sk_malloc_throw(len); + memcpy(fType, type, len); + fWeOwnTheType = true; + } + } +} + +SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y, unsigned modi) { + if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) { + return nullptr; + } + + if (this->onSendClickToChildren(x, y, modi)) { + F2BIter iter(this); + SkView* child; + + while ((child = iter.next()) != nullptr) { + SkPoint p; +#if 0 + if (!child->globalToLocal(x, y, &p)) { + continue; + } +#else + // the above seems broken, so just respecting fLoc for now <reed> + p.set(x - child->fLoc.x(), y - child->fLoc.y()); +#endif + + Click* click = child->findClickHandler(p.fX, p.fY, modi); + + if (click) { + return click; + } + } + } + + return this->onFindClickHandler(x, y, modi); +} + +void SkView::DoClickDown(Click* click, int x, int y, unsigned modi) { + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (nullptr == target) { + return; + } + + click->fIOrig.set(x, y); + click->fICurr = click->fIPrev = click->fIOrig; + + click->fOrig.iset(x, y); + if (!target->globalToLocal(&click->fOrig)) { + // no history to let us recover from this failure + return; + } + click->fPrev = click->fCurr = click->fOrig; + + click->fState = Click::kDown_State; + click->fModifierKeys = modi; + target->onClick(click); +} + +void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi) { + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (nullptr == target) { + return; + } + + click->fIPrev = click->fICurr; + click->fICurr.set(x, y); + + click->fPrev = click->fCurr; + click->fCurr.iset(x, y); + if (!target->globalToLocal(&click->fCurr)) { + // on failure pretend the mouse didn't move + click->fCurr = click->fPrev; + } + + click->fState = Click::kMoved_State; + click->fModifierKeys = modi; + target->onClick(click); +} + +void SkView::DoClickUp(Click* click, int x, int y, unsigned modi) { + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (nullptr == target) { + return; + } + + click->fIPrev = click->fICurr; + click->fICurr.set(x, y); + + click->fPrev = click->fCurr; + click->fCurr.iset(x, y); + if (!target->globalToLocal(&click->fCurr)) { + // on failure pretend the mouse didn't move + click->fCurr = click->fPrev; + } + + click->fState = Click::kUp_State; + click->fModifierKeys = modi; + target->onClick(click); +} + +////////////////////////////////////////////////////////////////////// + +void SkView::invokeLayout() { + SkView::Layout* layout = this->getLayout(); + + if (layout) { + layout->layoutChildren(this); + } +} + +void SkView::onDraw(SkCanvas* canvas) { + Artist* artist = this->getArtist(); + + if (artist) { + artist->draw(this, canvas); + } +} + +void SkView::onSizeChange() {} + +bool SkView::onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) { + return true; +} + +SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) { + return nullptr; +} + +bool SkView::onClick(Click*) { + return false; +} + +bool SkView::handleInval(const SkRect*) { + return false; +} + +////////////////////////////////////////////////////////////////////// + +void SkView::getLocalBounds(SkRect* bounds) const { + if (bounds) { + bounds->set(0, 0, fWidth, fHeight); + } +} + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +void SkView::detachFromParent_NoLayout() { + this->validate(); + if (fParent == nullptr) { + return; + } + + if (fContainsFocus) { + (void)this->setFocusView(nullptr); + } + + this->inval(nullptr); + + SkView* next = nullptr; + + if (fNextSibling != this) { // do we have any siblings + fNextSibling->fPrevSibling = fPrevSibling; + fPrevSibling->fNextSibling = fNextSibling; + next = fNextSibling; + } + + if (fParent->fFirstChild == this) { + fParent->fFirstChild = next; + } + + fParent = fNextSibling = fPrevSibling = nullptr; + + this->validate(); + this->unref(); +} + +void SkView::detachFromParent() { + this->validate(); + SkView* parent = fParent; + + if (parent) { + this->detachFromParent_NoLayout(); + parent->invokeLayout(); + } +} + +SkView* SkView::attachChildToBack(SkView* child) { + this->validate(); + SkASSERT(child != this); + + if (child == nullptr || fFirstChild == child) + goto DONE; + + child->ref(); + child->detachFromParent_NoLayout(); + + if (fFirstChild == nullptr) { + child->fNextSibling = child; + child->fPrevSibling = child; + } else { + child->fNextSibling = fFirstChild; + child->fPrevSibling = fFirstChild->fPrevSibling; + fFirstChild->fPrevSibling->fNextSibling = child; + fFirstChild->fPrevSibling = child; + } + + fFirstChild = child; + child->fParent = this; + child->inval(nullptr); + + this->validate(); + this->invokeLayout(); +DONE: + return child; +} + +SkView* SkView::attachChildToFront(SkView* child) { + this->validate(); + SkASSERT(child != this); + + if (child == nullptr || (fFirstChild && fFirstChild->fPrevSibling == child)) + goto DONE; + + child->ref(); + child->detachFromParent_NoLayout(); + + if (fFirstChild == nullptr) { + fFirstChild = child; + child->fNextSibling = child; + child->fPrevSibling = child; + } else { + child->fNextSibling = fFirstChild; + child->fPrevSibling = fFirstChild->fPrevSibling; + fFirstChild->fPrevSibling->fNextSibling = child; + fFirstChild->fPrevSibling = child; + } + + child->fParent = this; + child->inval(nullptr); + + this->validate(); + this->invokeLayout(); +DONE: + return child; +} + +void SkView::detachAllChildren() { + this->validate(); + while (fFirstChild) + fFirstChild->detachFromParent_NoLayout(); +} + +void SkView::localToGlobal(SkMatrix* matrix) const { + if (matrix) { + matrix->reset(); + const SkView* view = this; + while (view) + { + matrix->preConcat(view->getLocalMatrix()); + matrix->preTranslate(-view->fLoc.fX, -view->fLoc.fY); + view = view->fParent; + } + } +} + +bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const { + if (local) { + SkMatrix m; + this->localToGlobal(&m); + if (!m.invert(&m)) { + return false; + } + SkPoint p; + m.mapXY(x, y, &p); + local->set(p.fX, p.fY); + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +/* Even if the subclass overrides onInflate, they should always be + sure to call the inherited method, so that we get called. +*/ +void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node) { + SkScalar x, y; + + x = this->locX(); + y = this->locY(); + (void)dom.findScalar(node, "x", &x); + (void)dom.findScalar(node, "y", &y); + this->setLoc(x, y); + + x = this->width(); + y = this->height(); + (void)dom.findScalar(node, "width", &x); + (void)dom.findScalar(node, "height", &y); + this->setSize(x, y); + + // inflate the flags + + static const char* gFlagNames[] = { + "visible", "enabled", "focusable", "flexH", "flexV" + }; + SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount); + + bool b; + uint32_t flags = this->getFlags(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++) { + if (dom.findBool(node, gFlagNames[i], &b)) { + flags = SkSetClearShift(flags, b, i); + } + } + this->setFlags(flags); +} + +void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node) { + this->onInflate(dom, node); +} + +void SkView::onPostInflate(const SkTDict<SkView*>&) { + // override in subclass as needed +} + +void SkView::postInflate(const SkTDict<SkView*>& dict) { + this->onPostInflate(dict); + + B2FIter iter(this); + SkView* child; + while ((child = iter.next()) != nullptr) + child->postInflate(dict); +} + +////////////////////////////////////////////////////////////////// + +SkView* SkView::sendEventToParents(const SkEvent& evt) { + SkView* parent = fParent; + + while (parent) { + if (parent->doEvent(evt)) { + return parent; + } + parent = parent->fParent; + } + return nullptr; +} + +SkView* SkView::sendQueryToParents(SkEvent* evt) { + SkView* parent = fParent; + + while (parent) { + if (parent->doQuery(evt)) { + return parent; + } + parent = parent->fParent; + } + return nullptr; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +SkView::F2BIter::F2BIter(const SkView* parent) { + fFirstChild = parent ? parent->fFirstChild : nullptr; + fChild = fFirstChild ? fFirstChild->fPrevSibling : nullptr; +} + +SkView* SkView::F2BIter::next() { + SkView* curr = fChild; + + if (fChild) { + if (fChild == fFirstChild) { + fChild = nullptr; + } else { + fChild = fChild->fPrevSibling; + } + } + return curr; +} + +SkView::B2FIter::B2FIter(const SkView* parent) { + fFirstChild = parent ? parent->fFirstChild : nullptr; + fChild = fFirstChild; +} + +SkView* SkView::B2FIter::next() { + SkView* curr = fChild; + + if (fChild) { + SkView* next = fChild->fNextSibling; + if (next == fFirstChild) + next = nullptr; + fChild = next; + } + return curr; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkView::validate() const { +// SkASSERT(this->getRefCnt() > 0 && this->getRefCnt() < 100); + if (fParent) { + SkASSERT(fNextSibling); + SkASSERT(fPrevSibling); + } else { + bool nextNull = nullptr == fNextSibling; + bool prevNull = nullptr == fNextSibling; + SkASSERT(nextNull == prevNull); + } +} + +static inline void show_if_nonzero(const char name[], SkScalar value) { + if (value) { + SkDebugf("%s=\"%g\"", name, value/65536.); + } +} + +static void tab(int level) { + for (int i = 0; i < level; i++) { + SkDebugf(" "); + } +} + +static void dumpview(const SkView* view, int level, bool recurse) { + tab(level); + + SkDebugf("<view"); + show_if_nonzero(" x", view->locX()); + show_if_nonzero(" y", view->locY()); + show_if_nonzero(" width", view->width()); + show_if_nonzero(" height", view->height()); + + if (recurse) { + SkView::B2FIter iter(view); + SkView* child; + bool noChildren = true; + + while ((child = iter.next()) != nullptr) { + if (noChildren) { + SkDebugf(">\n"); + } + noChildren = false; + dumpview(child, level + 1, true); + } + + if (!noChildren) { + tab(level); + SkDebugf("</view>\n"); + } else { + goto ONELINER; + } + } else { + ONELINER: + SkDebugf(" />\n"); + } +} + +void SkView::dump(bool recurse) const { + dumpview(this, 0, recurse); +} + +#endif diff --git a/gfx/skia/skia/src/views/SkViewPriv.cpp b/gfx/skia/skia/src/views/SkViewPriv.cpp new file mode 100644 index 000000000..7dbe5f1ca --- /dev/null +++ b/gfx/skia/skia/src/views/SkViewPriv.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkViewPriv.h" + +////////////////////////////////////////////////////////////////////// + +void SkView::Artist::draw(SkView* view, SkCanvas* canvas) +{ + SkASSERT(view && canvas); + this->onDraw(view, canvas); +} + +void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(node); + this->onInflate(dom, node); +} + +void SkView::Artist::onInflate(const SkDOM&, const SkDOM::Node*) +{ + // subclass should override this as needed +} + +SkView::Artist* SkView::getArtist() const +{ + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + SkASSERT(rec == nullptr || rec->fArtist != nullptr); + + return rec ? rec->fArtist : nullptr; +} + +SkView::Artist* SkView::setArtist(Artist* obj) +{ + if (obj == nullptr) + { + this->removeTagList(kViewArtist_SkTagList); + } + else // add/replace + { + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fArtist, obj); + else + this->addTagList(new Artist_SkTagList(obj)); + } + return obj; +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkView::Layout::layoutChildren(SkView* parent) +{ + SkASSERT(parent); + if (parent->width() > 0 && parent->height() > 0) + this->onLayoutChildren(parent); +} + +void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(node); + this->onInflate(dom, node); +} + +void SkView::Layout::onInflate(const SkDOM&, const SkDOM::Node*) +{ + // subclass should override this as needed +} + +SkView::Layout* SkView::getLayout() const +{ + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + SkASSERT(rec == nullptr || rec->fLayout != nullptr); + + return rec ? rec->fLayout : nullptr; +} + +SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow) +{ + if (obj == nullptr) + { + this->removeTagList(kViewLayout_SkTagList); + } + else // add/replace + { + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fLayout, obj); + else + this->addTagList(new Layout_SkTagList(obj)); + } + + if (invokeLayoutNow) + this->invokeLayout(); + + return obj; +} diff --git a/gfx/skia/skia/src/views/SkViewPriv.h b/gfx/skia/skia/src/views/SkViewPriv.h new file mode 100644 index 000000000..3b7645712 --- /dev/null +++ b/gfx/skia/skia/src/views/SkViewPriv.h @@ -0,0 +1,43 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkViewPriv_DEFINED +#define SkViewPriv_DEFINED + +#include "SkView.h" +#include "SkTagList.h" + +struct Layout_SkTagList : SkTagList { + SkView::Layout* fLayout; + + Layout_SkTagList(SkView::Layout* layout) + : SkTagList(kViewLayout_SkTagList), fLayout(layout) + { + SkASSERT(layout); + layout->ref(); + } + virtual ~Layout_SkTagList() + { + fLayout->unref(); + } +}; + +struct Artist_SkTagList : SkTagList { + SkView::Artist* fArtist; + + Artist_SkTagList(SkView::Artist* artist) + : SkTagList(kViewArtist_SkTagList), fArtist(artist) + { + SkASSERT(artist); + artist->ref(); + } + virtual ~Artist_SkTagList() + { + fArtist->unref(); + } +}; + +#endif diff --git a/gfx/skia/skia/src/views/SkWindow.cpp b/gfx/skia/skia/src/views/SkWindow.cpp new file mode 100644 index 000000000..6e1ebf7a7 --- /dev/null +++ b/gfx/skia/skia/src/views/SkWindow.cpp @@ -0,0 +1,361 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkSurface.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +#define SK_EventDelayInval "\xd" "n" "\xa" "l" + +SkWindow::SkWindow() + : fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType) + , fFocusView(nullptr) +{ + fClicks.reset(); + fWaitingOnInval = false; + fMatrix.reset(); + + fBitmap.allocN32Pixels(0, 0); +} + +SkWindow::~SkWindow() { + fClicks.deleteAll(); + fMenus.deleteAll(); +} + +sk_sp<SkSurface> SkWindow::makeSurface() { + const SkBitmap& bm = this->getBitmap(); + return SkSurface::MakeRasterDirect(bm.info(), bm.getPixels(), bm.rowBytes(), &fSurfaceProps); +} + +void SkWindow::setMatrix(const SkMatrix& matrix) { + if (fMatrix != matrix) { + fMatrix = matrix; + this->inval(nullptr); + } +} + +void SkWindow::preConcat(const SkMatrix& matrix) { + SkMatrix m; + m.setConcat(fMatrix, matrix); + this->setMatrix(m); +} + +void SkWindow::postConcat(const SkMatrix& matrix) { + SkMatrix m; + m.setConcat(matrix, fMatrix); + this->setMatrix(m); +} + +void SkWindow::resize(const SkImageInfo& info) { + if (fBitmap.info() != info) { + fBitmap.allocPixels(info); + this->inval(nullptr); + } + this->setSize(SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())); +} + +void SkWindow::resize(int width, int height) { + this->resize(fBitmap.info().makeWH(width, height)); +} + +void SkWindow::setColorType(SkColorType ct, sk_sp<SkColorSpace> cs) { + const SkImageInfo& info = fBitmap.info(); + this->resize(SkImageInfo::Make(info.width(), info.height(), ct, kPremul_SkAlphaType, cs)); +} + +bool SkWindow::handleInval(const SkRect* localR) { + SkIRect ir; + + if (localR) { + SkRect devR; + SkMatrix inverse; + if (!fMatrix.invert(&inverse)) { + return false; + } + fMatrix.mapRect(&devR, *localR); + devR.round(&ir); + } else { + ir.set(0, 0, + SkScalarRoundToInt(this->width()), + SkScalarRoundToInt(this->height())); + } + fDirtyRgn.op(ir, SkRegion::kUnion_Op); + + this->onHandleInval(ir); + return true; +} + +void SkWindow::forceInvalAll() { + fDirtyRgn.setRect(0, 0, + SkScalarCeilToInt(this->width()), + SkScalarCeilToInt(this->height())); +} + +#ifdef SK_SIMULATE_FAILED_MALLOC +extern bool gEnableControlledThrow; +#endif + +bool SkWindow::update(SkIRect* updateArea) { + if (!fDirtyRgn.isEmpty()) { + sk_sp<SkSurface> surface(this->makeSurface()); + SkCanvas* canvas = surface->getCanvas(); + + canvas->clipRegion(fDirtyRgn); + if (updateArea) { + *updateArea = fDirtyRgn.getBounds(); + } + + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(fMatrix); + + // empty this now, so we can correctly record any inval calls that + // might be made during the draw call. + fDirtyRgn.setEmpty(); + +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = true; +#endif +#ifdef SK_BUILD_FOR_WIN32 + //try { + this->draw(canvas); + //} + //catch (...) { + //} +#else + this->draw(canvas); +#endif +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = false; +#endif + + return true; + } + return false; +} + +bool SkWindow::handleChar(SkUnichar uni) { + if (this->onHandleChar(uni)) + return true; + + SkView* focus = this->getFocusView(); + if (focus == nullptr) + focus = this; + + SkEvent evt(SK_EventType_Unichar); + evt.setFast32(uni); + return focus->doEvent(evt); +} + +bool SkWindow::handleKey(SkKey key) { + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKey(key)) + return true; + + // send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == nullptr) + focus = this; + + SkEvent evt(SK_EventType_Key); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + + if (key == kUp_SkKey || key == kDown_SkKey) { + if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == nullptr) + this->onSetFocusView(nullptr); + return true; + } + return false; +} + +bool SkWindow::handleKeyUp(SkKey key) { + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKeyUp(key)) + return true; + + //send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == nullptr) + focus = this; + + //should this one be the same? + SkEvent evt(SK_EventType_KeyUp); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + return false; +} + +void SkWindow::addMenu(SkOSMenu* menu) { + *fMenus.append() = menu; + this->onAddMenu(menu); +} + +void SkWindow::setTitle(const char title[]) { + if (nullptr == title) { + title = ""; + } + fTitle.set(title); + this->onSetTitle(title); +} + +bool SkWindow::onEvent(const SkEvent& evt) { + if (evt.isType(SK_EventDelayInval)) { + for (SkRegion::Iterator iter(fDirtyRgn); !iter.done(); iter.next()) + this->onHandleInval(iter.rect()); + fWaitingOnInval = false; + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SkWindow::onGetFocusView(SkView** focus) const { + if (focus) + *focus = fFocusView; + return true; +} + +bool SkWindow::onSetFocusView(SkView* focus) { + if (fFocusView != focus) { + if (fFocusView) + fFocusView->onFocusChange(false); + fFocusView = focus; + if (focus) + focus->onFocusChange(true); + } + return true; +} + +void SkWindow::onHandleInval(const SkIRect&) { +} + +bool SkWindow::onHandleChar(SkUnichar) { + return false; +} + +bool SkWindow::onHandleKey(SkKey) { + return false; +} + +bool SkWindow::onHandleKeyUp(SkKey) { + return false; +} + +bool SkWindow::handleClick(int x, int y, Click::State state, void *owner, + unsigned modifierKeys) { + return this->onDispatchClick(x, y, state, owner, modifierKeys); +} + +bool SkWindow::onDispatchClick(int x, int y, Click::State state, + void* owner, unsigned modifierKeys) { + bool handled = false; + + // First, attempt to find an existing click with this owner. + int index = -1; + for (int i = 0; i < fClicks.count(); i++) { + if (owner == fClicks[i]->fOwner) { + index = i; + break; + } + } + + switch (state) { + case Click::kDown_State: { + if (index != -1) { + delete fClicks[index]; + fClicks.remove(index); + } + Click* click = this->findClickHandler(SkIntToScalar(x), + SkIntToScalar(y), modifierKeys); + + if (click) { + click->fOwner = owner; + *fClicks.append() = click; + SkView::DoClickDown(click, x, y, modifierKeys); + handled = true; + } + break; + } + case Click::kMoved_State: + if (index != -1) { + SkView::DoClickMoved(fClicks[index], x, y, modifierKeys); + handled = true; + } + break; + case Click::kUp_State: + if (index != -1) { + SkView::DoClickUp(fClicks[index], x, y, modifierKeys); + delete fClicks[index]; + fClicks.remove(index); + handled = true; + } + break; + default: + // Do nothing + break; + } + return handled; +} + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "gl/GrGLInterface.h" +#include "gl/GrGLUtil.h" +#include "SkGr.h" + +sk_sp<SkSurface> SkWindow::makeGpuBackedSurface(const AttachmentInfo& attachmentInfo, + const GrGLInterface* interface, + GrContext* grContext) { + GrBackendRenderTargetDesc desc; + desc.fWidth = SkScalarRoundToInt(this->width()); + desc.fHeight = SkScalarRoundToInt(this->height()); + if (0 == desc.fWidth || 0 == desc.fHeight) { + return nullptr; + } + + // TODO: Query the actual framebuffer for sRGB capable. However, to + // preserve old (fake-linear) behavior, we don't do this. Instead, rely + // on the flag (currently driven via 'C' mode in SampleApp). + // + // Also, we may not have real sRGB support (ANGLE, in particular), so check for + // that, and fall back to L32: + // + // ... and, if we're using a 10-bit/channel FB0, it doesn't do sRGB conversion on write, + // so pretend that it's non-sRGB 8888: + desc.fConfig = + grContext->caps()->srgbSupport() && + SkImageInfoIsGammaCorrect(info()) && + (attachmentInfo.fColorBits != 30) + ? kSkiaGamma8888_GrPixelConfig : kSkia8888_GrPixelConfig; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fSampleCnt = attachmentInfo.fSampleCount; + desc.fStencilBits = attachmentInfo.fStencilBits; + GrGLint buffer; + GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer); + desc.fRenderTargetHandle = buffer; + + sk_sp<SkColorSpace> colorSpace = + grContext->caps()->srgbSupport() && SkImageInfoIsGammaCorrect(info()) + ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : nullptr; + return SkSurface::MakeFromBackendRenderTarget(grContext, desc, colorSpace, &fSurfaceProps); +} + +#endif diff --git a/gfx/skia/skia/src/views/ios/SkOSWindow_iOS.mm b/gfx/skia/skia/src/views/ios/SkOSWindow_iOS.mm new file mode 100755 index 000000000..aa7d3759b --- /dev/null +++ b/gfx/skia/skia/src/views/ios/SkOSWindow_iOS.mm @@ -0,0 +1,72 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import <UIKit/UIKit.h> +#include "SkCanvas.h" +#include "SkGraphics.h" +#import "SkEventNotifier.h" +#include "SkOSMenu.h" +#include "SkTime.h" +#include "SkTypes.h" +#import "SkUIView.h" +#include "SkWindow.h" + +#define kINVAL_UIVIEW_EventType "inval-uiview" + +SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd) { + fInvalEventIsPending = false; + fNotifier = [[SkEventNotifier alloc] init]; +} +SkOSWindow::~SkOSWindow() { + [(SkEventNotifier*)fNotifier release]; +} + +void SkOSWindow::onHandleInval(const SkIRect& r) { + if (!fInvalEventIsPending) { + fInvalEventIsPending = true; + (new SkEvent(kINVAL_UIVIEW_EventType, this->getSinkID()))->post(); + } +} + +bool SkOSWindow::onEvent(const SkEvent& evt) { + if (evt.isType(kINVAL_UIVIEW_EventType)) { + fInvalEventIsPending = false; + const SkIRect& r = this->getDirtyBounds(); + [(SkUIView*)fHWND postInvalWithRect:&r]; + return true; + } + if ([(SkUIView*)fHWND onHandleEvent:evt]) { + return true; + } + return this->INHERITED::onEvent(evt); +} + +void SkOSWindow::onSetTitle(const char title[]) { + [(SkUIView*)fHWND setSkTitle:title]; +} + +void SkOSWindow::onAddMenu(const SkOSMenu* menu) { + [(SkUIView*)fHWND onAddMenu:menu]; +} + +void SkOSWindow::onUpdateMenu(SkOSMenu* menu) { + [(SkUIView*)fHWND onUpdateMenu:menu]; +} + +bool SkOSWindow::attach(SkBackEndTypes /* attachType */, + int /* msaaSampleCount */, + bool /* deepColor */, + AttachmentInfo* info) { + [(SkUIView*)fHWND getAttachmentInfo:info]; + bool success = true; + return success; +} + +void SkOSWindow::release() {} + +void SkOSWindow::present() { +} diff --git a/gfx/skia/skia/src/views/mac/SkEventNotifier.h b/gfx/skia/skia/src/views/mac/SkEventNotifier.h new file mode 100644 index 000000000..ea6bbf1e3 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkEventNotifier.h @@ -0,0 +1,13 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#import <Foundation/Foundation.h> + +@interface SkEventNotifier : NSObject +- (void)receiveSkEvent:(NSNotification*)notification; ++ (void)postTimedSkEvent:(NSTimeInterval)ti; ++ (void)timerFireMethod:(NSTimer*)theTimer; +@end diff --git a/gfx/skia/skia/src/views/mac/SkEventNotifier.mm b/gfx/skia/skia/src/views/mac/SkEventNotifier.mm new file mode 100644 index 000000000..0864380d9 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkEventNotifier.mm @@ -0,0 +1,68 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkEventNotifier.h" +#include "SkEvent.h" +#define SkEventClass @"SkEvenClass" +@implementation SkEventNotifier +- (id)init { + self = [super init]; + if (self) { + //Register as an observer for SkEventClass events and call + //receiveSkEvent: upon receiving the event + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(receiveSkEvent:) + name:SkEventClass + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +-(BOOL) acceptsFirstResponder { + return YES; +} + +//SkEvent Handers +- (void)receiveSkEvent:(NSNotification *)notification { + if(SkEvent::ProcessEvent()) + SkEvent::SignalNonEmptyQueue(); +} + ++ (void)postTimedSkEvent:(NSTimeInterval)timeInterval { + [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self + selector:@selector(timerFireMethod:) + userInfo:nil repeats:NO]; +} + ++ (void)timerFireMethod:(NSTimer*)theTimer { + SkEvent::ServiceQueueTimer(); +} + +@end +//////////////////////////////////////////////////////////////////////////////// +void SkEvent::SignalNonEmptyQueue() { + //post a SkEventClass event to the default notification queue + NSNotification* notification = [NSNotification notificationWithName:SkEventClass object:nil]; + [[NSNotificationQueue defaultQueue] enqueueNotification:notification + postingStyle:NSPostWhenIdle + coalesceMask:NSNotificationNoCoalescing + forModes:nil]; +} + +void SkEvent::SignalQueueTimer(SkMSec delay) { + if (delay) { + //Convert to seconds + NSTimeInterval ti = delay/(float)SK_MSec1; + [SkEventNotifier postTimedSkEvent:ti]; + } +} diff --git a/gfx/skia/skia/src/views/mac/SkNSView.h b/gfx/skia/skia/src/views/mac/SkNSView.h new file mode 100644 index 000000000..779c51b7e --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkNSView.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import <QuartzCore/QuartzCore.h> +#import <Cocoa/Cocoa.h> +#import "SkWindow.h" +class SkEvent; +@class SkNSView; + +@protocol SkNSViewOptionsDelegate <NSObject> +@optional +// Called when the view needs to handle adding an SkOSMenu +- (void) view:(SkNSView*)view didAddMenu:(const SkOSMenu*)menu; +- (void) view:(SkNSView*)view didUpdateMenu:(const SkOSMenu*)menu; +@end + +@interface SkNSView : NSView { + BOOL fRedrawRequestPending; + + NSString* fTitle; + SkOSWindow* fWind; +#if SK_SUPPORT_GPU + NSOpenGLContext* fGLContext; +#endif + id<SkNSViewOptionsDelegate> fOptionsDelegate; +} + +@property (nonatomic, readonly) SkOSWindow *fWind; +@property (nonatomic, retain) NSString* fTitle; +#if SK_SUPPORT_GPU +@property (nonatomic, retain) NSOpenGLContext* fGLContext; +#endif +@property (nonatomic, assign) id<SkNSViewOptionsDelegate> fOptionsDelegate; + +- (id)initWithDefaults; +- (void)setUpWindow; +- (void)resizeSkView:(NSSize)newSize; +- (void)setSkTitle:(const char*)title; +- (void)onAddMenu:(const SkOSMenu*)menu; +- (void)onUpdateMenu:(const SkOSMenu*)menu; +- (void)postInvalWithRect:(const SkIRect*)rectOrNil; +- (BOOL)onHandleEvent:(const SkEvent&)event; + +- (bool)attach:(SkOSWindow::SkBackEndTypes)attachType withMSAASampleCount:(int) sampleCount andGetInfo:(SkOSWindow::AttachmentInfo*) info; +- (void)detach; +- (void)present; + +- (void)setVSync:(bool)enable; + +- (void)freeNativeWind; + +@end diff --git a/gfx/skia/skia/src/views/mac/SkNSView.mm b/gfx/skia/skia/src/views/mac/SkNSView.mm new file mode 100644 index 000000000..ce2938237 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkNSView.mm @@ -0,0 +1,430 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkNSView.h" +#include "SkCanvas.h" +#include "SkSurface.h" +#include "SkCGUtils.h" +#include "SkEvent.h" +static_assert(SK_SUPPORT_GPU, "not_implemented_for_non_gpu_build"); +#include <OpenGL/gl.h> + +//#define FORCE_REDRAW +// Can be dropped when we no longer support 10.6. +#define RETINA_API_AVAILABLE (defined(MAC_OS_X_VERSION_10_7) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) +@implementation SkNSView +@synthesize fWind, fTitle, fOptionsDelegate, fGLContext; + +- (id)initWithCoder:(NSCoder*)coder { + if ((self = [super initWithCoder:coder])) { + self = [self initWithDefaults]; + [self setUpWindow]; + } + return self; +} + +- (id)initWithFrame:(NSRect)frameRect { + if ((self = [super initWithFrame:frameRect])) { + self = [self initWithDefaults]; + [self setUpWindow]; + } + return self; +} + +- (id)initWithDefaults { +#if RETINA_API_AVAILABLE + [self setWantsBestResolutionOpenGLSurface:YES]; +#endif + fRedrawRequestPending = false; + fWind = NULL; + return self; +} + +- (void)setUpWindow { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(backingPropertiesChanged:) + name:@"NSWindowDidChangeBackingPropertiesNotification" + object:[self window]]; + if (fWind) { + fWind->setVisibleP(true); + NSSize size = self.frame.size; +#if RETINA_API_AVAILABLE + size = [self convertSizeToBacking:self.frame.size]; +#endif + fWind->resize((int) size.width, (int) size.height); + [[self window] setAcceptsMouseMovedEvents:YES]; + } +} + +-(BOOL) isFlipped { + return YES; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (float)scaleFactor { + NSWindow *window = [self window]; +#if RETINA_API_AVAILABLE + if (window) { + return [window backingScaleFactor]; + } + return [[NSScreen mainScreen] backingScaleFactor]; +#else + if (window) { + return [window userSpaceScaleFactor]; + } + return [[NSScreen mainScreen] userSpaceScaleFactor]; +#endif +} + +- (void)backingPropertiesChanged:(NSNotification *)notification { + CGFloat oldBackingScaleFactor = (CGFloat)[ + [notification.userInfo objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue + ]; + CGFloat newBackingScaleFactor = [self scaleFactor]; + if (oldBackingScaleFactor == newBackingScaleFactor) { + return; + } + + // TODO: need a better way to force a refresh (that works). + // [fGLContext update] does not appear to update if the point size has not changed, + // even if the backing size has changed. + [self setFrameSize:NSMakeSize(self.frame.size.width + 1, self.frame.size.height + 1)]; +} + +- (void)resizeSkView:(NSSize)newSize { +#if RETINA_API_AVAILABLE + newSize = [self convertSizeToBacking:newSize]; +#endif + if (fWind && (fWind->width() != newSize.width || fWind->height() != newSize.height)) { + fWind->resize((int) newSize.width, (int) newSize.height); + if (fGLContext) { + glClear(GL_STENCIL_BUFFER_BIT); + [fGLContext update]; + } + } +} + +- (void) setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self resizeSkView:newSize]; +} + +- (void)dealloc { + [self freeNativeWind]; + self.fGLContext = nil; + self.fTitle = nil; + [super dealloc]; +} + +- (void)freeNativeWind { + delete fWind; + fWind = nil; +} + +//////////////////////////////////////////////////////////////////////////////// + +- (void)drawSkia { + fRedrawRequestPending = false; + if (fWind) { + sk_sp<SkSurface> surface(fWind->makeSurface()); + fWind->draw(surface->getCanvas()); +#ifdef FORCE_REDRAW + fWind->inval(NULL); +#endif + } +} + +- (void)setSkTitle:(const char *)title { + self.fTitle = [NSString stringWithUTF8String:title]; + [[self window] setTitle:self.fTitle]; +} + +- (BOOL)onHandleEvent:(const SkEvent&)evt { + return false; +} + +#include "SkOSMenu.h" +- (void)onAddMenu:(const SkOSMenu*)menu { + [self.fOptionsDelegate view:self didAddMenu:menu]; +} + +- (void)onUpdateMenu:(const SkOSMenu*)menu { + [self.fOptionsDelegate view:self didUpdateMenu:menu]; +} + +- (void)postInvalWithRect:(const SkIRect*)r { + if (!fRedrawRequestPending) { + fRedrawRequestPending = true; + [self setNeedsDisplay:YES]; + [self performSelector:@selector(drawSkia) withObject:nil afterDelay:0]; + } +} +/////////////////////////////////////////////////////////////////////////////// + +#include "SkKey.h" +enum { + SK_MacReturnKey = 36, + SK_MacDeleteKey = 51, + SK_MacEndKey = 119, + SK_MacLeftKey = 123, + SK_MacRightKey = 124, + SK_MacDownKey = 125, + SK_MacUpKey = 126, + SK_Mac0Key = 0x52, + SK_Mac1Key = 0x53, + SK_Mac2Key = 0x54, + SK_Mac3Key = 0x55, + SK_Mac4Key = 0x56, + SK_Mac5Key = 0x57, + SK_Mac6Key = 0x58, + SK_Mac7Key = 0x59, + SK_Mac8Key = 0x5b, + SK_Mac9Key = 0x5c +}; + +static SkKey raw2key(UInt32 raw) +{ + static const struct { + UInt32 fRaw; + SkKey fKey; + } gKeys[] = { + { SK_MacUpKey, kUp_SkKey }, + { SK_MacDownKey, kDown_SkKey }, + { SK_MacLeftKey, kLeft_SkKey }, + { SK_MacRightKey, kRight_SkKey }, + { SK_MacReturnKey, kOK_SkKey }, + { SK_MacDeleteKey, kBack_SkKey }, + { SK_MacEndKey, kEnd_SkKey }, + { SK_Mac0Key, k0_SkKey }, + { SK_Mac1Key, k1_SkKey }, + { SK_Mac2Key, k2_SkKey }, + { SK_Mac3Key, k3_SkKey }, + { SK_Mac4Key, k4_SkKey }, + { SK_Mac5Key, k5_SkKey }, + { SK_Mac6Key, k6_SkKey }, + { SK_Mac7Key, k7_SkKey }, + { SK_Mac8Key, k8_SkKey }, + { SK_Mac9Key, k9_SkKey } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) + if (gKeys[i].fRaw == raw) + return gKeys[i].fKey; + return kNONE_SkKey; +} + +- (void)keyDown:(NSEvent *)event { + if (NULL == fWind) + return; + + SkKey key = raw2key([event keyCode]); + if (kNONE_SkKey != key) + fWind->handleKey(key); + else{ + unichar c = [[event characters] characterAtIndex:0]; + fWind->handleChar((SkUnichar)c); + } +} + +- (void)keyUp:(NSEvent *)event { + if (NULL == fWind) + return; + + SkKey key = raw2key([event keyCode]); + if (kNONE_SkKey != key) + fWind->handleKeyUp(key); + // else + // unichar c = [[event characters] characterAtIndex:0]; +} + +static const struct { + unsigned fNSModifierMask; + unsigned fSkModifierMask; +} gModifierMasks[] = { + { NSAlphaShiftKeyMask, kShift_SkModifierKey }, + { NSShiftKeyMask, kShift_SkModifierKey }, + { NSControlKeyMask, kControl_SkModifierKey }, + { NSAlternateKeyMask, kOption_SkModifierKey }, + { NSCommandKeyMask, kCommand_SkModifierKey }, +}; + +static unsigned convertNSModifiersToSk(NSUInteger nsModi) { + unsigned skModi = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(gModifierMasks); ++i) { + if (nsModi & gModifierMasks[i].fNSModifierMask) { + skModi |= gModifierMasks[i].fSkModifierMask; + } + } + return skModi; +} + +- (void)mouseDown:(NSEvent *)event { + NSPoint p = [event locationInWindow]; + unsigned modi = convertNSModifiersToSk([event modifierFlags]); + + if ([self mouse:p inRect:[self bounds]] && fWind) { + NSPoint loc = [self convertPoint:p fromView:nil]; +#if RETINA_API_AVAILABLE + loc = [self convertPointToBacking:loc]; //y-up + loc.y = -loc.y; +#endif + fWind->handleClick((int) loc.x, (int) loc.y, + SkView::Click::kDown_State, self, modi); + } +} + +- (void)mouseDragged:(NSEvent *)event { + NSPoint p = [event locationInWindow]; + unsigned modi = convertNSModifiersToSk([event modifierFlags]); + + if ([self mouse:p inRect:[self bounds]] && fWind) { + NSPoint loc = [self convertPoint:p fromView:nil]; +#if RETINA_API_AVAILABLE + loc = [self convertPointToBacking:loc]; //y-up + loc.y = -loc.y; +#endif + fWind->handleClick((int) loc.x, (int) loc.y, + SkView::Click::kMoved_State, self, modi); + } +} + +- (void)mouseMoved:(NSEvent *)event { + NSPoint p = [event locationInWindow]; + unsigned modi = convertNSModifiersToSk([event modifierFlags]); + + if ([self mouse:p inRect:[self bounds]] && fWind) { + NSPoint loc = [self convertPoint:p fromView:nil]; +#if RETINA_API_AVAILABLE + loc = [self convertPointToBacking:loc]; //y-up + loc.y = -loc.y; +#endif + fWind->handleClick((int) loc.x, (int) loc.y, + SkView::Click::kMoved_State, self, modi); + } +} + +- (void)mouseUp:(NSEvent *)event { + NSPoint p = [event locationInWindow]; + unsigned modi = convertNSModifiersToSk([event modifierFlags]); + + if ([self mouse:p inRect:[self bounds]] && fWind) { + NSPoint loc = [self convertPoint:p fromView:nil]; +#if RETINA_API_AVAILABLE + loc = [self convertPointToBacking:loc]; //y-up + loc.y = -loc.y; +#endif + fWind->handleClick((int) loc.x, (int) loc.y, + SkView::Click::kUp_State, self, modi); + } +} + +/////////////////////////////////////////////////////////////////////////////// +#include <OpenGL/OpenGL.h> + +static CGLContextObj createGLContext(int msaaSampleCount) { + GLint major, minor; + CGLGetVersion(&major, &minor); + + static const CGLPixelFormatAttribute attributes[] = { + kCGLPFAStencilSize, (CGLPixelFormatAttribute) 8, + kCGLPFAAccelerated, + kCGLPFADoubleBuffer, + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, + (CGLPixelFormatAttribute)0 + }; + + CGLPixelFormatObj format; + GLint npix = 0; + if (msaaSampleCount > 0) { + static const int kAttributeCount = SK_ARRAY_COUNT(attributes); + CGLPixelFormatAttribute msaaAttributes[kAttributeCount + 5]; + memcpy(msaaAttributes, attributes, sizeof(attributes)); + SkASSERT(0 == msaaAttributes[kAttributeCount - 1]); + msaaAttributes[kAttributeCount - 1] = kCGLPFASampleBuffers; + msaaAttributes[kAttributeCount + 0] = (CGLPixelFormatAttribute)1; + msaaAttributes[kAttributeCount + 1] = kCGLPFAMultisample; + msaaAttributes[kAttributeCount + 2] = kCGLPFASamples; + msaaAttributes[kAttributeCount + 3] = + (CGLPixelFormatAttribute)msaaSampleCount; + msaaAttributes[kAttributeCount + 4] = (CGLPixelFormatAttribute)0; + CGLChoosePixelFormat(msaaAttributes, &format, &npix); + } + if (!npix) { + CGLChoosePixelFormat(attributes, &format, &npix); + } + CGLContextObj ctx; + CGLCreateContext(format, NULL, &ctx); + CGLDestroyPixelFormat(format); + + static const GLint interval = 1; + CGLSetParameter(ctx, kCGLCPSwapInterval, &interval); + CGLSetCurrentContext(ctx); + return ctx; +} + +- (void)viewDidMoveToWindow { + [super viewDidMoveToWindow]; + + //Attaching view to fGLContext requires that the view to be part of a window, + //and that the NSWindow instance must have a CoreGraphics counterpart (or + //it must NOT be deferred or should have been on screen at least once) + if ([fGLContext view] != self && nil != self.window) { + [fGLContext setView:self]; + } +} +- (bool)attach:(SkOSWindow::SkBackEndTypes)attachType + withMSAASampleCount:(int) sampleCount + andGetInfo:(SkOSWindow::AttachmentInfo*) info { + if (nil == fGLContext) { + CGLContextObj ctx = createGLContext(sampleCount); + SkASSERT(ctx); + fGLContext = [[NSOpenGLContext alloc] initWithCGLContextObj:ctx]; + CGLReleaseContext(ctx); + if (NULL == fGLContext) { + return false; + } + [fGLContext setView:self]; + } + + [fGLContext makeCurrentContext]; + CGLPixelFormatObj format = CGLGetPixelFormat((CGLContextObj)[fGLContext CGLContextObj]); + CGLDescribePixelFormat(format, 0, kCGLPFASamples, &info->fSampleCount); + CGLDescribePixelFormat(format, 0, kCGLPFAStencilSize, &info->fStencilBits); + NSSize size = self.bounds.size; +#if RETINA_API_AVAILABLE + size = [self convertSizeToBacking:size]; +#endif + glViewport(0, 0, (int) size.width, (int) size.height); + glClearColor(0, 0, 0, 0); + glClearStencil(0); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + return true; +} + +- (void)detach { + [fGLContext release]; + fGLContext = nil; +} + +- (void)present { + if (nil != fGLContext) { + [fGLContext flushBuffer]; + } +} + +- (void)setVSync:(bool)enable { + if (fGLContext) { + GLint interval = enable ? 1 : 0; + CGLContextObj ctx = (CGLContextObj)[fGLContext CGLContextObj]; + CGLSetParameter(ctx, kCGLCPSwapInterval, &interval); + } +} +@end diff --git a/gfx/skia/skia/src/views/mac/SkOSWindow_Mac.mm b/gfx/skia/skia/src/views/mac/SkOSWindow_Mac.mm new file mode 100644 index 000000000..faf1bbafd --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkOSWindow_Mac.mm @@ -0,0 +1,95 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#if defined(SK_BUILD_FOR_MAC) + +#import <Cocoa/Cocoa.h> +#include "SkOSWindow_Mac.h" +#include "SkOSMenu.h" +#include "SkTypes.h" +#include "SkWindow.h" +#import "SkNSView.h" +#import "SkEventNotifier.h" +#define kINVAL_NSVIEW_EventType "inval-nsview" + +static_assert(SK_SUPPORT_GPU, "not_implemented_for_non_gpu_build"); + +SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd) { + fInvalEventIsPending = false; + fGLContext = NULL; + fNotifier = [[SkEventNotifier alloc] init]; +} +SkOSWindow::~SkOSWindow() { + [(SkEventNotifier*)fNotifier release]; +} + +void SkOSWindow::onHandleInval(const SkIRect& r) { + if (!fInvalEventIsPending) { + fInvalEventIsPending = true; + (new SkEvent(kINVAL_NSVIEW_EventType, this->getSinkID()))->post(); + } +} + +bool SkOSWindow::onEvent(const SkEvent& evt) { + if (evt.isType(kINVAL_NSVIEW_EventType)) { + fInvalEventIsPending = false; + const SkIRect& r = this->getDirtyBounds(); + [(SkNSView*)fHWND postInvalWithRect:&r]; + [(NSOpenGLContext*)fGLContext update]; + return true; + } + if ([(SkNSView*)fHWND onHandleEvent:evt]) { + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner, + unsigned modi) { + return this->INHERITED::onDispatchClick(x, y, state, owner, modi); +} + +void SkOSWindow::onSetTitle(const char title[]) { + [(SkNSView*)fHWND setSkTitle:title]; +} + +void SkOSWindow::onAddMenu(const SkOSMenu* menu) { + [(SkNSView*)fHWND onAddMenu:menu]; +} + +void SkOSWindow::onUpdateMenu(const SkOSMenu* menu) { + [(SkNSView*)fHWND onUpdateMenu:menu]; +} + +bool SkOSWindow::attach(SkBackEndTypes attachType, int sampleCount, bool /*deepColor*/, + AttachmentInfo* info) { + return [(SkNSView*)fHWND attach:attachType withMSAASampleCount:sampleCount andGetInfo:info]; +} + +void SkOSWindow::release() { + [(SkNSView*)fHWND detach]; +} + +void SkOSWindow::present() { + [(SkNSView*)fHWND present]; +} + +void SkOSWindow::closeWindow() { + [[(SkNSView*)fHWND window] close]; +} + +void SkOSWindow::setVsync(bool enable) { + [(SkNSView*)fHWND setVSync:enable]; +} + +bool SkOSWindow::makeFullscreen() { + [(SkNSView*)fHWND enterFullScreenMode:[NSScreen mainScreen] withOptions:nil]; + return true; +} + + +#endif diff --git a/gfx/skia/skia/src/views/mac/SkOptionsTableView.h b/gfx/skia/skia/src/views/mac/SkOptionsTableView.h new file mode 100644 index 000000000..8fa03d1fc --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkOptionsTableView.h @@ -0,0 +1,39 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import <Cocoa/Cocoa.h> +#import "SkNSView.h" +#import "SkOSMenu.h" +#import "SkEvent.h" +@interface SkOptionItem : NSObject { + NSCell* fCell; + const SkOSMenu::Item* fItem; +} +@property (nonatomic, assign) const SkOSMenu::Item* fItem; +@property (nonatomic, retain) NSCell* fCell; +@end + +@interface SkOptionsTableView : NSTableView <SkNSViewOptionsDelegate, NSTableViewDelegate, NSTableViewDataSource> { + NSMutableArray* fItems; + const SkTDArray<SkOSMenu*>* fMenus; + BOOL fShowKeys; +} +@property (nonatomic, retain) NSMutableArray* fItems; + +- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus; +- (void)updateMenu:(const SkOSMenu*)menu; +- (void)loadMenu:(const SkOSMenu*)menu; +- (IBAction)toggleKeyEquivalents:(id)sender; + +- (NSCell*)createAction; +- (NSCell*)createList:(NSArray*)items current:(int)index; +- (NSCell*)createSlider:(float)value min:(float)min max:(float)max; +- (NSCell*)createSwitch:(BOOL)state; +- (NSCell*)createTextField:(NSString*)placeHolder; +- (NSCell*)createTriState:(NSCellStateValue)state; + +@end diff --git a/gfx/skia/skia/src/views/mac/SkOptionsTableView.mm b/gfx/skia/skia/src/views/mac/SkOptionsTableView.mm new file mode 100644 index 000000000..b4cdbf411 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkOptionsTableView.mm @@ -0,0 +1,297 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkOptionsTableView.h" +#import "SkTextFieldCell.h" +@implementation SkOptionItem +@synthesize fCell, fItem; +- (void)dealloc { + [fCell release]; + [super dealloc]; +} +@end + +@implementation SkOptionsTableView +@synthesize fItems; + +- (id)initWithCoder:(NSCoder*)coder { + if ((self = [super initWithCoder:coder])) { + self.dataSource = self; + self.delegate = self; + fMenus = NULL; + fShowKeys = YES; + [self setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone]; + self.fItems = [NSMutableArray array]; + } + return self; +} + +- (void)dealloc { + self.fItems = nil; + [super dealloc]; +} + +- (void) view:(SkNSView*)view didAddMenu:(const SkOSMenu*)menu {} +- (void) view:(SkNSView*)view didUpdateMenu:(const SkOSMenu*)menu { + [self updateMenu:menu]; +} + +- (IBAction)toggleKeyEquivalents:(id)sender { + fShowKeys = !fShowKeys; + NSMenuItem* item = (NSMenuItem*)sender; + [item setState:fShowKeys]; + [self reloadData]; +} + +- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus { + fMenus = menus; + for (int i = 0; i < fMenus->count(); ++i) { + [self loadMenu:(*fMenus)[i]]; + } +} + +- (void)updateMenu:(const SkOSMenu*)menu { + // the first menu is always assumed to be the static, the second is + // repopulated every time over and over again + + // seems pretty weird that we have to get rid of the const'ness here, + // but trying to propagate the const'ness through all the way to the fMenus + // vector was a non-starter. + + int menuIndex = fMenus->find(const_cast<SkOSMenu *>(menu)); + if (menuIndex >= 0 && menuIndex < fMenus->count()) { + NSUInteger first = 0; + for (int i = 0; i < menuIndex; ++i) { + first += (*fMenus)[i]->getCount(); + } + [fItems removeObjectsInRange:NSMakeRange(first, [fItems count] - first)]; + [self loadMenu:menu]; + } + [self reloadData]; +} + +- (NSCellStateValue)triStateToNSState:(SkOSMenu::TriState)state { + if (SkOSMenu::kOnState == state) + return NSOnState; + else if (SkOSMenu::kOffState == state) + return NSOffState; + else + return NSMixedState; +} + +- (void)loadMenu:(const SkOSMenu*)menu { + const SkOSMenu::Item* menuitems[menu->getCount()]; + menu->getItems(menuitems); + for (int i = 0; i < menu->getCount(); ++i) { + const SkOSMenu::Item* item = menuitems[i]; + SkOptionItem* option = [[SkOptionItem alloc] init]; + option.fItem = item; + + if (SkOSMenu::kList_Type == item->getType()) { + int index = 0, count = 0; + SkOSMenu::FindListItemCount(*item->getEvent(), &count); + NSMutableArray* optionstrs = [[NSMutableArray alloc] initWithCapacity:count]; + SkAutoTDeleteArray<SkString> ada(new SkString[count]); + SkString* options = ada.get(); + SkOSMenu::FindListItems(*item->getEvent(), options); + for (int i = 0; i < count; ++i) + [optionstrs addObject:[NSString stringWithUTF8String:options[i].c_str()]]; + SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &index); + option.fCell = [self createList:optionstrs current:index]; + [optionstrs release]; + } + else { + bool state = false; + SkString str; + SkOSMenu::TriState tristate; + switch (item->getType()) { + case SkOSMenu::kAction_Type: + option.fCell = [self createAction]; + break; + case SkOSMenu::kSlider_Type: + SkScalar min, max, value; + SkOSMenu::FindSliderValue(*item->getEvent(), item->getSlotName(), &value); + SkOSMenu::FindSliderMin(*item->getEvent(), &min); + SkOSMenu::FindSliderMax(*item->getEvent(), &max); + option.fCell = [self createSlider:value + min:min + max:max]; + break; + case SkOSMenu::kSwitch_Type: + SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state); + option.fCell = [self createSwitch:(BOOL)state]; + break; + case SkOSMenu::kTriState_Type: + SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate); + option.fCell = [self createTriState:[self triStateToNSState:tristate]]; + break; + case SkOSMenu::kTextField_Type: + SkOSMenu::FindText(*item->getEvent(),item->getSlotName(), &str); + option.fCell = [self createTextField:[NSString stringWithUTF8String:str.c_str()]]; + break; + default: + break; + } + } + [fItems addObject:option]; + [option release]; + } +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return [self.fItems count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSInteger columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]]; + if (columnIndex == 0) { + const SkOSMenu::Item* item = ((SkOptionItem*)[fItems objectAtIndex:row]).fItem; + NSString* label = [NSString stringWithUTF8String:item->getLabel()]; + if (fShowKeys) + return [NSString stringWithFormat:@"%@ (%c)", label, item->getKeyEquivalent()]; + else + return label; + } + else + return nil; +} + +- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + if (tableColumn) { + NSInteger columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]]; + if (columnIndex == 1) + return [((SkOptionItem*)[fItems objectAtIndex:row]).fCell copy]; + else + return [[[SkTextFieldCell alloc] init] autorelease]; + } + return nil; +} + +- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSInteger columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]]; + if (columnIndex == 1) { + SkOptionItem* option = (SkOptionItem*)[self.fItems objectAtIndex:row]; + NSCell* storedCell = option.fCell; + const SkOSMenu::Item* item = option.fItem; + switch (item->getType()) { + case SkOSMenu::kAction_Type: + break; + case SkOSMenu::kList_Type: + [cell selectItemAtIndex:[(NSPopUpButtonCell*)storedCell indexOfSelectedItem]]; + break; + case SkOSMenu::kSlider_Type: + [cell setFloatValue:[storedCell floatValue]]; + break; + case SkOSMenu::kSwitch_Type: + [cell setState:[(NSButtonCell*)storedCell state]]; + break; + case SkOSMenu::kTextField_Type: + if ([[storedCell stringValue] length] > 0) + [cell setStringValue:[storedCell stringValue]]; + break; + case SkOSMenu::kTriState_Type: + [cell setState:[(NSButtonCell*)storedCell state]]; + break; + default: + break; + } + } + else { + [(SkTextFieldCell*)cell setEditable:NO]; + } +} + +- (void)tableView:(NSTableView *)tableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSInteger columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]]; + if (columnIndex == 1) { + SkOptionItem* option = (SkOptionItem*)[self.fItems objectAtIndex:row]; + NSCell* cell = option.fCell; + const SkOSMenu::Item* item = option.fItem; + switch (item->getType()) { + case SkOSMenu::kAction_Type: + item->postEvent(); + break; + case SkOSMenu::kList_Type: + [(NSPopUpButtonCell*)cell selectItemAtIndex:[anObject intValue]]; + item->setInt([anObject intValue]); + break; + case SkOSMenu::kSlider_Type: + [cell setFloatValue:[anObject floatValue]]; + item->setScalar([anObject floatValue]); + break; + case SkOSMenu::kSwitch_Type: + [cell setState:[anObject boolValue]]; + item->setBool([anObject boolValue]); + break; + case SkOSMenu::kTextField_Type: + if ([anObject length] > 0) { + [cell setStringValue:anObject]; + item->setString([anObject UTF8String]); + } + break; + case SkOSMenu::kTriState_Type: + [cell setState:[anObject intValue]]; + item->setTriState((SkOSMenu::TriState)[anObject intValue]); + break; + default: + break; + } + item->postEvent(); + } +} + +- (NSCell*)createAction{ + NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease]; + [cell setTitle:@""]; + [cell setButtonType:NSMomentaryPushInButton]; + [cell setBezelStyle:NSSmallSquareBezelStyle]; + return cell; +} + +- (NSCell*)createList:(NSArray*)items current:(int)index { + NSPopUpButtonCell* cell = [[[NSPopUpButtonCell alloc] init] autorelease]; + [cell addItemsWithTitles:items]; + [cell selectItemAtIndex:index]; + [cell setArrowPosition:NSPopUpArrowAtBottom]; + [cell setBezelStyle:NSSmallSquareBezelStyle]; + return cell; +} + +- (NSCell*)createSlider:(float)value min:(float)min max:(float)max { + NSSliderCell* cell = [[[NSSliderCell alloc] init] autorelease]; + [cell setFloatValue:value]; + [cell setMinValue:min]; + [cell setMaxValue:max]; + return cell; +} + +- (NSCell*)createSwitch:(BOOL)state { + NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease]; + [cell setState:state]; + [cell setTitle:@""]; + [cell setButtonType:NSSwitchButton]; + return cell; +} + +- (NSCell*)createTextField:(NSString*)placeHolder { + SkTextFieldCell* cell = [[[SkTextFieldCell alloc] init] autorelease]; + [cell setEditable:YES]; + [cell setStringValue:@""]; + [cell setPlaceholderString:placeHolder]; + return cell; +} + +- (NSCell*)createTriState:(NSCellStateValue)state { + NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease]; + [cell setAllowsMixedState:TRUE]; + [cell setTitle:@""]; + [cell setState:(NSInteger)state]; + [cell setButtonType:NSSwitchButton]; + return cell; +} +@end diff --git a/gfx/skia/skia/src/views/mac/SkSampleNSView.h b/gfx/skia/skia/src/views/mac/SkSampleNSView.h new file mode 100644 index 000000000..b7da9fd55 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkSampleNSView.h @@ -0,0 +1,11 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkNSView.h" +@interface SkSampleNSView : SkNSView +- (id)initWithDefaults; +@end diff --git a/gfx/skia/skia/src/views/mac/SkSampleNSView.mm b/gfx/skia/skia/src/views/mac/SkSampleNSView.mm new file mode 100644 index 000000000..7c0fc5907 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkSampleNSView.mm @@ -0,0 +1,31 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkSampleNSView.h" +#include "SampleApp.h" +#include <crt_externs.h> +@implementation SkSampleNSView + +- (id)initWithDefaults { + if ((self = [super initWithDefaults])) { + fWind = new SampleWindow(self, *_NSGetArgc(), *_NSGetArgv(), NULL); + } + return self; +} + +- (void)swipeWithEvent:(NSEvent *)event { + CGFloat x = [event deltaX]; + if (x < 0) + ((SampleWindow*)fWind)->previousSample(); + else if (x > 0) + ((SampleWindow*)fWind)->nextSample(); + else + ((SampleWindow*)fWind)->showOverview(); +} + +@end diff --git a/gfx/skia/skia/src/views/mac/SkTextFieldCell.h b/gfx/skia/skia/src/views/mac/SkTextFieldCell.h new file mode 100644 index 000000000..dfca7ae69 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkTextFieldCell.h @@ -0,0 +1,14 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#import <Cocoa/Cocoa.h> +//A text field cell that has vertically centered text +@interface SkTextFieldCell : NSTextFieldCell { + BOOL selectingOrEditing; +} +@end diff --git a/gfx/skia/skia/src/views/mac/SkTextFieldCell.m b/gfx/skia/skia/src/views/mac/SkTextFieldCell.m new file mode 100644 index 000000000..c5efc4640 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/SkTextFieldCell.m @@ -0,0 +1,56 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#import "SkTextFieldCell.h" +@implementation SkTextFieldCell +- (NSRect)drawingRectForBounds:(NSRect)theRect { + NSRect newRect = [super drawingRectForBounds:theRect]; + if (selectingOrEditing == NO) { + NSSize textSize = [self cellSizeForBounds:theRect]; + float heightDelta = newRect.size.height - textSize.height; + if (heightDelta > 0) { + newRect.size.height -= heightDelta; + newRect.origin.y += (heightDelta / 2); + } + } + return newRect; +} + +- (void)selectWithFrame:(NSRect)aRect + inView:(NSView *)controlView + editor:(NSText *)textObj + delegate:(id)anObject + start:(NSInteger)selStart + length:(NSInteger)selLength { + aRect = [self drawingRectForBounds:aRect]; + selectingOrEditing = YES; + [super selectWithFrame:aRect + inView:controlView + editor:textObj + delegate:anObject + start:selStart + length:selLength]; + selectingOrEditing = NO; +} + +- (void)editWithFrame:(NSRect)aRect + inView:(NSView *)controlView + editor:(NSText *)textObj + delegate:(id)anObject + event:(NSEvent *)theEvent { + aRect = [self drawingRectForBounds:aRect]; + selectingOrEditing = YES; + [super editWithFrame:aRect + inView:controlView + editor:textObj + delegate:anObject + event:theEvent]; + selectingOrEditing = NO; +} + +@end diff --git a/gfx/skia/skia/src/views/mac/skia_mac.mm b/gfx/skia/skia/src/views/mac/skia_mac.mm new file mode 100644 index 000000000..98d4c4bd9 --- /dev/null +++ b/gfx/skia/skia/src/views/mac/skia_mac.mm @@ -0,0 +1,126 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <crt_externs.h> +#import <Cocoa/Cocoa.h> +#include "SkApplication.h" +#include "SkGraphics.h" +#include "SkNSView.h" + +@interface MainView : SkNSView { +} +- (id)initWithFrame: (NSRect)frame ; +- (void)dealloc; +- (void)begin; +@end + +@implementation MainView : SkNSView + +- (id)initWithFrame: (NSRect)frame { + self = [super initWithFrame:frame]; + return self; +} + +- (void)dealloc { + delete fWind; + [super dealloc]; +} + +- (void)begin { + fWind = create_sk_window(self, *_NSGetArgc(), *_NSGetArgv()); + [self setUpWindow]; +} +@end + +@interface AppDelegate : NSObject<NSApplicationDelegate, NSWindowDelegate> { +} +- (id)init; +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication; +@end + +# +@implementation AppDelegate : NSObject +- (id)init { + self = [super init]; + return self; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return TRUE; +} +@end + +int main(int argc, char *argv[]) { + SkGraphics::Init(); + signal(SIGPIPE, SIG_IGN); + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + NSApplication* app = [NSApplication sharedApplication]; + + NSUInteger windowStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask); + + NSRect windowRect = NSMakeRect(100, 100, 1000, 1000); + NSWindow* window = [[NSWindow alloc] initWithContentRect:windowRect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; + + NSRect rect = [NSWindow contentRectForFrameRect:windowRect styleMask:windowStyle]; + MainView* customView = [[MainView alloc] initWithFrame:rect]; + [customView setTranslatesAutoresizingMaskIntoConstraints:NO]; + NSView* contentView = window.contentView; + [contentView addSubview:customView]; + NSDictionary *views = NSDictionaryOfVariableBindings(customView); + + [contentView addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" + options:0 + metrics:nil + views:views]]; + + [contentView addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" + options:0 + metrics:nil + views:views]]; + + [customView begin]; + [customView release]; + + [window makeKeyAndOrderFront:NSApp]; + + AppDelegate * appDelegate = [[[AppDelegate alloc] init] autorelease]; + + app.delegate = appDelegate; + + NSMenu* menu=[[NSMenu alloc] initWithTitle:@"AMainMenu"]; + NSMenuItem* item; + NSMenu* subMenu; + + //Create the application menu. + item=[[NSMenuItem alloc] initWithTitle:@"Apple" action:NULL keyEquivalent:@""]; + [menu addItem:item]; + subMenu=[[NSMenu alloc] initWithTitle:@"Apple"]; + [menu setSubmenu:subMenu forItem:item]; + [item release]; + item=[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; + [subMenu addItem:item]; + [item release]; + [subMenu release]; + + //Add the menu to the app. + [app setMenu:menu]; + + [app setActivationPolicy:NSApplicationActivationPolicyRegular]; + + [app run]; + + [menu release]; + [appDelegate release]; + [window release]; + [pool release]; + + return EXIT_SUCCESS; +} diff --git a/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp b/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp new file mode 100644 index 000000000..88b8353b6 --- /dev/null +++ b/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp @@ -0,0 +1,401 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkOSWindow_SDL.h" +#include "SkCanvas.h" + +#if defined(SK_BUILD_FOR_ANDROID) +#include <GLES/gl.h> +#elif defined(SK_BUILD_FOR_UNIX) +#include <GL/gl.h> +#elif defined(SK_BUILD_FOR_MAC) +#include <gl.h> +#endif + +const int kInitialWindowWidth = 640; +const int kInitialWindowHeight = 480; +static SkOSWindow* gCurrentWindow; + +static void report_sdl_error(const char* failure) { + const char* error = SDL_GetError(); + SkASSERT(error); // Called only to check SDL error. + SkDebugf("%s SDL Error: %s.\n", failure, error); + SDL_ClearError(); +} +SkOSWindow::SkOSWindow(void*) + : fWindow(nullptr) + , fGLContext(nullptr) + , fWindowMSAASampleCount(0) { + + SkASSERT(!gCurrentWindow); + gCurrentWindow = this; + + this->createWindow(0); +} + +SkOSWindow::~SkOSWindow() { + this->destroyWindow(); + gCurrentWindow = nullptr; +} + +SkOSWindow* SkOSWindow::GetInstanceForWindowID(Uint32 windowID) { + if (gCurrentWindow && + gCurrentWindow->fWindow && + SDL_GetWindowID(gCurrentWindow->fWindow) == windowID) { + return gCurrentWindow; + } + return nullptr; +} + +void SkOSWindow::release() { + if (fGLContext) { + SDL_GL_DeleteContext(fGLContext); + fGLContext = nullptr; + } +} + +bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, + AttachmentInfo* info) { + this->createWindow(msaaSampleCount); + if (!fWindow) { + return false; + } + if (!fGLContext) { + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + report_sdl_error("Failed to create SDL GL context."); + return false; + } + glClearColor(0, 0, 0, 0); + glClearStencil(0); + glStencilMask(0xffffffff); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } + + if (SDL_GL_MakeCurrent(fWindow, fGLContext) != 0) { + report_sdl_error("Failed to make SDL GL context current."); + this->release(); + return false; + } + + info->fSampleCount = msaaSampleCount; + info->fStencilBits = 8; + + glViewport(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height())); + return true; +} + +void SkOSWindow::present() { + if (!fWindow) { + return; + } + SDL_GL_SwapWindow(fWindow); +} + +bool SkOSWindow::makeFullscreen() { + if (!fWindow) { + return false; + } + SDL_SetWindowFullscreen(fWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); + return true; +} + +void SkOSWindow::setVsync(bool vsync) { + if (!fWindow) { + return; + } + SDL_GL_SetSwapInterval(vsync ? 1 : 0); +} + +void SkOSWindow::closeWindow() { + this->destroyWindow(); + + // Currently closing the window causes the app to quit. + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} + +static SkKey convert_sdlkey_to_skkey(SDL_Keycode src) { + switch (src) { + case SDLK_UP: + return kUp_SkKey; + case SDLK_DOWN: + return kDown_SkKey; + case SDLK_LEFT: + return kLeft_SkKey; + case SDLK_RIGHT: + return kRight_SkKey; + case SDLK_HOME: + return kHome_SkKey; + case SDLK_END: + return kEnd_SkKey; + case SDLK_ASTERISK: + return kStar_SkKey; + case SDLK_HASH: + return kHash_SkKey; + case SDLK_0: + return k0_SkKey; + case SDLK_1: + return k1_SkKey; + case SDLK_2: + return k2_SkKey; + case SDLK_3: + return k3_SkKey; + case SDLK_4: + return k4_SkKey; + case SDLK_5: + return k5_SkKey; + case SDLK_6: + return k6_SkKey; + case SDLK_7: + return k7_SkKey; + case SDLK_8: + return k8_SkKey; + case SDLK_9: + return k9_SkKey; + default: + return kNONE_SkKey; + } +} + +void SkOSWindow::createWindow(int msaaSampleCount) { + if (fWindowMSAASampleCount != msaaSampleCount) { + this->destroyWindow(); + } + if (fWindow) { + return; + } + uint32_t windowFlags = +#if defined(SK_BUILD_FOR_ANDROID) + SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN_DESKTOP | + SDL_WINDOW_ALLOW_HIGHDPI | +#endif + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + + // GL settings are part of SDL_WINDOW_OPENGL window creation arguments. +#if defined(SK_BUILD_FOR_ANDROID) + // TODO we should try and get a 3.0 context first + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); +#if defined(SK_BUILD_FOR_UNIX) + // Apparently MSAA request matches "slow caveat". Make SDL not set anything for caveat for MSAA + // by setting -1 for ACCELERATED_VISUAL. For non-MSAA, set ACCELERATED_VISUAL to 1 just for + // compatiblity with other platforms. + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, msaaSampleCount > 0 ? -1 : 1); +#else + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); +#endif + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaaSampleCount > 0 ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaaSampleCount); + + // This is an approximation for sizing purposes. + bool isInitialWindow = this->width() == 0 && this->height() == 0; + SkScalar windowWidth = isInitialWindow ? kInitialWindowWidth : this->width(); + SkScalar windowHeight = isInitialWindow ? kInitialWindowHeight : this->height(); + + fWindow = SDL_CreateWindow(this->getTitle(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + windowWidth, windowHeight, windowFlags); + if (!fWindow) { + report_sdl_error("Failed to create SDL window."); + return; + } + fWindowMSAASampleCount = msaaSampleCount; +} + +void SkOSWindow::destroyWindow() { + this->release(); + if (fWindow) { + SDL_DestroyWindow(fWindow); + fWindow = nullptr; + fWindowMSAASampleCount = 0; + } +} + +bool SkOSWindow::HasDirtyWindows() { + if (gCurrentWindow && gCurrentWindow->fWindow) { + return gCurrentWindow->isDirty(); + } + return false; +} + +void SkOSWindow::UpdateDirtyWindows() { + if (gCurrentWindow && gCurrentWindow->fWindow) { + if (gCurrentWindow->isDirty()) { + // This will call present. + gCurrentWindow->update(nullptr); + } + } +} + +void SkOSWindow::HandleEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_MOUSEMOTION: + if (SkOSWindow* window = GetInstanceForWindowID(event.motion.windowID)) { + if (event.motion.state == SDL_PRESSED) { + window->handleClick(event.motion.x, event.motion.y, + SkView::Click::kMoved_State, nullptr); + } + } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (SkOSWindow* window = GetInstanceForWindowID(event.button.windowID)) { + window->handleClick(event.button.x, event.button.y, + event.button.state == SDL_PRESSED ? + SkView::Click::kDown_State : + SkView::Click::kUp_State, nullptr); + } + break; + case SDL_KEYDOWN: + if (SkOSWindow* window = GetInstanceForWindowID(event.key.windowID)) { + SDL_Keycode key = event.key.keysym.sym; + SkKey sk = convert_sdlkey_to_skkey(key); + if (kNONE_SkKey != sk) { + if (event.key.state == SDL_PRESSED) { + window->handleKey(sk); + } else { + window->handleKeyUp(sk); + } + } else if (key == SDLK_ESCAPE) { + window->closeWindow(); + } + } + break; + case SDL_TEXTINPUT: + if (SkOSWindow* window = GetInstanceForWindowID(event.text.windowID)) { + size_t len = strlen(event.text.text); + for (size_t i = 0; i < len; i++) { + window->handleChar((SkUnichar)event.text.text[i]); + } + } + break; + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_SHOWN: + // For initialization purposes, we resize upon first show. + // Fallthrough. + case SDL_WINDOWEVENT_SIZE_CHANGED: + if (SkOSWindow* window = GetInstanceForWindowID(event.window.windowID)) { + int w = 0; + int h = 0; + SDL_GetWindowSize(window->fWindow, &w, &h); + window->resize(w, h); + } + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (GetInstanceForWindowID(event.text.windowID)) { + SDL_StartTextInput(); + } + break; + default: + break; + } + break; + default: + break; + } +} + +SkMSec gTimerDelay; + +void SkOSWindow::RunEventLoop() { + for (;;) { + SkEvent::ServiceQueueTimer(); + bool hasMoreSkEvents = SkEvent::ProcessEvent(); + + SDL_Event event; + bool hasSDLEvents = SDL_PollEvent(&event) == 1; + + // Invalidations do not post to event loop, rather we just go through the + // windows for each event loop iteration. + bool hasDirtyWindows = HasDirtyWindows(); + + if (!hasSDLEvents && !hasMoreSkEvents && !hasDirtyWindows) { + // If there is no SDL events, SkOSWindow updates or SkEvents + // to be done, wait for the SDL events. + if (gTimerDelay > 0) { + hasSDLEvents = SDL_WaitEventTimeout(&event, gTimerDelay) == 1; + } else { + hasSDLEvents = SDL_WaitEvent(&event) == 1; + } + } + while (hasSDLEvents) { + if (event.type == SDL_QUIT) { + return; + } + HandleEvent(event); + hasSDLEvents = SDL_PollEvent(&event); + } + UpdateDirtyWindows(); + } +} + +void SkOSWindow::onSetTitle(const char title[]) { + if (!fWindow) { + return; + } + this->updateWindowTitle(); +} + +void SkOSWindow::updateWindowTitle() { + SDL_SetWindowTitle(fWindow, this->getTitle()); +} +/////////////////////////////////////////////////////////////////////////////////////// + +void SkEvent::SignalNonEmptyQueue() { + // nothing to do, since we spin on our event-queue +} + +void SkEvent::SignalQueueTimer(SkMSec delay) { + gTimerDelay = delay; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkApplication.h" +#include "SkEvent.h" +#include "SkWindow.h" + +#if defined(SK_BUILD_FOR_ANDROID) +int SDL_main(int argc, char** argv) { +#else +int main(int argc, char** argv) { +#endif + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { + report_sdl_error("Failed to init SDL."); + return -1; + } + + application_init(); + + SkOSWindow* window = create_sk_window(nullptr, argc, argv); + + // drain any events that occurred before |window| was assigned. + while (SkEvent::ProcessEvent()); + + SkOSWindow::RunEventLoop(); + + delete window; + application_term(); + + SDL_Quit(); + + return 0; +} diff --git a/gfx/skia/skia/src/views/unix/SkOSWindow_Unix.cpp b/gfx/skia/skia/src/views/unix/SkOSWindow_Unix.cpp new file mode 100644 index 000000000..2f195927e --- /dev/null +++ b/gfx/skia/skia/src/views/unix/SkOSWindow_Unix.cpp @@ -0,0 +1,519 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/XKBlib.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glu.h> + +#include "SkWindow.h" + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkWindow.h" +#include "XkeysToSkKeys.h" +extern "C" { + #include "keysym2ucs.h" +} + +const int WIDTH = 500; +const int HEIGHT = 500; + +// Determine which events to listen for. +const long EVENT_MASK = StructureNotifyMask|ButtonPressMask|ButtonReleaseMask + |ExposureMask|PointerMotionMask|KeyPressMask|KeyReleaseMask; + +SkOSWindow::SkOSWindow(void*) + : fVi(nullptr) + , fMSAASampleCount(0) { + fUnixWindow.fDisplay = nullptr; + fUnixWindow.fGLContext = nullptr; + this->initWindow(0, nullptr); + this->resize(WIDTH, HEIGHT); +} + +SkOSWindow::~SkOSWindow() { + this->internalCloseWindow(); +} + +void SkOSWindow::internalCloseWindow() { + if (fUnixWindow.fDisplay) { + this->release(); + SkASSERT(fUnixWindow.fGc); + XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc); + fUnixWindow.fGc = nullptr; + XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin); + fVi = nullptr; + XCloseDisplay(fUnixWindow.fDisplay); + fUnixWindow.fDisplay = nullptr; + fMSAASampleCount = 0; + } +} + +void SkOSWindow::initWindow(int requestedMSAASampleCount, AttachmentInfo* info) { + if (fMSAASampleCount != requestedMSAASampleCount) { + this->internalCloseWindow(); + } + // presence of fDisplay means we already have a window + if (fUnixWindow.fDisplay) { + if (info) { + if (fVi) { + glXGetConfig(fUnixWindow.fDisplay, fVi, GLX_SAMPLES_ARB, &info->fSampleCount); + glXGetConfig(fUnixWindow.fDisplay, fVi, GLX_STENCIL_SIZE, &info->fStencilBits); + } else { + info->fSampleCount = 0; + info->fStencilBits = 0; + } + } + return; + } + fUnixWindow.fDisplay = XOpenDisplay(nullptr); + Display* dsp = fUnixWindow.fDisplay; + if (nullptr == dsp) { + SkDebugf("Could not open an X Display"); + return; + } + // Attempt to create a window that supports GL + GLint att[] = { + GLX_RGBA, + GLX_DEPTH_SIZE, 24, + GLX_DOUBLEBUFFER, + GLX_STENCIL_SIZE, 8, + None + }; + SkASSERT(nullptr == fVi); + if (requestedMSAASampleCount > 0) { + static const GLint kAttCount = SK_ARRAY_COUNT(att); + GLint msaaAtt[kAttCount + 4]; + memcpy(msaaAtt, att, sizeof(att)); + SkASSERT(None == msaaAtt[kAttCount - 1]); + msaaAtt[kAttCount - 1] = GLX_SAMPLE_BUFFERS_ARB; + msaaAtt[kAttCount + 0] = 1; + msaaAtt[kAttCount + 1] = GLX_SAMPLES_ARB; + msaaAtt[kAttCount + 2] = requestedMSAASampleCount; + msaaAtt[kAttCount + 3] = None; + fVi = glXChooseVisual(dsp, DefaultScreen(dsp), msaaAtt); + fMSAASampleCount = requestedMSAASampleCount; + } + if (nullptr == fVi) { + fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att); + fMSAASampleCount = 0; + } + + if (fVi) { + if (info) { + glXGetConfig(dsp, fVi, GLX_SAMPLES_ARB, &info->fSampleCount); + glXGetConfig(dsp, fVi, GLX_STENCIL_SIZE, &info->fStencilBits); + } + Colormap colorMap = XCreateColormap(dsp, + RootWindow(dsp, fVi->screen), + fVi->visual, + AllocNone); + XSetWindowAttributes swa; + swa.colormap = colorMap; + swa.event_mask = EVENT_MASK; + fUnixWindow.fWin = XCreateWindow(dsp, + RootWindow(dsp, fVi->screen), + 0, 0, // x, y + WIDTH, HEIGHT, + 0, // border width + fVi->depth, + InputOutput, + fVi->visual, + CWEventMask | CWColormap, + &swa); + } else { + if (info) { + info->fSampleCount = 0; + info->fStencilBits = 0; + } + // Create a simple window instead. We will not be able to show GL + fUnixWindow.fWin = XCreateSimpleWindow(dsp, + DefaultRootWindow(dsp), + 0, 0, // x, y + WIDTH, HEIGHT, + 0, // border width + 0, // border value + 0); // background value + } + this->mapWindowAndWait(); + fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, nullptr); +} + +static unsigned getModi(const XEvent& evt) { + static const struct { + unsigned fXMask; + unsigned fSkMask; + } gModi[] = { + // X values found by experiment. Is there a better way? + { 1, kShift_SkModifierKey }, + { 4, kControl_SkModifierKey }, + { 8, kOption_SkModifierKey }, + }; + + unsigned modi = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(gModi); ++i) { + if (evt.xkey.state & gModi[i].fXMask) { + modi |= gModi[i].fSkMask; + } + } + return modi; +} + +static SkMSec gTimerDelay; + +static bool MyXNextEventWithDelay(Display* dsp, XEvent* evt) { + // Check for pending events before entering the select loop. There might + // be events in the in-memory queue but not processed yet. + if (XPending(dsp)) { + XNextEvent(dsp, evt); + return true; + } + + SkMSec ms = gTimerDelay; + if (ms > 0) { + int x11_fd = ConnectionNumber(dsp); + fd_set input_fds; + FD_ZERO(&input_fds); + FD_SET(x11_fd, &input_fds); + + timeval tv; + tv.tv_sec = ms / 1000; // seconds + tv.tv_usec = (ms % 1000) * 1000; // microseconds + + if (!select(x11_fd + 1, &input_fds, nullptr, nullptr, &tv)) { + if (!XPending(dsp)) { + return false; + } + } + } + XNextEvent(dsp, evt); + return true; +} + +static Atom wm_delete_window_message; + +SkOSWindow::NextXEventResult SkOSWindow::nextXEvent() { + XEvent evt; + Display* dsp = fUnixWindow.fDisplay; + + if (!MyXNextEventWithDelay(dsp, &evt)) { + return kContinue_NextXEventResult; + } + + switch (evt.type) { + case Expose: + if (0 == evt.xexpose.count) { + return kPaintRequest_NextXEventResult; + } + break; + case ConfigureNotify: + this->resize(evt.xconfigure.width, evt.xconfigure.height); + break; + case ButtonPress: + if (evt.xbutton.button == Button1) + this->handleClick(evt.xbutton.x, evt.xbutton.y, + SkView::Click::kDown_State, nullptr, getModi(evt)); + break; + case ButtonRelease: + if (evt.xbutton.button == Button1) + this->handleClick(evt.xbutton.x, evt.xbutton.y, + SkView::Click::kUp_State, nullptr, getModi(evt)); + break; + case MotionNotify: + this->handleClick(evt.xmotion.x, evt.xmotion.y, + SkView::Click::kMoved_State, nullptr, getModi(evt)); + break; + case KeyPress: { + int shiftLevel = (evt.xkey.state & ShiftMask) ? 1 : 0; + KeySym keysym = XkbKeycodeToKeysym(dsp, evt.xkey.keycode, + 0, shiftLevel); + if (keysym == XK_Escape) { + return kQuitRequest_NextXEventResult; + } + this->handleKey(XKeyToSkKey(keysym)); + long uni = keysym2ucs(keysym); + if (uni != -1) { + this->handleChar((SkUnichar) uni); + } + break; + } + case KeyRelease: + this->handleKeyUp(XKeyToSkKey(XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0))); + break; + case ClientMessage: + if ((Atom)evt.xclient.data.l[0] == wm_delete_window_message) { + return kQuitRequest_NextXEventResult; + } + // fallthrough + default: + // Do nothing for other events + break; + } + return kContinue_NextXEventResult; +} + +void SkOSWindow::loop() { + Display* dsp = fUnixWindow.fDisplay; + if (nullptr == dsp) { + return; + } + Window win = fUnixWindow.fWin; + + wm_delete_window_message = XInternAtom(dsp, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dsp, win, &wm_delete_window_message, 1); + + XSelectInput(dsp, win, EVENT_MASK); + + bool sentExposeEvent = false; + + for (;;) { + SkEvent::ServiceQueueTimer(); + + bool moreToDo = SkEvent::ProcessEvent(); + + if (this->isDirty() && !sentExposeEvent) { + sentExposeEvent = true; + + XEvent evt; + sk_bzero(&evt, sizeof(evt)); + evt.type = Expose; + evt.xexpose.display = dsp; + XSendEvent(dsp, win, false, ExposureMask, &evt); + } + + if (XPending(dsp) || !moreToDo) { + switch (this->nextXEvent()) { + case kContinue_NextXEventResult: + break; + case kPaintRequest_NextXEventResult: + sentExposeEvent = false; + if (this->isDirty()) { + this->update(nullptr); + } + this->doPaint(); + break; + case kQuitRequest_NextXEventResult: + return; + } + } + } +} + +void SkOSWindow::mapWindowAndWait() { + SkASSERT(fUnixWindow.fDisplay); + Display* dsp = fUnixWindow.fDisplay; + Window win = fUnixWindow.fWin; + XMapWindow(dsp, win); + + long eventMask = StructureNotifyMask; + XSelectInput(dsp, win, eventMask); + + // Wait until screen is ready. + XEvent evt; + do { + XNextEvent(dsp, &evt); + } while(evt.type != MapNotify); + +} + +//////////////////////////////////////////////// + +// Some helper code to load the correct version of glXSwapInterval +#define GLX_GET_PROC_ADDR(name) glXGetProcAddress(reinterpret_cast<const GLubyte*>((name))) +#define EXT_WRANGLE(name, type, ...) \ + if (GLX_GET_PROC_ADDR(#name)) { \ + static type k##name; \ + if (!k##name) { \ + k##name = (type) GLX_GET_PROC_ADDR(#name); \ + } \ + k##name(__VA_ARGS__); \ + /*SkDebugf("using %s\n", #name);*/ \ + return; \ + } + +static void glXSwapInterval(Display* dsp, GLXDrawable drawable, int interval) { + EXT_WRANGLE(glXSwapIntervalEXT, PFNGLXSWAPINTERVALEXTPROC, dsp, drawable, interval); + EXT_WRANGLE(glXSwapIntervalMESA, PFNGLXSWAPINTERVALMESAPROC, interval); + EXT_WRANGLE(glXSwapIntervalSGI, PFNGLXSWAPINTERVALSGIPROC, interval); +} + +///////////////////////////////////////////////////////////////////////// + +bool SkOSWindow::attach(SkBackEndTypes, int msaaSampleCount, bool deepColor, + AttachmentInfo* info) { + this->initWindow(msaaSampleCount, info); + + if (nullptr == fUnixWindow.fDisplay) { + return false; + } + if (nullptr == fUnixWindow.fGLContext) { + SkASSERT(fVi); + + fUnixWindow.fGLContext = glXCreateContext(fUnixWindow.fDisplay, + fVi, + nullptr, + GL_TRUE); + if (nullptr == fUnixWindow.fGLContext) { + return false; + } + } + glXMakeCurrent(fUnixWindow.fDisplay, + fUnixWindow.fWin, + fUnixWindow.fGLContext); + glViewport(0, 0, + SkScalarRoundToInt(this->width()), + SkScalarRoundToInt(this->height())); + glClearColor(0, 0, 0, 0); + glClearStencil(0); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + return true; +} + +void SkOSWindow::release() { + if (nullptr == fUnixWindow.fDisplay || nullptr == fUnixWindow.fGLContext) { + return; + } + glXMakeCurrent(fUnixWindow.fDisplay, None, nullptr); + glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext); + fUnixWindow.fGLContext = nullptr; +} + +void SkOSWindow::present() { + if (fUnixWindow.fDisplay && fUnixWindow.fGLContext) { + glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin); + } +} + +void SkOSWindow::onSetTitle(const char title[]) { + if (nullptr == fUnixWindow.fDisplay) { + return; + } + XTextProperty textProp; + textProp.value = (unsigned char*)title; + textProp.format = 8; + textProp.nitems = strlen((char*)textProp.value); + textProp.encoding = XA_STRING; + XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp); +} + +static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap) { + sk_bzero(&image, sizeof(image)); + + int bitsPerPixel = bitmap.bytesPerPixel() * 8; + image.width = bitmap.width(); + image.height = bitmap.height(); + image.format = ZPixmap; + image.data = (char*) bitmap.getPixels(); + image.byte_order = LSBFirst; + image.bitmap_unit = bitsPerPixel; + image.bitmap_bit_order = LSBFirst; + image.bitmap_pad = bitsPerPixel; + image.depth = 24; + image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * 4; + image.bits_per_pixel = bitsPerPixel; + return XInitImage(&image); +} + +void SkOSWindow::doPaint() { + if (nullptr == fUnixWindow.fDisplay) { + return; + } + // If we are drawing with GL, we don't need XPutImage. + if (fUnixWindow.fGLContext) { + return; + } + // Draw the bitmap to the screen. + const SkBitmap& bitmap = getBitmap(); + int width = bitmap.width(); + int height = bitmap.height(); + + XImage image; + if (!convertBitmapToXImage(image, bitmap)) { + return; + } + + XPutImage(fUnixWindow.fDisplay, + fUnixWindow.fWin, + fUnixWindow.fGc, + &image, + 0, 0, // src x,y + 0, 0, // dst x,y + width, height); +} + +enum { + _NET_WM_STATE_REMOVE =0, + _NET_WM_STATE_ADD = 1, + _NET_WM_STATE_TOGGLE =2 +}; + +bool SkOSWindow::makeFullscreen() { + Display* dsp = fUnixWindow.fDisplay; + if (nullptr == dsp) { + return false; + } + + // Full screen + Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False); + + XEvent evt; + sk_bzero(&evt, sizeof(evt)); + evt.type = ClientMessage; + evt.xclient.window = fUnixWindow.fWin; + evt.xclient.message_type = wm_state; + evt.xclient.format = 32; + evt.xclient.data.l[0] = _NET_WM_STATE_ADD; + evt.xclient.data.l[1] = fullscreen; + evt.xclient.data.l[2] = 0; + + XSendEvent(dsp, DefaultRootWindow(dsp), False, + SubstructureRedirectMask | SubstructureNotifyMask, &evt); + return true; +} + +void SkOSWindow::setVsync(bool vsync) { + if (fUnixWindow.fDisplay && fUnixWindow.fGLContext && fUnixWindow.fWin) { + int swapInterval = vsync ? 1 : 0; + glXSwapInterval(fUnixWindow.fDisplay, fUnixWindow.fWin, swapInterval); + } +} + +void SkOSWindow::closeWindow() { + Display* dsp = fUnixWindow.fDisplay; + if (nullptr == dsp) { + return; + } + + XEvent evt; + sk_bzero(&evt, sizeof(evt)); + evt.type = ClientMessage; + evt.xclient.message_type = XInternAtom(dsp, "WM_PROTOCOLS", true); + evt.xclient.window = fUnixWindow.fWin; + evt.xclient.format = 32; + evt.xclient.data.l[0] = XInternAtom(dsp, "WM_DELETE_WINDOW", false); + evt.xclient.data.l[1] = CurrentTime; + + XSendEvent(dsp, fUnixWindow.fWin, false, NoEventMask, &evt); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkEvent::SignalNonEmptyQueue() { + // nothing to do, since we spin on our event-queue, polling for XPending +} + +void SkEvent::SignalQueueTimer(SkMSec delay) { + // just need to record the delay time. We handle waking up for it in + // MyXNextEventWithDelay() + gTimerDelay = delay; +} diff --git a/gfx/skia/skia/src/views/unix/XkeysToSkKeys.h b/gfx/skia/skia/src/views/unix/XkeysToSkKeys.h new file mode 100644 index 000000000..aced74c0a --- /dev/null +++ b/gfx/skia/skia/src/views/unix/XkeysToSkKeys.h @@ -0,0 +1,66 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "X11/Xlib.h" +#include "X11/keysym.h" + +#include "SkKey.h" + +#ifndef XKEYS_TOSKKEYS_H +#define XKEYS_TOSKKEYS_H + +SkKey XKeyToSkKey(KeySym keysym) { + switch (keysym) { + case XK_BackSpace: + return kBack_SkKey; + case XK_Return: + return kOK_SkKey; + case XK_Home: + return kHome_SkKey; + case XK_End: + return kEnd_SkKey; + case XK_Right: + return kRight_SkKey; + case XK_Left: + return kLeft_SkKey; + case XK_Down: + return kDown_SkKey; + case XK_Up: + return kUp_SkKey; + case XK_KP_0: + case XK_KP_Insert: + return k0_SkKey; + case XK_KP_1: + case XK_KP_End: + return k1_SkKey; + case XK_KP_2: + case XK_KP_Down: + return k2_SkKey; + case XK_KP_3: + case XK_KP_Page_Down: + return k3_SkKey; + case XK_KP_4: + case XK_KP_Left: + return k4_SkKey; + case XK_KP_5: + return k5_SkKey; + case XK_KP_6: + case XK_KP_Right: + return k6_SkKey; + case XK_KP_7: + case XK_KP_Home: + return k7_SkKey; + case XK_KP_8: + case XK_KP_Up: + return k8_SkKey; + case XK_KP_9: + case XK_KP_Page_Up: + return k9_SkKey; + default: + return kNONE_SkKey; + } +} +#endif diff --git a/gfx/skia/skia/src/views/unix/keysym2ucs.c b/gfx/skia/skia/src/views/unix/keysym2ucs.c new file mode 100644 index 000000000..a0c4ced9e --- /dev/null +++ b/gfx/skia/skia/src/views/unix/keysym2ucs.c @@ -0,0 +1,848 @@ +/* $XFree86$ + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn <http://www.cl.cam.ac.uk/~mgk25/>, + * University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + * + * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl) + */ + +#include "keysym2ucs.h" + +struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ +/* 0x08b1 topleftsummation ? ??? */ +/* 0x08b2 botleftsummation ? ??? */ +/* 0x08b3 topvertsummationconnector ? ??? */ +/* 0x08b4 botvertsummationconnector ? ??? */ +/* 0x08b5 toprightsummation ? ??? */ +/* 0x08b6 botrightsummation ? ??? */ +/* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ +/* 0x09df blank ? ??? */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ +/* 0x0aac signifblank ? ??? */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ? ??? */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ +/* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ +/* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ + { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ? ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ + { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ +}; + +long keysym2ucs(KeySym keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + /* binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else { + /* found it */ + return keysymtab[mid].ucs; + } + } + + /* no matching Unicode value found */ + return -1; +} diff --git a/gfx/skia/skia/src/views/unix/keysym2ucs.h b/gfx/skia/skia/src/views/unix/keysym2ucs.h new file mode 100644 index 000000000..0287f02ad --- /dev/null +++ b/gfx/skia/skia/src/views/unix/keysym2ucs.h @@ -0,0 +1,14 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +/* + * This module converts keysym values into the corresponding ISO 10646-1 + * (UCS, Unicode) values. + */ + +#include <X11/X.h> + +long keysym2ucs(KeySym keysym); diff --git a/gfx/skia/skia/src/views/unix/skia_unix.cpp b/gfx/skia/skia/src/views/unix/skia_unix.cpp new file mode 100644 index 000000000..9f9905962 --- /dev/null +++ b/gfx/skia/skia/src/views/unix/skia_unix.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkApplication.h" +#include "SkEvent.h" +#include "SkGraphics.h" +#include "SkWindow.h" + +int main(int argc, char** argv){ + SkGraphics::Init(); + SkOSWindow* window = create_sk_window(nullptr, argc, argv); + + // drain any events that occurred before |window| was assigned. + while (SkEvent::ProcessEvent()); + + // Start normal Skia sequence + application_init(); + + window->loop(); + + delete window; + application_term(); + return 0; +} diff --git a/gfx/skia/skia/src/views/win/SkOSWindow_win.cpp b/gfx/skia/skia/src/views/win/SkOSWindow_win.cpp new file mode 100644 index 000000000..983b253c5 --- /dev/null +++ b/gfx/skia/skia/src/views/win/SkOSWindow_win.cpp @@ -0,0 +1,772 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkTypes.h" + +#if defined(SK_BUILD_FOR_WIN) + +#include "SkLeanWindows.h" + +#include <GL/gl.h> +#include <WindowsX.h> +#include "win/SkWGL.h" +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkTime.h" +#include "SkUtils.h" + +#include "SkGraphics.h" + +#if SK_ANGLE +#include "gl/GrGLAssembleInterface.h" +#include "gl/GrGLInterface.h" +#include "GLES2/gl2.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif // SK_ANGLE + +const int kDefaultWindowWidth = 500; +const int kDefaultWindowHeight = 500; + +#define GL_CALL(IFACE, X) \ + SkASSERT(IFACE); \ + do { \ + (IFACE)->fFunctions.f##X; \ + } while (false) + +#define WM_EVENT_CALLBACK (WM_USER+0) + +void post_skwinevent(HWND hwnd) +{ + PostMessage(hwnd, WM_EVENT_CALLBACK, 0, 0); +} + +SkTHashMap<void*, SkOSWindow*> SkOSWindow::gHwndToOSWindowMap; + +SkOSWindow::SkOSWindow(const void* winInit) { + fWinInit = *(const WindowInit*)winInit; + + fHWND = CreateWindow(fWinInit.fClass, NULL, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, kDefaultWindowWidth, kDefaultWindowHeight, NULL, NULL, + fWinInit.fInstance, NULL); + gHwndToOSWindowMap.set(fHWND, this); +#if SK_SUPPORT_GPU +#if SK_ANGLE + fDisplay = EGL_NO_DISPLAY; + fContext = EGL_NO_CONTEXT; + fSurface = EGL_NO_SURFACE; +#endif + + fHGLRC = NULL; +#endif + fAttached = kNone_BackEndType; + fFullscreen = false; +} + +SkOSWindow::~SkOSWindow() { +#if SK_SUPPORT_GPU + if (fHGLRC) { + wglDeleteContext((HGLRC)fHGLRC); + } +#if SK_ANGLE + if (EGL_NO_CONTEXT != fContext) { + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + } + + if (EGL_NO_SURFACE != fSurface) { + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + } + + if (EGL_NO_DISPLAY != fDisplay) { + eglTerminate(fDisplay); + fDisplay = EGL_NO_DISPLAY; + } +#endif // SK_ANGLE +#endif // SK_SUPPORT_GPU + this->closeWindow(); +} + +static SkKey winToskKey(WPARAM vk) { + static const struct { + WPARAM fVK; + SkKey fKey; + } gPair[] = { + { VK_BACK, kBack_SkKey }, + { VK_CLEAR, kBack_SkKey }, + { VK_RETURN, kOK_SkKey }, + { VK_UP, kUp_SkKey }, + { VK_DOWN, kDown_SkKey }, + { VK_LEFT, kLeft_SkKey }, + { VK_RIGHT, kRight_SkKey } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fVK == vk) { + return gPair[i].fKey; + } + } + return kNONE_SkKey; +} + +static unsigned getModifiers(UINT message) { + return 0; // TODO +} + +bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_KEYDOWN: { + SkKey key = winToskKey(wParam); + if (kNONE_SkKey != key) { + this->handleKey(key); + return true; + } + } break; + case WM_KEYUP: { + SkKey key = winToskKey(wParam); + if (kNONE_SkKey != key) { + this->handleKeyUp(key); + return true; + } + } break; + case WM_UNICHAR: + this->handleChar((SkUnichar) wParam); + return true; + case WM_CHAR: { + const uint16_t* c = reinterpret_cast<uint16_t*>(&wParam); + this->handleChar(SkUTF16_NextUnichar(&c)); + return true; + } break; + case WM_SIZE: { + INT width = LOWORD(lParam); + INT height = HIWORD(lParam); + this->resize(width, height); + break; + } + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + this->doPaint(hdc); + EndPaint(hWnd, &ps); + return true; + } break; + + case WM_LBUTTONDOWN: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + Click::kDown_State, NULL, getModifiers(message)); + return true; + + case WM_MOUSEMOVE: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + Click::kMoved_State, NULL, getModifiers(message)); + return true; + + case WM_LBUTTONUP: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + Click::kUp_State, NULL, getModifiers(message)); + return true; + + case WM_EVENT_CALLBACK: + if (SkEvent::ProcessEvent()) { + post_skwinevent(hWnd); + } + return true; + } + return false; +} + +void SkOSWindow::doPaint(void* ctx) { + this->update(NULL); + + if (kNone_BackEndType == fAttached) + { + HDC hdc = (HDC)ctx; + const SkBitmap& bitmap = this->getBitmap(); + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bitmap.width(); + bmi.bmiHeader.biHeight = -bitmap.height(); // top-down image + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + + // + // Do the SetDIBitsToDevice. + // + // TODO(wjmaclean): + // Fix this call to handle SkBitmaps that have rowBytes != width, + // i.e. may have padding at the end of lines. The SkASSERT below + // may be ignored by builds, and the only obviously safe option + // seems to be to copy the bitmap to a temporary (contiguous) + // buffer before passing to SetDIBitsToDevice(). + SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes()); + bitmap.lockPixels(); + int ret = SetDIBitsToDevice(hdc, + 0, 0, + bitmap.width(), bitmap.height(), + 0, 0, + 0, bitmap.height(), + bitmap.getPixels(), + &bmi, + DIB_RGB_COLORS); + (void)ret; // we're ignoring potential failures for now. + bitmap.unlockPixels(); + } +} + +void SkOSWindow::updateSize() +{ + RECT r; + GetWindowRect((HWND)fHWND, &r); + this->resize(r.right - r.left, r.bottom - r.top); +} + +void SkOSWindow::onHandleInval(const SkIRect& r) { + RECT rect; + rect.left = r.fLeft; + rect.top = r.fTop; + rect.right = r.fRight; + rect.bottom = r.fBottom; + InvalidateRect((HWND)fHWND, &rect, FALSE); +} + +void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) +{ +} + +void SkOSWindow::onSetTitle(const char title[]){ + SetWindowTextA((HWND)fHWND, title); +} + +enum { + SK_MacReturnKey = 36, + SK_MacDeleteKey = 51, + SK_MacEndKey = 119, + SK_MacLeftKey = 123, + SK_MacRightKey = 124, + SK_MacDownKey = 125, + SK_MacUpKey = 126, + + SK_Mac0Key = 0x52, + SK_Mac1Key = 0x53, + SK_Mac2Key = 0x54, + SK_Mac3Key = 0x55, + SK_Mac4Key = 0x56, + SK_Mac5Key = 0x57, + SK_Mac6Key = 0x58, + SK_Mac7Key = 0x59, + SK_Mac8Key = 0x5b, + SK_Mac9Key = 0x5c +}; + +static SkKey raw2key(uint32_t raw) +{ + static const struct { + uint32_t fRaw; + SkKey fKey; + } gKeys[] = { + { SK_MacUpKey, kUp_SkKey }, + { SK_MacDownKey, kDown_SkKey }, + { SK_MacLeftKey, kLeft_SkKey }, + { SK_MacRightKey, kRight_SkKey }, + { SK_MacReturnKey, kOK_SkKey }, + { SK_MacDeleteKey, kBack_SkKey }, + { SK_MacEndKey, kEnd_SkKey }, + { SK_Mac0Key, k0_SkKey }, + { SK_Mac1Key, k1_SkKey }, + { SK_Mac2Key, k2_SkKey }, + { SK_Mac3Key, k3_SkKey }, + { SK_Mac4Key, k4_SkKey }, + { SK_Mac5Key, k5_SkKey }, + { SK_Mac6Key, k6_SkKey }, + { SK_Mac7Key, k7_SkKey }, + { SK_Mac8Key, k8_SkKey }, + { SK_Mac9Key, k9_SkKey } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) + if (gKeys[i].fRaw == raw) + return gKeys[i].fKey; + return kNONE_SkKey; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void SkEvent::SignalNonEmptyQueue() +{ + SkOSWindow::ForAllWindows([](void* hWND, SkOSWindow**) { + post_skwinevent((HWND)hWND); + }); +} + +static UINT_PTR gTimer; + +VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + SkEvent::ServiceQueueTimer(); + //SkDebugf("timer task fired\n"); +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + if (gTimer) + { + KillTimer(NULL, gTimer); + gTimer = NULL; + } + if (delay) + { + gTimer = SetTimer(NULL, 0, delay, sk_timer_proc); + //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer); + } +} + +#if SK_SUPPORT_GPU + +bool SkOSWindow::attachGL(int msaaSampleCount, bool deepColor, AttachmentInfo* info) { + HDC dc = GetDC((HWND)fHWND); + if (NULL == fHGLRC) { + fHGLRC = SkCreateWGLContext(dc, msaaSampleCount, deepColor, + kGLPreferCompatibilityProfile_SkWGLContextRequest); + if (NULL == fHGLRC) { + return false; + } + glClearStencil(0); + glClearColor(0, 0, 0, 0); + glStencilMask(0xffffffff); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + } + if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) { + // use DescribePixelFormat to get the stencil and color bit depth. + int pixelFormat = GetPixelFormat(dc); + PIXELFORMATDESCRIPTOR pfd; + DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd); + info->fStencilBits = pfd.cStencilBits; + // pfd.cColorBits includes alpha, so it will be 32 in 8/8/8/8 and 10/10/10/2 + info->fColorBits = pfd.cRedBits + pfd.cGreenBits + pfd.cBlueBits; + + // Get sample count if the MSAA WGL extension is present + SkWGLExtensions extensions; + if (extensions.hasExtension(dc, "WGL_ARB_multisample")) { + static const int kSampleCountAttr = SK_WGL_SAMPLES; + extensions.getPixelFormatAttribiv(dc, + pixelFormat, + 0, + 1, + &kSampleCountAttr, + &info->fSampleCount); + } else { + info->fSampleCount = 0; + } + + glViewport(0, 0, + SkScalarRoundToInt(this->width()), + SkScalarRoundToInt(this->height())); + return true; + } + return false; +} + +void SkOSWindow::detachGL() { + wglMakeCurrent(GetDC((HWND)fHWND), 0); + wglDeleteContext((HGLRC)fHGLRC); + fHGLRC = NULL; +} + +void SkOSWindow::presentGL() { + HDC dc = GetDC((HWND)fHWND); + SwapBuffers(dc); + ReleaseDC((HWND)fHWND, dc); +} + +#if SK_ANGLE + +static void* get_angle_egl_display(void* nativeDisplay) { + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; + eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + // We expect ANGLE to support this extension + if (!eglGetPlatformDisplayEXT) { + return EGL_NO_DISPLAY; + } + + EGLDisplay display = EGL_NO_DISPLAY; + // Try for an ANGLE D3D11 context, fall back to D3D9, and finally GL. + EGLint attribs[3][3] = { + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_NONE + }, + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, + EGL_NONE + }, + }; + for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) { + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]); + } + return display; +} + +struct ANGLEAssembleContext { + ANGLEAssembleContext() { + fEGL = GetModuleHandle("libEGL.dll"); + fGL = GetModuleHandle("libGLESv2.dll"); + } + + bool isValid() const { return SkToBool(fEGL) && SkToBool(fGL); } + + HMODULE fEGL; + HMODULE fGL; +}; + +static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) { + const ANGLEAssembleContext& context = *reinterpret_cast<const ANGLEAssembleContext*>(ctx); + GrGLFuncPtr proc = (GrGLFuncPtr) GetProcAddress(context.fGL, name); + if (proc) { + return proc; + } + proc = (GrGLFuncPtr) GetProcAddress(context.fEGL, name); + if (proc) { + return proc; + } + return eglGetProcAddress(name); +} + +static const GrGLInterface* get_angle_gl_interface() { + ANGLEAssembleContext context; + if (!context.isValid()) { + return nullptr; + } + return GrGLAssembleGLESInterface(&context, angle_get_gl_proc); +} + +bool create_ANGLE(EGLNativeWindowType hWnd, + int msaaSampleCount, + EGLDisplay* eglDisplay, + EGLContext* eglContext, + EGLSurface* eglSurface, + EGLConfig* eglConfig) { + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE, EGL_NONE + }; + static const EGLint configAttribList[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + static const EGLint surfaceAttribList[] = { + EGL_NONE, EGL_NONE + }; + + EGLDisplay display = get_angle_egl_display(GetDC(hWnd)); + + if (EGL_NO_DISPLAY == display) { + SkDebugf("Could not create ANGLE egl display!\n"); + return false; + } + + // Initialize EGL + EGLint majorVersion, minorVersion; + if (!eglInitialize(display, &majorVersion, &minorVersion)) { + return false; + } + + EGLint numConfigs; + if (!eglGetConfigs(display, NULL, 0, &numConfigs)) { + return false; + } + + // Choose config + bool foundConfig = false; + if (msaaSampleCount) { + static const int kConfigAttribListCnt = + SK_ARRAY_COUNT(configAttribList); + EGLint msaaConfigAttribList[kConfigAttribListCnt + 4]; + memcpy(msaaConfigAttribList, + configAttribList, + sizeof(configAttribList)); + SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]); + msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS; + msaaConfigAttribList[kConfigAttribListCnt + 0] = 1; + msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES; + msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount; + msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE; + if (eglChooseConfig(display, msaaConfigAttribList, eglConfig, 1, &numConfigs)) { + SkASSERT(numConfigs > 0); + foundConfig = true; + } + } + if (!foundConfig) { + if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { + return false; + } + } + + // Create a surface + EGLSurface surface = eglCreateWindowSurface(display, *eglConfig, + (EGLNativeWindowType)hWnd, + surfaceAttribList); + if (surface == EGL_NO_SURFACE) { + return false; + } + + // Create a GL context + EGLContext context = eglCreateContext(display, *eglConfig, + EGL_NO_CONTEXT, + contextAttribs ); + if (context == EGL_NO_CONTEXT ) { + return false; + } + + // Make the context current + if (!eglMakeCurrent(display, surface, surface, context)) { + return false; + } + + *eglDisplay = display; + *eglContext = context; + *eglSurface = surface; + return true; +} + +bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) { + if (EGL_NO_DISPLAY == fDisplay) { + bool bResult = create_ANGLE((HWND)fHWND, + msaaSampleCount, + &fDisplay, + &fContext, + &fSurface, + &fConfig); + if (false == bResult) { + return false; + } + fANGLEInterface.reset(get_angle_gl_interface()); + if (!fANGLEInterface) { + this->detachANGLE(); + return false; + } + GL_CALL(fANGLEInterface, ClearStencil(0)); + GL_CALL(fANGLEInterface, ClearColor(0, 0, 0, 0)); + GL_CALL(fANGLEInterface, StencilMask(0xffffffff)); + GL_CALL(fANGLEInterface, Clear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); + } + if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + this->detachANGLE(); + return false; + } + eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits); + eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount); + + GL_CALL(fANGLEInterface, Viewport(0, 0, SkScalarRoundToInt(this->width()), + SkScalarRoundToInt(this->height()))); + return true; +} + +void SkOSWindow::detachANGLE() { + fANGLEInterface.reset(nullptr); + eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT); + + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + + eglTerminate(fDisplay); + fDisplay = EGL_NO_DISPLAY; +} + +void SkOSWindow::presentANGLE() { + GL_CALL(fANGLEInterface, Flush()); + + eglSwapBuffers(fDisplay, fSurface); +} +#endif // SK_ANGLE + +#endif // SK_SUPPORT_GPU + +// return true on success +bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, + AttachmentInfo* info) { + + // attach doubles as "windowResize" so we need to allo + // already bound states to pass through again + // TODO: split out the resize functionality +// SkASSERT(kNone_BackEndType == fAttached); + bool result = true; + + switch (attachType) { + case kNone_BackEndType: + // nothing to do + break; +#if SK_SUPPORT_GPU + case kNativeGL_BackEndType: + result = attachGL(msaaSampleCount, deepColor, info); + break; +#if SK_ANGLE + case kANGLE_BackEndType: + result = attachANGLE(msaaSampleCount, info); + break; +#endif // SK_ANGLE +#endif // SK_SUPPORT_GPU + default: + SkASSERT(false); + result = false; + break; + } + + if (result) { + fAttached = attachType; + } + + return result; +} + +void SkOSWindow::release() { + switch (fAttached) { + case kNone_BackEndType: + // nothing to do + break; +#if SK_SUPPORT_GPU + case kNativeGL_BackEndType: + detachGL(); + break; +#if SK_ANGLE + case kANGLE_BackEndType: + detachANGLE(); + break; +#endif // SK_ANGLE +#endif // SK_SUPPORT_GPU + default: + SkASSERT(false); + break; + } + fAttached = kNone_BackEndType; +} + +void SkOSWindow::present() { + switch (fAttached) { + case kNone_BackEndType: + // nothing to do + return; +#if SK_SUPPORT_GPU + case kNativeGL_BackEndType: + presentGL(); + break; +#if SK_ANGLE + case kANGLE_BackEndType: + presentANGLE(); + break; +#endif // SK_ANGLE +#endif // SK_SUPPORT_GPU + default: + SkASSERT(false); + break; + } +} + +bool SkOSWindow::makeFullscreen() { + if (fFullscreen) { + return true; + } +#if SK_SUPPORT_GPU + if (fHGLRC) { + this->detachGL(); + } +#endif // SK_SUPPORT_GPU + // This is hacked together from various sources on the web. It can certainly be improved and be + // made more robust. + + // Save current window/resolution information. We do this in case we ever implement switching + // back to windowed mode. + fSavedWindowState.fZoomed = SkToBool(IsZoomed((HWND)fHWND)); + if (fSavedWindowState.fZoomed) { + SendMessage((HWND)fHWND, WM_SYSCOMMAND, SC_RESTORE, 0); + } + fSavedWindowState.fStyle = GetWindowLong((HWND)fHWND, GWL_STYLE); + fSavedWindowState.fExStyle = GetWindowLong((HWND)fHWND, GWL_EXSTYLE); + GetWindowRect((HWND)fHWND, &fSavedWindowState.fRect); + DEVMODE currScreenSettings; + memset(&currScreenSettings,0,sizeof(currScreenSettings)); + currScreenSettings.dmSize = sizeof(currScreenSettings); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &currScreenSettings); + fSavedWindowState.fScreenWidth = currScreenSettings.dmPelsWidth; + fSavedWindowState.fScreenHeight = currScreenSettings.dmPelsHeight; + fSavedWindowState.fScreenBits = currScreenSettings.dmBitsPerPel; + fSavedWindowState.fHWND = fHWND; + + // Try different sizes to find an allowed setting? Use ChangeDisplaySettingsEx? + static const int kWidth = 1280; + static const int kHeight = 1024; + DEVMODE newScreenSettings; + memset(&newScreenSettings, 0, sizeof(newScreenSettings)); + newScreenSettings.dmSize = sizeof(newScreenSettings); + newScreenSettings.dmPelsWidth = kWidth; + newScreenSettings.dmPelsHeight = kHeight; + newScreenSettings.dmBitsPerPel = 32; + newScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; + if (ChangeDisplaySettings(&newScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { + return false; + } + RECT WindowRect; + WindowRect.left = 0; + WindowRect.right = kWidth; + WindowRect.top = 0; + WindowRect.bottom = kHeight; + ShowCursor(FALSE); + AdjustWindowRectEx(&WindowRect, WS_POPUP, FALSE, WS_EX_APPWINDOW); + HWND fsHWND = CreateWindowEx( + WS_EX_APPWINDOW, + fWinInit.fClass, + NULL, + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, + 0, 0, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top, + NULL, + NULL, + fWinInit.fInstance, + NULL + ); + if (!fsHWND) { + return false; + } + // Hide the old window and set the entry in the global mapping for this SkOSWindow to the + // new HWND. + ShowWindow((HWND)fHWND, SW_HIDE); + gHwndToOSWindowMap.remove(fHWND); + fHWND = fsHWND; + gHwndToOSWindowMap.set(fHWND, this); + this->updateSize(); + + fFullscreen = true; + return true; +} + +void SkOSWindow::setVsync(bool enable) { + SkWGLExtensions wgl; + wgl.swapInterval(enable ? 1 : 0); +} + +void SkOSWindow::closeWindow() { + DestroyWindow((HWND)fHWND); + if (fFullscreen) { + DestroyWindow((HWND)fSavedWindowState.fHWND); + } + gHwndToOSWindowMap.remove(fHWND); +} +#endif diff --git a/gfx/skia/skia/src/views/win/skia_win.cpp b/gfx/skia/skia/src/views/win/skia_win.cpp new file mode 100644 index 000000000..df600d771 --- /dev/null +++ b/gfx/skia/skia/src/views/win/skia_win.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#include <tchar.h> + +#include "SkApplication.h" +#include "SkGraphics.h" +#include "SkOSWindow_Win.h" + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +// Returns the main window Win32 class name. +static const TCHAR* register_class(HINSTANCE hInstance) { + WNDCLASSEX wcex; + // The main window class name + static const TCHAR gSZWindowClass[] = _T("SkiaApp"); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = NULL; + wcex.hCursor = NULL; + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = gSZWindowClass; + wcex.hIconSm = NULL; + + RegisterClassEx(&wcex); + + return gSZWindowClass; +} + +static char* tchar_to_utf8(const TCHAR* str) { +#ifdef _UNICODE + int size = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), NULL, 0, NULL, NULL); + char* str8 = (char*) sk_malloc_throw(size+1); + WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), str8, size, NULL, NULL); + str8[size] = '\0'; + return str8; +#else + return _strdup(str); +#endif +} + +// This file can work with GUI or CONSOLE subsystem types since we define _tWinMain and main(). + +static int main_common(HINSTANCE hInstance, int show, int argc, char**argv); + +int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, + int nCmdShow) { + + // convert from lpCmdLine to argc, argv. + char* argv[4096]; + int argc = 0; + TCHAR exename[1024], *next; + int exenameLen = GetModuleFileName(NULL, exename, SK_ARRAY_COUNT(exename)); + // we're ignoring the possibility that the exe name exceeds the exename buffer + (void) exenameLen; + argv[argc++] = tchar_to_utf8(exename); + TCHAR* arg = _tcstok_s(lpCmdLine, _T(" "), &next); + while (arg != NULL) { + argv[argc++] = tchar_to_utf8(arg); + arg = _tcstok_s(NULL, _T(" "), &next); + } + int result = main_common(hInstance, nCmdShow, argc, argv); + for (int i = 0; i < argc; ++i) { + sk_free(argv[i]); + } + return result; +} + +int main(int argc, char**argv) { + SkGraphics::Init(); + return main_common(GetModuleHandle(NULL), SW_SHOW, argc, argv); +} + +static int main_common(HINSTANCE hInstance, int show, int argc, char**argv) { + const TCHAR* windowClass = register_class(hInstance); + + application_init(); + + SkOSWindow::WindowInit winInit; + winInit.fInstance = hInstance; + winInit.fClass = windowClass; + + create_sk_window(&winInit, argc, argv); + SkOSWindow::ForAllWindows([show](void* hWnd, SkOSWindow**) { + ShowWindow((HWND)hWnd, show); + UpdateWindow((HWND)hWnd); } + ); + + MSG msg; + // Main message loop + while (GetMessage(&msg, NULL, 0, 0)) { + if (true) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + application_term(); + + return (int) msg.wParam; +} + +extern SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv); + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_COMMAND: + return DefWindowProc(hWnd, message, wParam, lParam); + case WM_DESTROY: + PostQuitMessage(0); + break; + default: { + SkOSWindow* window = SkOSWindow::GetOSWindowForHWND(hWnd); + if (window && window->wndProc(hWnd, message, wParam, lParam)) { + return 0; + } else { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + } + return 0; +} |