summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/views
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/views')
-rw-r--r--gfx/skia/skia/src/views/SkEvent.cpp512
-rw-r--r--gfx/skia/skia/src/views/SkEventSink.cpp300
-rw-r--r--gfx/skia/skia/src/views/SkOSMenu.cpp263
-rw-r--r--gfx/skia/skia/src/views/SkTagList.cpp61
-rw-r--r--gfx/skia/skia/src/views/SkTagList.h42
-rw-r--r--gfx/skia/skia/src/views/SkTouchGesture.cpp353
-rw-r--r--gfx/skia/skia/src/views/SkView.cpp810
-rw-r--r--gfx/skia/skia/src/views/SkViewPriv.cpp102
-rw-r--r--gfx/skia/skia/src/views/SkViewPriv.h43
-rw-r--r--gfx/skia/skia/src/views/SkWindow.cpp361
-rwxr-xr-xgfx/skia/skia/src/views/ios/SkOSWindow_iOS.mm72
-rw-r--r--gfx/skia/skia/src/views/mac/SkEventNotifier.h13
-rw-r--r--gfx/skia/skia/src/views/mac/SkEventNotifier.mm68
-rw-r--r--gfx/skia/skia/src/views/mac/SkNSView.h56
-rw-r--r--gfx/skia/skia/src/views/mac/SkNSView.mm430
-rw-r--r--gfx/skia/skia/src/views/mac/SkOSWindow_Mac.mm95
-rw-r--r--gfx/skia/skia/src/views/mac/SkOptionsTableView.h39
-rw-r--r--gfx/skia/skia/src/views/mac/SkOptionsTableView.mm297
-rw-r--r--gfx/skia/skia/src/views/mac/SkSampleNSView.h11
-rw-r--r--gfx/skia/skia/src/views/mac/SkSampleNSView.mm31
-rw-r--r--gfx/skia/skia/src/views/mac/SkTextFieldCell.h14
-rw-r--r--gfx/skia/skia/src/views/mac/SkTextFieldCell.m56
-rw-r--r--gfx/skia/skia/src/views/mac/skia_mac.mm126
-rw-r--r--gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp401
-rw-r--r--gfx/skia/skia/src/views/unix/SkOSWindow_Unix.cpp519
-rw-r--r--gfx/skia/skia/src/views/unix/XkeysToSkKeys.h66
-rw-r--r--gfx/skia/skia/src/views/unix/keysym2ucs.c848
-rw-r--r--gfx/skia/skia/src/views/unix/keysym2ucs.h14
-rw-r--r--gfx/skia/skia/src/views/unix/skia_unix.cpp28
-rw-r--r--gfx/skia/skia/src/views/win/SkOSWindow_win.cpp772
-rw-r--r--gfx/skia/skia/src/views/win/skia_win.cpp135
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;
+}