From 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:30:52 -0800 Subject: auto import from //depot/cupcake/@135843 --- WebKitTools/DumpRenderTree/win/EventSender.cpp | 597 +++++++++++++++++++++++++ 1 file changed, 597 insertions(+) create mode 100644 WebKitTools/DumpRenderTree/win/EventSender.cpp (limited to 'WebKitTools/DumpRenderTree/win/EventSender.cpp') diff --git a/WebKitTools/DumpRenderTree/win/EventSender.cpp b/WebKitTools/DumpRenderTree/win/EventSender.cpp new file mode 100644 index 0000000..efecc03 --- /dev/null +++ b/WebKitTools/DumpRenderTree/win/EventSender.cpp @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "EventSender.h" + +#include "DraggingInfo.h" +#include "DumpRenderTree.h" + +#include +#include +#include +#include +#include +#include +#include + +#define WM_DRT_SEND_QUEUED_EVENT (WM_APP+1) + +static bool down; +static bool dragMode = true; +static bool replayingSavedEvents; +static int timeOffset; +static POINT lastMousePosition; + +struct DelayedMessage { + MSG msg; + unsigned delay; +}; + +static DelayedMessage msgQueue[1024]; +static unsigned endOfQueue; +static unsigned startOfQueue; + +static bool didDragEnter; +DraggingInfo* draggingInfo = 0; + +static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) +{ + return JSValueMakeBoolean(context, dragMode); +} + +static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) +{ + dragMode = JSValueToBoolean(context, value); + return true; +} + +static JSValueRef getConstantCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) +{ + if (JSStringIsEqualToUTF8CString(propertyName, "WM_KEYDOWN")) + return JSValueMakeNumber(context, WM_KEYDOWN); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_KEYUP")) + return JSValueMakeNumber(context, WM_KEYUP); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_CHAR")) + return JSValueMakeNumber(context, WM_CHAR); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_DEADCHAR")) + return JSValueMakeNumber(context, WM_DEADCHAR); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYDOWN")) + return JSValueMakeNumber(context, WM_SYSKEYDOWN); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYUP")) + return JSValueMakeNumber(context, WM_SYSKEYUP); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSCHAR")) + return JSValueMakeNumber(context, WM_SYSCHAR); + if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSDEADCHAR")) + return JSValueMakeNumber(context, WM_SYSDEADCHAR); + ASSERT_NOT_REACHED(); + return JSValueMakeUndefined(context); +} + +static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount > 0) { + msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception); + ASSERT(!exception || !*exception); + } + + return JSValueMakeUndefined(context); +} + +static DWORD currentEventTime() +{ + return ::GetTickCount() + timeOffset; +} + +static MSG makeMsg(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + MSG result = {0}; + result.hwnd = hwnd; + result.message = message; + result.wParam = wParam; + result.lParam = lParam; + result.time = currentEventTime(); + result.pt = lastMousePosition; + + return result; +} + +static LRESULT dispatchMessage(const MSG* msg) +{ + ASSERT(msg); + ::TranslateMessage(msg); + return ::DispatchMessage(msg); +} + +static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + down = true; + MSG msg = makeMsg(webViewWindow, WM_RBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); + dispatchMessage(&msg); + down = false; + msg = makeMsg(webViewWindow, WM_RBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); + dispatchMessage(&msg); + + return JSValueMakeUndefined(context); +} + +static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + down = true; + MSG msg = makeMsg(webViewWindow, WM_LBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); + if (!msgQueue[endOfQueue].delay) + dispatchMessage(&msg); + else { + // replaySavedEvents has the required logic to make leapForward delays work + msgQueue[endOfQueue++].msg = msg; + replaySavedEvents(); + } + + return JSValueMakeUndefined(context); +} + +static inline POINTL pointl(const POINT& point) +{ + POINTL result; + result.x = point.x; + result.y = point.y; + return result; +} + +static void doMouseUp(MSG msg) +{ + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + dispatchMessage(&msg); + down = false; + + if (draggingInfo) { + COMPtr webView; + COMPtr webViewDropTarget; + if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) { + POINT screenPoint = msg.pt; + DWORD effect = 0; + ::ClientToScreen(webViewWindow, &screenPoint); + if (!didDragEnter) { + webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); + didDragEnter = true; + } + HRESULT hr = draggingInfo->dropSource()->QueryContinueDrag(0, 0); + webViewDropTarget->DragOver(0, pointl(screenPoint), &effect); + if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) { + DWORD effect = 0; + webViewDropTarget->Drop(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); + } else + webViewDropTarget->DragLeave(); + + delete draggingInfo; + draggingInfo = 0; + } + } +} + +static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + MSG msg = makeMsg(webViewWindow, WM_LBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); + + if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) { + msgQueue[endOfQueue++].msg = msg; + replaySavedEvents(); + } else + doMouseUp(msg); + + return JSValueMakeUndefined(context); +} + +static void doMouseMove(MSG msg) +{ + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + dispatchMessage(&msg); + + if (down && draggingInfo) { + POINT screenPoint = msg.pt; + ::ClientToScreen(webViewWindow, &screenPoint); + + IWebView* webView; + COMPtr webViewDropTarget; + if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) { + DWORD effect = 0; + if (didDragEnter) + webViewDropTarget->DragOver(MK_LBUTTON, pointl(screenPoint), &effect); + else { + webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); + didDragEnter = true; + } + draggingInfo->dropSource()->GiveFeedback(effect); + } + } +} + +static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount < 2) + return JSValueMakeUndefined(context); + + lastMousePosition.x = (int)JSValueToNumber(context, arguments[0], exception); + ASSERT(!exception || !*exception); + lastMousePosition.y = (int)JSValueToNumber(context, arguments[1], exception); + ASSERT(!exception || !*exception); + + MSG msg = makeMsg(webViewWindow, WM_MOUSEMOVE, down ? MK_LBUTTON : 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); + + if (dragMode && down && !replayingSavedEvents) { + msgQueue[endOfQueue++].msg = msg; + return JSValueMakeUndefined(context); + } + + doMouseMove(msg); + + return JSValueMakeUndefined(context); +} + +void replaySavedEvents() +{ + replayingSavedEvents = true; + + MSG msg = { 0 }; + + while (startOfQueue < endOfQueue && !msgQueue[startOfQueue].delay) { + msg = msgQueue[startOfQueue++].msg; + switch (msg.message) { + case WM_LBUTTONUP: + doMouseUp(msg); + break; + case WM_MOUSEMOVE: + doMouseMove(msg); + break; + case WM_LBUTTONDOWN: + dispatchMessage(&msg); + break; + default: + // Not reached + break; + } + } + + int numQueuedMessages = endOfQueue - startOfQueue; + if (!numQueuedMessages) { + startOfQueue = 0; + endOfQueue = 0; + replayingSavedEvents = false; + ASSERT(!down); + return; + } + + if (msgQueue[startOfQueue].delay) { + ::Sleep(msgQueue[startOfQueue].delay); + msgQueue[startOfQueue].delay = 0; + } + + ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0); + while (::GetMessage(&msg, webViewWindow, 0, 0)) { + // FIXME: Why do we get a WM_MOUSELEAVE? it breaks tests + if (msg.message == WM_MOUSELEAVE) + continue; + if (msg.message != WM_DRT_SEND_QUEUED_EVENT) { + dispatchMessage(&msg); + continue; + } + msg = msgQueue[startOfQueue++].msg; + switch (msg.message) { + case WM_LBUTTONUP: + doMouseUp(msg); + break; + case WM_MOUSEMOVE: + doMouseMove(msg); + break; + case WM_LBUTTONDOWN: + dispatchMessage(&msg); + break; + default: + // Not reached + break; + } + if (startOfQueue >= endOfQueue) + break; + ::Sleep(msgQueue[startOfQueue].delay); + msgQueue[startOfQueue].delay = 0; + ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0); + } + startOfQueue = 0; + endOfQueue = 0; + + replayingSavedEvents = false; +} + +static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount < 1) + return JSValueMakeUndefined(context); + + static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length"); + + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + JSStringRef character = JSValueToStringCopy(context, arguments[0], exception); + ASSERT(!*exception); + int virtualKeyCode; + int charCode = 0; + int keyData = 1; + bool needsShiftKeyModifier = false; + if (JSStringIsEqualToUTF8CString(character, "leftArrow")) { + virtualKeyCode = VK_LEFT; + keyData += KF_EXTENDED << 16; // In this case, extended means "not keypad". + } else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) { + virtualKeyCode = VK_RIGHT; + keyData += KF_EXTENDED << 16; + } else if (JSStringIsEqualToUTF8CString(character, "upArrow")) { + virtualKeyCode = VK_UP; + keyData += KF_EXTENDED << 16; + } else if (JSStringIsEqualToUTF8CString(character, "downArrow")) { + virtualKeyCode = VK_DOWN; + keyData += KF_EXTENDED << 16; + } else if (JSStringIsEqualToUTF8CString(character, "pageUp")) + virtualKeyCode = VK_PRIOR; + else if (JSStringIsEqualToUTF8CString(character, "pageDown")) + virtualKeyCode = VK_NEXT; + else if (JSStringIsEqualToUTF8CString(character, "home")) + virtualKeyCode = VK_HOME; + else if (JSStringIsEqualToUTF8CString(character, "end")) + virtualKeyCode = VK_END; + else if (JSStringIsEqualToUTF8CString(character, "delete")) + virtualKeyCode = VK_BACK; + else { + charCode = JSStringGetCharactersPtr(character)[0]; + virtualKeyCode = LOBYTE(VkKeyScan(charCode)); + if (WTF::isASCIIUpper(charCode)) + needsShiftKeyModifier = true; + } + JSStringRelease(character); + + BYTE keyState[256]; + if (argumentCount > 1 || needsShiftKeyModifier) { + ::GetKeyboardState(keyState); + + BYTE newKeyState[256]; + memcpy(newKeyState, keyState, sizeof(keyState)); + + if (needsShiftKeyModifier) + newKeyState[VK_SHIFT] = 0x80; + + if (argumentCount > 1) { + JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], exception); + if (modifiersArray) { + int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0); + for (int i = 0; i < modifiersCount; ++i) { + JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0); + JSStringRef string = JSValueToStringCopy(context, value, 0); + if (JSStringIsEqualToUTF8CString(string, "ctrlKey")) + newKeyState[VK_CONTROL] = 0x80; + else if (JSStringIsEqualToUTF8CString(string, "shiftKey")) + newKeyState[VK_SHIFT] = 0x80; + else if (JSStringIsEqualToUTF8CString(string, "altKey")) + newKeyState[VK_MENU] = 0x80; + + JSStringRelease(string); + } + } + } + + ::SetKeyboardState(newKeyState); + } + + MSG msg = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYDOWN : WM_KEYDOWN, virtualKeyCode, keyData); + if (virtualKeyCode != 255) + dispatchMessage(&msg); + else { + // For characters that do not exist in the active keyboard layout, + // ::Translate will not work, so we post an WM_CHAR event ourselves. + ::PostMessage(webViewWindow, WM_CHAR, charCode, 0); + } + + // Tests expect that all messages are processed by the time keyDown() returns. + if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE) || ::PeekMessage(&msg, webViewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE)) + ::DispatchMessage(&msg); + + MSG msgUp = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYUP : WM_KEYUP, virtualKeyCode, keyData); + ::DispatchMessage(&msgUp); + + if (argumentCount > 1 || needsShiftKeyModifier) + ::SetKeyboardState(keyState); + + return JSValueMakeUndefined(context); +} + +// eventSender.dispatchMessage(message, wParam, lParam, time = currentEventTime(), x = lastMousePosition.x, y = lastMousePosition.y) +static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount < 3) + return JSValueMakeUndefined(context); + + COMPtr framePrivate; + if (SUCCEEDED(frame->QueryInterface(&framePrivate))) + framePrivate->layout(); + + MSG msg = {}; + msg.hwnd = webViewWindow; + msg.message = JSValueToNumber(context, arguments[0], exception); + ASSERT(!*exception); + msg.wParam = JSValueToNumber(context, arguments[1], exception); + ASSERT(!*exception); + msg.lParam = static_cast(JSValueToNumber(context, arguments[2], exception)); + ASSERT(!*exception); + if (argumentCount >= 4) { + msg.time = JSValueToNumber(context, arguments[3], exception); + ASSERT(!*exception); + } + if (!msg.time) + msg.time = currentEventTime(); + if (argumentCount >= 6) { + msg.pt.x = JSValueToNumber(context, arguments[4], exception); + ASSERT(!*exception); + msg.pt.y = JSValueToNumber(context, arguments[5], exception); + ASSERT(!*exception); + } else + msg.pt = lastMousePosition; + + ::DispatchMessage(&msg); + + return JSValueMakeUndefined(context); +} + +static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return JSValueMakeUndefined(context); + + COMPtr webIBActions(Query, webView); + if (!webIBActions) + return JSValueMakeUndefined(context); + + webIBActions->makeTextLarger(0); + return JSValueMakeUndefined(context); +} + +static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return JSValueMakeUndefined(context); + + COMPtr webIBActions(Query, webView); + if (!webIBActions) + return JSValueMakeUndefined(context); + + webIBActions->makeTextSmaller(0); + return JSValueMakeUndefined(context); +} + +static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return JSValueMakeUndefined(context); + + COMPtr webIBActions(Query, webView); + if (!webIBActions) + return JSValueMakeUndefined(context); + + webIBActions->zoomPageIn(0); + return JSValueMakeUndefined(context); +} + +static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return JSValueMakeUndefined(context); + + COMPtr webIBActions(Query, webView); + if (!webIBActions) + return JSValueMakeUndefined(context); + + webIBActions->zoomPageOut(0); + return JSValueMakeUndefined(context); +} + +static JSStaticFunction staticFunctions[] = { + { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "dispatchMessage", dispatchMessageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { 0, 0, 0 } +}; + +static JSStaticValue staticValues[] = { + { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone }, + { "WM_KEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_KEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_CHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_DEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_SYSKEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_SYSKEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_SYSCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { "WM_SYSDEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, + { 0, 0, 0, 0 } +}; + +static JSClassRef getClass(JSContextRef context) +{ + static JSClassRef eventSenderClass = 0; + + if (!eventSenderClass) { + JSClassDefinition classDefinition = {0}; + classDefinition.staticFunctions = staticFunctions; + classDefinition.staticValues = staticValues; + + eventSenderClass = JSClassCreate(&classDefinition); + } + + return eventSenderClass; +} + +JSObjectRef makeEventSender(JSContextRef context) +{ + down = false; + dragMode = true; + replayingSavedEvents = false; + timeOffset = 0; + lastMousePosition.x = 0; + lastMousePosition.y = 0; + + endOfQueue = 0; + startOfQueue = 0; + + didDragEnter = false; + draggingInfo = 0; + + return JSObjectMake(context, getClass(context), 0); +} -- cgit v1.1