michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsWindowBase.h" michael@0: michael@0: #include "mozilla/MiscEvents.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "WinUtils.h" michael@0: #include "npapi.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::widget; michael@0: michael@0: static const wchar_t kUser32LibName[] = L"user32.dll"; michael@0: bool nsWindowBase::sTouchInjectInitialized = false; michael@0: InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr; michael@0: michael@0: bool michael@0: nsWindowBase::DispatchPluginEvent(const MSG& aMsg) michael@0: { michael@0: if (!PluginHasFocus()) { michael@0: return false; michael@0: } michael@0: WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this); michael@0: nsIntPoint point(0, 0); michael@0: InitEvent(pluginEvent, &point); michael@0: NPEvent npEvent; michael@0: npEvent.event = aMsg.message; michael@0: npEvent.wParam = aMsg.wParam; michael@0: npEvent.lParam = aMsg.lParam; michael@0: pluginEvent.pluginEvent = &npEvent; michael@0: pluginEvent.retargetToFocusedDocument = true; michael@0: return DispatchWindowEvent(&pluginEvent); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: nsWindowBase::InitTouchInjection() michael@0: { michael@0: if (!sTouchInjectInitialized) { michael@0: // Initialize touch injection on the first call michael@0: HMODULE hMod = LoadLibraryW(kUser32LibName); michael@0: if (!hMod) { michael@0: return false; michael@0: } michael@0: michael@0: InitializeTouchInjectionPtr func = michael@0: (InitializeTouchInjectionPtr)GetProcAddress(hMod, "InitializeTouchInjection"); michael@0: if (!func) { michael@0: WinUtils::Log("InitializeTouchInjection not available."); michael@0: return false; michael@0: } michael@0: michael@0: if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) { michael@0: WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d", GetLastError()); michael@0: return false; michael@0: } michael@0: michael@0: sInjectTouchFuncPtr = michael@0: (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput"); michael@0: if (!sInjectTouchFuncPtr) { michael@0: WinUtils::Log("InjectTouchInput not available."); michael@0: return false; michael@0: } michael@0: sTouchInjectInitialized = true; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, michael@0: POINTER_FLAGS aFlags, uint32_t aPressure, michael@0: uint32_t aOrientation) michael@0: { michael@0: if (aId > TOUCH_INJECT_MAX_POINTS) { michael@0: WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS."); michael@0: return false; michael@0: } michael@0: michael@0: POINTER_TOUCH_INFO info; michael@0: memset(&info, 0, sizeof(POINTER_TOUCH_INFO)); michael@0: michael@0: info.touchFlags = TOUCH_FLAG_NONE; michael@0: info.touchMask = TOUCH_MASK_CONTACTAREA|TOUCH_MASK_ORIENTATION|TOUCH_MASK_PRESSURE; michael@0: info.pressure = aPressure; michael@0: info.orientation = aOrientation; michael@0: michael@0: info.pointerInfo.pointerFlags = aFlags; michael@0: info.pointerInfo.pointerType = PT_TOUCH; michael@0: info.pointerInfo.pointerId = aId; michael@0: info.pointerInfo.ptPixelLocation.x = WinUtils::LogToPhys(aPointerScreenPoint.x); michael@0: info.pointerInfo.ptPixelLocation.y = WinUtils::LogToPhys(aPointerScreenPoint.y); michael@0: michael@0: info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2; michael@0: info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2; michael@0: info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2; michael@0: info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2; michael@0: michael@0: if (!sInjectTouchFuncPtr(1, &info)) { michael@0: WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError()); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId, michael@0: nsIWidget::TouchPointerState aPointerState, michael@0: nsIntPoint aPointerScreenPoint, michael@0: double aPointerPressure, michael@0: uint32_t aPointerOrientation) michael@0: { michael@0: if (!InitTouchInjection()) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: bool hover = aPointerState & TOUCH_HOVER; michael@0: bool contact = aPointerState & TOUCH_CONTACT; michael@0: bool remove = aPointerState & TOUCH_REMOVE; michael@0: bool cancel = aPointerState & TOUCH_CANCEL; michael@0: michael@0: // win api expects a value from 0 to 1024. aPointerPressure is a value michael@0: // from 0.0 to 1.0. michael@0: uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024); michael@0: michael@0: // If we already know about this pointer id get it's record michael@0: PointerInfo* info = mActivePointers.Get(aPointerId); michael@0: michael@0: // We know about this pointer, send an update michael@0: if (info) { michael@0: POINTER_FLAGS flags = POINTER_FLAG_UPDATE; michael@0: if (hover) { michael@0: flags |= POINTER_FLAG_INRANGE; michael@0: } else if (contact) { michael@0: flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_INRANGE; michael@0: } else if (remove) { michael@0: flags = POINTER_FLAG_UP; michael@0: // Remove the pointer from our tracking list. This is nsAutPtr wrapped, michael@0: // so shouldn't leak. michael@0: mActivePointers.Remove(aPointerId); michael@0: } michael@0: michael@0: if (cancel) { michael@0: flags |= POINTER_FLAG_CANCELED; michael@0: } michael@0: michael@0: return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, michael@0: pressure, aPointerOrientation) ? michael@0: NS_ERROR_UNEXPECTED : NS_OK; michael@0: } michael@0: michael@0: // Missing init state, error out michael@0: if (remove || cancel) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Create a new pointer michael@0: info = new PointerInfo(aPointerId, aPointerScreenPoint); michael@0: michael@0: POINTER_FLAGS flags = POINTER_FLAG_INRANGE; michael@0: if (contact) { michael@0: flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_DOWN; michael@0: } michael@0: michael@0: mActivePointers.Put(aPointerId, info); michael@0: return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, michael@0: pressure, aPointerOrientation) ? michael@0: NS_ERROR_UNEXPECTED : NS_OK; michael@0: } michael@0: michael@0: // static michael@0: PLDHashOperator michael@0: nsWindowBase::CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr& aInfo, void* aUserArg) michael@0: { michael@0: nsWindowBase* self = static_cast(aUserArg); michael@0: self->InjectTouchPoint(aInfo.get()->mPointerId, aInfo.get()->mPosition, POINTER_FLAG_CANCELED); michael@0: return (PLDHashOperator)(PL_DHASH_NEXT|PL_DHASH_REMOVE); michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowBase::ClearNativeTouchSequence() michael@0: { michael@0: if (!sTouchInjectInitialized) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // cancel all input points michael@0: mActivePointers.Enumerate(CancelTouchPoints, (void*)this); michael@0: michael@0: nsBaseWidget::ClearNativeTouchSequence(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindowBase::DispatchCommandEvent(uint32_t aEventCommand) michael@0: { michael@0: nsCOMPtr command; michael@0: switch (aEventCommand) { michael@0: case APPCOMMAND_BROWSER_BACKWARD: michael@0: command = nsGkAtoms::Back; michael@0: break; michael@0: case APPCOMMAND_BROWSER_FORWARD: michael@0: command = nsGkAtoms::Forward; michael@0: break; michael@0: case APPCOMMAND_BROWSER_REFRESH: michael@0: command = nsGkAtoms::Reload; michael@0: break; michael@0: case APPCOMMAND_BROWSER_STOP: michael@0: command = nsGkAtoms::Stop; michael@0: break; michael@0: case APPCOMMAND_BROWSER_SEARCH: michael@0: command = nsGkAtoms::Search; michael@0: break; michael@0: case APPCOMMAND_BROWSER_FAVORITES: michael@0: command = nsGkAtoms::Bookmarks; michael@0: break; michael@0: case APPCOMMAND_BROWSER_HOME: michael@0: command = nsGkAtoms::Home; michael@0: break; michael@0: case APPCOMMAND_CLOSE: michael@0: command = nsGkAtoms::Close; michael@0: break; michael@0: case APPCOMMAND_FIND: michael@0: command = nsGkAtoms::Find; michael@0: break; michael@0: case APPCOMMAND_HELP: michael@0: command = nsGkAtoms::Help; michael@0: break; michael@0: case APPCOMMAND_NEW: michael@0: command = nsGkAtoms::New; michael@0: break; michael@0: case APPCOMMAND_OPEN: michael@0: command = nsGkAtoms::Open; michael@0: break; michael@0: case APPCOMMAND_PRINT: michael@0: command = nsGkAtoms::Print; michael@0: break; michael@0: case APPCOMMAND_SAVE: michael@0: command = nsGkAtoms::Save; michael@0: break; michael@0: case APPCOMMAND_FORWARD_MAIL: michael@0: command = nsGkAtoms::ForwardMail; michael@0: break; michael@0: case APPCOMMAND_REPLY_TO_MAIL: michael@0: command = nsGkAtoms::ReplyToMail; michael@0: break; michael@0: case APPCOMMAND_SEND_MAIL: michael@0: command = nsGkAtoms::SendMail; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this); michael@0: michael@0: InitEvent(event); michael@0: return DispatchWindowEvent(&event); michael@0: } michael@0: michael@0: bool michael@0: nsWindowBase::HandleAppCommandMsg(WPARAM aWParam, michael@0: LPARAM aLParam, michael@0: LRESULT *aRetValue) michael@0: { michael@0: uint32_t appCommand = GET_APPCOMMAND_LPARAM(aLParam); michael@0: uint32_t contentCommandMessage = NS_EVENT_NULL; michael@0: // XXX After we implement KeyboardEvent.key, we should dispatch the michael@0: // key event if (GET_DEVICE_LPARAM(lParam) == FAPPCOMMAND_KEY) is. michael@0: switch (appCommand) michael@0: { michael@0: case APPCOMMAND_BROWSER_BACKWARD: michael@0: case APPCOMMAND_BROWSER_FORWARD: michael@0: case APPCOMMAND_BROWSER_REFRESH: michael@0: case APPCOMMAND_BROWSER_STOP: michael@0: case APPCOMMAND_BROWSER_SEARCH: michael@0: case APPCOMMAND_BROWSER_FAVORITES: michael@0: case APPCOMMAND_BROWSER_HOME: michael@0: case APPCOMMAND_CLOSE: michael@0: case APPCOMMAND_FIND: michael@0: case APPCOMMAND_HELP: michael@0: case APPCOMMAND_NEW: michael@0: case APPCOMMAND_OPEN: michael@0: case APPCOMMAND_PRINT: michael@0: case APPCOMMAND_SAVE: michael@0: case APPCOMMAND_FORWARD_MAIL: michael@0: case APPCOMMAND_REPLY_TO_MAIL: michael@0: case APPCOMMAND_SEND_MAIL: michael@0: // We shouldn't consume the message always because if we don't handle michael@0: // the message, the sender (typically, utility of keyboard or mouse) michael@0: // may send other key messages which indicate well known shortcut key. michael@0: if (DispatchCommandEvent(appCommand)) { michael@0: // tell the driver that we handled the event michael@0: *aRetValue = 1; michael@0: return true; michael@0: } michael@0: break; michael@0: michael@0: // Use content command for following commands: michael@0: case APPCOMMAND_COPY: michael@0: contentCommandMessage = NS_CONTENT_COMMAND_COPY; michael@0: break; michael@0: case APPCOMMAND_CUT: michael@0: contentCommandMessage = NS_CONTENT_COMMAND_CUT; michael@0: break; michael@0: case APPCOMMAND_PASTE: michael@0: contentCommandMessage = NS_CONTENT_COMMAND_PASTE; michael@0: break; michael@0: case APPCOMMAND_REDO: michael@0: contentCommandMessage = NS_CONTENT_COMMAND_REDO; michael@0: break; michael@0: case APPCOMMAND_UNDO: michael@0: contentCommandMessage = NS_CONTENT_COMMAND_UNDO; michael@0: break; michael@0: } michael@0: michael@0: if (contentCommandMessage) { michael@0: WidgetContentCommandEvent contentCommand(true, contentCommandMessage, michael@0: this); michael@0: DispatchWindowEvent(&contentCommand); michael@0: // tell the driver that we handled the event michael@0: *aRetValue = 1; michael@0: return true; michael@0: } michael@0: michael@0: // default = false - tell the driver that the event was not handled michael@0: return false; michael@0: }