/* vim: set sw=2 sts=2 et cin: */ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //============================================================================= /* * This file is divided into the following major sections: * * - Macros * - Variables & Forward declarations * - nsWindow Create / Destroy * - Standard Window Operations * - Window Positioning * - Plugin Operations * - Top-level (frame window) Operations * - Mouse Pointers * - Rollup Event Handlers * - nsWindow's Window Procedure * - Window Message Handlers * - Drag & Drop - Target methods * - Keyboard Handlers * - IME * - Event Dispatch * */ //============================================================================= #include "nsWindow.h" #include "os2FrameWindow.h" #include "gfxContext.h" #include "gfxOS2Surface.h" #include "imgIContainer.h" #include "npapi.h" #include "nsDragService.h" #include "nsGfxCIID.h" #include "nsHashKeys.h" #include "nsIRollupListener.h" #include "nsIScreenManager.h" #include "nsOS2Uni.h" #include "nsTHashtable.h" #include "nsGkAtoms.h" #include "wdgtos2rc.h" #include "nsIDOMWheelEvent.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" #include "mozilla/TextEvents.h" #include #include // std::max using namespace mozilla; using namespace mozilla::widget; //============================================================================= // Macros //============================================================================= // Drag and Drop // d&d flags - actions that might cause problems during d&d #define ACTION_PAINT 1 #define ACTION_DRAW 2 #define ACTION_SCROLL 3 #define ACTION_SHOW 4 #define ACTION_PTRPOS 5 // d&d status - shorten these references a bit #define DND_None (nsIDragSessionOS2::DND_NONE) #define DND_NativeDrag (nsIDragSessionOS2::DND_NATIVEDRAG) #define DND_MozDrag (nsIDragSessionOS2::DND_MOZDRAG) #define DND_InDrop (nsIDragSessionOS2::DND_INDROP) #define DND_DragStatus (nsIDragSessionOS2::DND_DRAGSTATUS) #define DND_DispatchEnterEvent (nsIDragSessionOS2::DND_DISPATCHENTEREVENT) #define DND_DispatchEvent (nsIDragSessionOS2::DND_DISPATCHEVENT) #define DND_GetDragoverResult (nsIDragSessionOS2::DND_GETDRAGOVERRESULT) #define DND_ExitSession (nsIDragSessionOS2::DND_EXITSESSION) //----------------------------------------------------------------------------- // App Command messages for IntelliMouse and Natural Keyboard Pro #define WM_APPCOMMAND 0x0319 #define APPCOMMAND_BROWSER_BACKWARD 1 #define APPCOMMAND_BROWSER_FORWARD 2 #define APPCOMMAND_BROWSER_REFRESH 3 #define APPCOMMAND_BROWSER_STOP 4 //----------------------------------------------------------------------------- // Keyboard-related macros // Used for character-to-keycode translation #define PMSCAN_PADMULT 0x37 #define PMSCAN_PAD7 0x47 #define PMSCAN_PAD8 0x48 #define PMSCAN_PAD9 0x49 #define PMSCAN_PADMINUS 0x4A #define PMSCAN_PAD4 0x4B #define PMSCAN_PAD5 0x4C #define PMSCAN_PAD6 0x4D #define PMSCAN_PADPLUS 0x4E #define PMSCAN_PAD1 0x4F #define PMSCAN_PAD2 0x50 #define PMSCAN_PAD3 0x51 #define PMSCAN_PAD0 0x52 #define PMSCAN_PADPERIOD 0x53 #define PMSCAN_PADDIV 0x5c #define isNumPadScanCode(scanCode) !((scanCode < PMSCAN_PAD7) || \ (scanCode > PMSCAN_PADPERIOD) || \ (scanCode == PMSCAN_PADMULT) || \ (scanCode == PMSCAN_PADDIV) || \ (scanCode == PMSCAN_PADMINUS) || \ (scanCode == PMSCAN_PADPLUS)) #define isNumlockOn (WinGetKeyState(HWND_DESKTOP, VK_NUMLOCK) & 0x0001) #define isKeyDown(vk) ((WinGetKeyState(HWND_DESKTOP,vk) & 0x8000) == 0x8000) //----------------------------------------------------------------------------- // Miscellanea // extract X & Y from a mouse msg mparam #define XFROMMP(m) (SHORT(LOUSHORT(m))) #define YFROMMP(m) (SHORT(HIUSHORT(m))) // make these methods seem more appropriate in context #define PM2NS_PARENT NS2PM_PARENT #define PM2NS NS2PM // used to identify plugin widgets (copied from nsPluginNativeWindowOS2.cpp) #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION \ "MozillaPluginWindowPropertyAssociation" // name of the window class used to clip plugins #define kClipWndClass "nsClipWnd" // IME caret not exist #define NO_IME_CARET (static_cast(-1)) //----------------------------------------------------------------------------- // Debug #ifdef DEBUG_FOCUS #define DEBUGFOCUS(what) fprintf(stderr, "[%8x] %8lx (%02d) "#what"\n", \ (int)this, mWnd, mWindowIdentifier) #else #define DEBUGFOCUS(what) #endif //============================================================================= // Variables & Forward declarations //============================================================================= // Miscellaneous global flags uint32_t gOS2Flags = 0; // Mouse pointers static HPOINTER sPtrArray[IDC_COUNT]; // location of last MB1 down - used for mouse-based copy/paste static POINTS sLastButton1Down = {0,0}; // set when any nsWindow is being dragged over static uint32_t sDragStatus = 0; #ifdef DEBUG_FOCUS int currentWindowIdentifier = 0; #endif // IME stuffs static HMODULE sIm32Mod = NULLHANDLE; static APIRET (APIENTRY *spfnImGetInstance)(HWND, PHIMI); static APIRET (APIENTRY *spfnImReleaseInstance)(HWND, HIMI); static APIRET (APIENTRY *spfnImGetConversionString)(HIMI, ULONG, PVOID, PULONG); static APIRET (APIENTRY *spfnImGetResultString)(HIMI, ULONG, PVOID, PULONG); static APIRET (APIENTRY *spfnImRequestIME)(HIMI, ULONG, ULONG, ULONG); //----------------------------------------------------------------------------- static uint32_t WMChar2KeyCode(MPARAM mp1, MPARAM mp2); //============================================================================= // nsWindow Create / Destroy //============================================================================= nsWindow::nsWindow() : nsBaseWidget() { mWnd = 0; mParent = 0; mFrame = 0; mWindowType = eWindowType_toplevel; mBorderStyle = eBorderStyle_default; mWindowState = nsWindowState_ePrecreate; mOnDestroyCalled = false; mIsDestroying = false; mInSetFocus = false; mNoPaint = false; mDragHps = 0; mDragStatus = 0; mClipWnd = 0; mCssCursorHPtr = 0; mThebesSurface = 0; mIsComposing = false; if (!gOS2Flags) { InitGlobals(); } } //----------------------------------------------------------------------------- nsWindow::~nsWindow() { // How destruction works: A call of Destroy() destroys the PM window. This // triggers an OnDestroy(), which frees resources. If not Destroy'd at // delete time, Destroy() gets called anyway. // NOTE: Calling virtual functions from destructors is bad; they always // bind in the current object (ie. as if they weren't virtual). It // may even be illegal to call them from here. mIsDestroying = true; if (mCssCursorHPtr) { WinDestroyPointer(mCssCursorHPtr); mCssCursorHPtr = 0; } // If the widget was released without calling Destroy() then // the native window still exists, and we need to destroy it if (!(mWindowState & nsWindowState_eDead)) { mWindowState |= nsWindowState_eDoingDelete; mWindowState &= ~(nsWindowState_eLive | nsWindowState_ePrecreate | nsWindowState_eInCreate); Destroy(); } // Once a plugin window has been destroyed, // its parent, the clipping window, can be destroyed. if (mClipWnd) { WinDestroyWindow(mClipWnd); mClipWnd = 0; } // If it exists, destroy our os2FrameWindow helper object. if (mFrame) { delete mFrame; mFrame = 0; } } //----------------------------------------------------------------------------- // Init Module-level variables. // static void nsWindow::InitGlobals() { gOS2Flags = kIsInitialized; // Register the MozillaWindowClass with PM. WinRegisterClass(0, kWindowClassName, fnwpNSWindow, 0, 8); // Register the dummy window class used to clip plugins. WinRegisterClass(0, kClipWndClass, WinDefWindowProc, 0, 4); // Load the mouse pointers from the dll containing 'gOS2Flags'. HMODULE hModResources = 0; DosQueryModFromEIP(&hModResources, 0, 0, 0, 0, (ULONG)&gOS2Flags); for (int i = 0; i < IDC_COUNT; i++) { sPtrArray[i] = WinLoadPointer(HWND_DESKTOP, hModResources, IDC_BASE+i); } // Work out if the system is DBCS. char buffer[16]; COUNTRYCODE cc = { 0 }; DosQueryDBCSEnv(sizeof(buffer), &cc, buffer); if (buffer[0] || buffer[1]) { gOS2Flags |= kIsDBCS; } // This is ugly. The Thinkpad TrackPoint driver checks to see whether // or not a window actually has a scroll bar as a child before sending // it scroll messages. Needless to say, no Mozilla window has real scroll // bars. So if you have the "os2.trackpoint" preference set, we put an // invisible scroll bar on every child window so we can scroll. if (Preferences::GetBool("os2.trackpoint", false)) { gOS2Flags |= kIsTrackPoint; } InitIME(); } //----------------------------------------------------------------------------- // Determine whether to use IME static void InitIME() { if (!getenv("MOZ_IME_OVERTHESPOT")) { CHAR szName[CCHMAXPATH]; ULONG rc; rc = DosLoadModule(szName, sizeof(szName), "os2im", &sIm32Mod); if (!rc) rc = DosQueryProcAddr(sIm32Mod, 104, nullptr, (PFN *)&spfnImGetInstance); if (!rc) rc = DosQueryProcAddr(sIm32Mod, 106, nullptr, (PFN *)&spfnImReleaseInstance); if (!rc) rc = DosQueryProcAddr(sIm32Mod, 118, nullptr, (PFN *)&spfnImGetConversionString); if (!rc) rc = DosQueryProcAddr(sIm32Mod, 122, nullptr, (PFN *)&spfnImGetResultString); if (!rc) rc = DosQueryProcAddr(sIm32Mod, 131, nullptr, (PFN *)&spfnImRequestIME); if (rc) { DosFreeModule(sIm32Mod); sIm32Mod = NULLHANDLE; } } } //----------------------------------------------------------------------------- // Release Module-level variables. // static void nsWindow::ReleaseGlobals() { for (int i = 0; i < IDC_COUNT; i++) { WinDestroyPointer(sPtrArray[i]); } } //----------------------------------------------------------------------------- // Init an nsWindow & create the appropriate native window. NS_METHOD nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, const nsIntRect& aRect, EVENT_CALLBACK aHandleEventFunction, nsDeviceContext* aContext, nsWidgetInitData* aInitData) { mWindowState = nsWindowState_eInCreate; // Identify the parent's nsWindow & native window. Only one of these // should be supplied. Note: only nsWindow saves pParent as mParent; // os2FrameWindow discards it since toplevel widgets have no parent. HWND hParent; nsWindow* pParent; if (aParent) { hParent = (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW); pParent = (nsWindow*)aParent; } else { if (aNativeParent && (HWND)aNativeParent != HWND_DESKTOP) { hParent = (HWND)aNativeParent; pParent = GetNSWindowPtr(hParent); } else { hParent = HWND_DESKTOP; pParent = 0; } } BaseCreate(aParent, aRect, aHandleEventFunction, aContext, aInitData); #ifdef DEBUG_FOCUS mWindowIdentifier = currentWindowIdentifier; currentWindowIdentifier++; #endif // Some basic initialization. if (aInitData) { // Suppress creation of a Thebes surface for windows that will never // be painted because they're always covered by another window. if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_invisible) { mNoPaint = true; } // Popup windows should not have an nsWindow parent. else if (mWindowType == eWindowType_popup) { pParent = 0; } } // For toplevel windows, create an instance of our helper class, // then have it create a frame & client window; otherwise, // call our own CreateWindow() method to create a child window. if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog || mWindowType == eWindowType_invisible) { mFrame = new os2FrameWindow(this); NS_ENSURE_TRUE(mFrame, NS_ERROR_FAILURE); mWnd = mFrame->CreateFrameWindow(pParent, hParent, aRect, mWindowType, mBorderStyle); NS_ENSURE_TRUE(mWnd, NS_ERROR_FAILURE); } else { nsresult rv = CreateWindow(pParent, hParent, aRect, aInitData); NS_ENSURE_SUCCESS(rv, rv); } // Store a pointer to this object in the window's extra bytes. SetNSWindowPtr(mWnd, this); // Finalize the widget creation process. WidgetGUIEvent event(true, NS_CREATE, this); InitEvent(event); DispatchWindowEvent(&event); mWindowState = nsWindowState_eLive; return NS_OK; } //----------------------------------------------------------------------------- // Create a native window for an nsWindow object. nsresult nsWindow::CreateWindow(nsWindow* aParent, HWND aParentWnd, const nsIntRect& aRect, nsWidgetInitData* aInitData) { // For pop-ups, the Desktop is the parent and aParentWnd is the owner. HWND hOwner = 0; if (mWindowType == eWindowType_popup && aParentWnd != HWND_DESKTOP) { hOwner = aParentWnd; aParentWnd = HWND_DESKTOP; } // While we comply with the clipSiblings flag, we always set // clipChildren regardless of the flag for performance reasons. uint32_t style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; if (aInitData && !aInitData->clipSiblings) { style &= ~WS_CLIPSIBLINGS; } // Create the window hidden; it will be resized below. mWnd = WinCreateWindow(aParentWnd, kWindowClassName, 0, style, 0, 0, 0, 0, hOwner, HWND_TOP, 0, 0, 0); NS_ENSURE_TRUE(mWnd, NS_ERROR_FAILURE); // If a TrackPoint is in use, create dummy scrollbars. // XXX Popups may need this also to scroll comboboxes. if ((gOS2Flags & kIsTrackPoint) && mWindowType == eWindowType_child) { WinCreateWindow(mWnd, WC_SCROLLBAR, 0, SBS_VERT, 0, 0, 0, 0, mWnd, HWND_TOP, FID_VERTSCROLL, 0, 0); } // Store the window's dimensions, then resize accordingly. mBounds = aRect; nsIntRect parRect; if (aParent) { aParent->GetBounds(parRect); } else { parRect.height = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN); } WinSetWindowPos(mWnd, 0, aRect.x, parRect.height - aRect.y - aRect.height, aRect.width, aRect.height, SWP_SIZE | SWP_MOVE); // Store the widget's parent and add it to the parent's list of children. // Don't ADDREF mParent because AddChild() ADDREFs us. mParent = aParent; if (mParent) { mParent->AddChild(this); } DEBUGFOCUS(Create nsWindow); return NS_OK; } //----------------------------------------------------------------------------- // Close this nsWindow. NS_METHOD nsWindow::Destroy() { // avoid calling into other objects if we're being deleted, 'cos // they must have no references to us. if ((mWindowState & nsWindowState_eLive) && mParent) { nsBaseWidget::Destroy(); } // just to be safe. If we're going away and for some reason we're still // the rollup widget, rollup and turn off capture. nsIRollupListener* rollupListener = GetActiveRollupListener(); nsCOMPtr rollupWidget; if (rollupListener) { rollupWidget = rollupListener->GetRollupWidget(); } if (this == rollupWidget) { rollupListener->Rollup(UINT32_MAX); CaptureRollupEvents(nullptr, false, true); } HWND hMain = GetMainWindow(); if (hMain) { DEBUGFOCUS(Destroy); if (hMain == WinQueryFocus(HWND_DESKTOP)) { WinSetFocus(HWND_DESKTOP, WinQueryWindow(hMain, QW_PARENT)); } WinDestroyWindow(hMain); } return NS_OK; } //============================================================================= // Standard Window Operations //============================================================================= // This can't be inlined in nsWindow.h because it doesn't know about // GetFrameWnd(). inline HWND nsWindow::GetMainWindow() { return mFrame ? mFrame->GetFrameWnd() : mWnd; } //----------------------------------------------------------------------------- // Inline this here for consistency (and a cleaner looking .h). // static inline nsWindow* nsWindow::GetNSWindowPtr(HWND aWnd) { return (nsWindow*)WinQueryWindowPtr(aWnd, QWL_NSWINDOWPTR); } //----------------------------------------------------------------------------- // static inline bool nsWindow::SetNSWindowPtr(HWND aWnd, nsWindow* aPtr) { return WinSetWindowPtr(aWnd, QWL_NSWINDOWPTR, aPtr); } //----------------------------------------------------------------------------- nsIWidget* nsWindow::GetParent() { // if this window isn't supposed to have a parent or it doesn't have // a parent, or if it or its parent is being destroyed, return null if (mFrame || mIsDestroying || mOnDestroyCalled || !mParent || mParent->mIsDestroying) { return 0; } return mParent; } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Enable(bool aState) { HWND hMain = GetMainWindow(); if (hMain) { WinEnableWindow(hMain, aState); } return NS_OK; } //----------------------------------------------------------------------------- bool nsWindow::IsEnabled() const { HWND hMain = GetMainWindow(); return !hMain || WinIsWindowEnabled(hMain); } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Show(bool aState) { if (mFrame) { return mFrame->Show(aState); } if (mWnd) { if (aState) { // don't try to show new windows (e.g. the Bookmark menu) // during a native dragover because they'll remain invisible; if (CheckDragStatus(ACTION_SHOW, 0)) { if (!IsVisible()) { PlaceBehind(eZPlacementTop, 0, false); } WinShowWindow(mWnd, true); } } else { WinShowWindow(mWnd, false); } } return NS_OK; } //----------------------------------------------------------------------------- bool nsWindow::IsVisible() const { return WinIsWindowVisible(GetMainWindow()); } //----------------------------------------------------------------------------- NS_METHOD nsWindow::SetFocus(bool aRaise) { // for toplevel windows, this is directed to the client (i.e. mWnd) if (mWnd) { if (!mInSetFocus) { DEBUGFOCUS(SetFocus); mInSetFocus = true; WinSetFocus(HWND_DESKTOP, mWnd); mInSetFocus = false; } } return NS_OK; } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Invalidate(const nsIntRect& aRect) { if (mWnd) { RECTL rcl = {aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height}; NS2PM(rcl); WinInvalidateRect(mWnd, &rcl, false); } return NS_OK; } //----------------------------------------------------------------------------- // Create a Thebes surface using the current window handle. gfxASurface* nsWindow::GetThebesSurface() { if (mWnd && !mThebesSurface) { mThebesSurface = new gfxOS2Surface(mWnd); } return mThebesSurface; } //----------------------------------------------------------------------------- // Internal-only method that suppresses creation of a Thebes surface // for windows that aren't supposed to be visible. If one was created // by an external call to GetThebesSurface(), it will be returned. gfxASurface* nsWindow::ConfirmThebesSurface() { if (!mThebesSurface && !mNoPaint && mWnd) { mThebesSurface = new gfxOS2Surface(mWnd); } return mThebesSurface; } //----------------------------------------------------------------------------- float nsWindow::GetDPI() { static int32_t sDPI = 0; // Create DC compatible with the screen, then query the DPI setting. // If this fails, fall back to something sensible. if (!sDPI) { HDC dc = DevOpenDC(0, OD_MEMORY,"*",0L, 0, 0); if (dc > 0) { LONG lDPI; if (DevQueryCaps(dc, CAPS_VERTICAL_FONT_RES, 1, &lDPI)) sDPI = lDPI; DevCloseDC(dc); } if (sDPI <= 0) { sDPI = 96; } } return sDPI; } //----------------------------------------------------------------------------- // Return some native data according to aDataType. void* nsWindow::GetNativeData(uint32_t aDataType) { switch(aDataType) { case NS_NATIVE_WIDGET: case NS_NATIVE_WINDOW: case NS_NATIVE_PLUGIN_PORT: return (void*)mWnd; // during a native drag over the current window or any drag // originating in Moz, return a drag HPS to avoid screen corruption; case NS_NATIVE_GRAPHIC: { HPS hps = 0; CheckDragStatus(ACTION_DRAW, &hps); if (!hps) { hps = WinGetPS(mWnd); } return (void*)hps; } } return 0; } //----------------------------------------------------------------------------- void nsWindow::FreeNativeData(void* data, uint32_t aDataType) { // an HPS is the only native data that needs to be freed if (aDataType == NS_NATIVE_GRAPHIC && data && !ReleaseIfDragHPS((HPS)data)) { WinReleasePS((HPS)data); } } //----------------------------------------------------------------------------- NS_METHOD nsWindow::CaptureMouse(bool aCapture) { if (aCapture) { WinSetCapture(HWND_DESKTOP, mWnd); } else { WinSetCapture(HWND_DESKTOP, 0); } return NS_OK; } //----------------------------------------------------------------------------- bool nsWindow::HasPendingInputEvent() { return (WinQueryQueueStatus(HWND_DESKTOP) & (QS_KEY | QS_MOUSE)) != 0; } //============================================================================= // Window Positioning //============================================================================= // For toplevel windows, mBounds contains the dimensions of the client // window. os2FrameWindow's "override" returns the size of the frame. NS_METHOD nsWindow::GetBounds(nsIntRect& aRect) { if (mFrame) { return mFrame->GetBounds(aRect); } aRect = mBounds; return NS_OK; } //----------------------------------------------------------------------------- // Since mBounds contains the dimensions of the client, os2FrameWindow // doesn't have to provide any special handling for this method. NS_METHOD nsWindow::GetClientBounds(nsIntRect& aRect) { aRect = mBounds; return NS_OK; } //----------------------------------------------------------------------------- nsIntPoint nsWindow::WidgetToScreenOffset() { POINTL point = { 0, 0 }; NS2PM(point); WinMapWindowPoints(mWnd, HWND_DESKTOP, &point, 1); return nsIntPoint(point.x, WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - point.y - 1); } //----------------------------------------------------------------------------- // Transform Y values between PM & XP coordinate systems. // ptl is in this window's space void nsWindow::NS2PM(POINTL& ptl) { ptl.y = mBounds.height - ptl.y - 1; } // rcl is in this window's space void nsWindow::NS2PM(RECTL& rcl) { LONG height = rcl.yTop - rcl.yBottom; rcl.yTop = mBounds.height - rcl.yBottom; rcl.yBottom = rcl.yTop - height; } // ptl is in parent's space void nsWindow::NS2PM_PARENT(POINTL& ptl) { if (mParent) { mParent->NS2PM(ptl); } else { HWND hParent = WinQueryWindow(mWnd, QW_PARENT); SWP swp; WinQueryWindowPos(hParent, &swp); ptl.y = swp.cy - ptl.y - 1; } } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Move(double aX, double aY) { if (mFrame) { nsresult rv = mFrame->Move(NSToIntRound(aX), NSToIntRound(aY)); NotifyRollupGeometryChange(); return rv; } Resize(aX, aY, mBounds.width, mBounds.height, false); return NS_OK; } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { if (mFrame) { nsresult rv = mFrame->Resize(NSToIntRound(aWidth), NSToIntRound(aHeight), aRepaint); NotifyRollupGeometryChange(); return rv; } Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint); return NS_OK; } //----------------------------------------------------------------------------- NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) { int32_t x = NSToIntRound(aX); int32_t y = NSToIntRound(aY); int32_t width = NSToIntRound(aWidth); int32_t height = NSToIntRound(aHeight); if (mFrame) { nsresult rv = mFrame->Resize(x, y, width, height, aRepaint); NotifyRollupGeometryChange(); return rv; } // For mWnd & eWindowType_child set the cached values upfront, see bug 286555. // For other mWnd types we defer transfer of values to mBounds to // WinSetWindowPos(), see bug 391421. if (!mWnd || mWindowType == eWindowType_child || mWindowType == eWindowType_plugin) { mBounds.x = x; mBounds.y = y; mBounds.width = width; mBounds.height = height; } // To keep top-left corner in the same place, use the new height // to calculate the coordinates for the top & bottom left corners. if (mWnd) { POINTL ptl = { x, y }; NS2PM_PARENT(ptl); ptl.y -= height - 1; // For popups, aX already gives the correct position. if (mWindowType == eWindowType_popup) { ptl.y = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - height - 1 - y; } else if (mParent) { WinMapWindowPoints(mParent->mWnd, WinQueryWindow(mWnd, QW_PARENT), &ptl, 1); } if (!WinSetWindowPos(mWnd, 0, ptl.x, ptl.y, width, height, SWP_MOVE | SWP_SIZE) && aRepaint) { WinInvalidateRect(mWnd, 0, FALSE); } } NotifyRollupGeometryChange(); return NS_OK; } //----------------------------------------------------------------------------- NS_METHOD nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget* aWidget, bool aActivate) { HWND hBehind = HWND_TOP; if (aPlacement == eZPlacementBottom) { hBehind = HWND_BOTTOM; } else if (aPlacement == eZPlacementBelow && aWidget) { hBehind = (static_cast(aWidget))->GetMainWindow(); } uint32_t flags = SWP_ZORDER; if (aActivate) { flags |= SWP_ACTIVATE; } WinSetWindowPos(GetMainWindow(), hBehind, 0, 0, 0, 0, flags); return NS_OK; } //----------------------------------------------------------------------------- // Set widget's position within its parent child list. NS_METHOD nsWindow::SetZIndex(int32_t aZIndex) { // nsBaseWidget::SetZIndex() never has done anything sensible but // has randomly placed widgets behind others (see bug 117730#c25). // To get bug #353011 solved simply override it here to do nothing. return NS_OK; } //============================================================================= // Plugin Operations //============================================================================= // Fire an NS_PLUGIN_ACTIVATE event whenever a window associated // with a plugin widget get the focus. void nsWindow::ActivatePlugin(HWND aWnd) { // avoid acting on recursive WM_FOCUSCHANGED msgs static bool inPluginActivate = FALSE; if (inPluginActivate) { return; } // This property is used by the plugin window to store a pointer // to its plugin object. We just use it as a convenient marker. if (!WinQueryProperty(mWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION)) { return; } // Fire a plugin activation event on the plugin widget. inPluginActivate = TRUE; DEBUGFOCUS(NS_PLUGIN_ACTIVATE); DispatchActivationEvent(NS_PLUGIN_ACTIVATE); // Activating the plugin moves the focus off the child that had it, // so try to restore it. If the WM_FOCUSCHANGED msg was synthesized // by the plugin, then mp1 contains the child window that lost focus. // Otherwise, just move it to the plugin's first child unless this // is the mplayer plugin - doing so will put us into an endless loop. // Since its children belong to another process, use the PID as a test. HWND hFocus = 0; if (WinIsChild(aWnd, mWnd)) { hFocus = aWnd; } else { hFocus = WinQueryWindow(mWnd, QW_TOP); if (hFocus) { PID pidFocus, pidThis; TID tid; WinQueryWindowProcess(hFocus, &pidFocus, &tid); WinQueryWindowProcess(mWnd, &pidThis, &tid); if (pidFocus != pidThis) { hFocus = 0; } } } if (hFocus) { WinSetFocus(HWND_DESKTOP, hFocus); } inPluginActivate = FALSE; return; } //----------------------------------------------------------------------------- // This is invoked on a window that has plugin widget children // to resize and clip those child windows. nsresult nsWindow::ConfigureChildren(const nsTArray& aConfigurations) { for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; nsWindow* w = static_cast(configuration.mChild); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); w->SetPluginClipRegion(configuration); } return NS_OK; } //----------------------------------------------------------------------------- // This is invoked on a plugin window to resize it and set a persistent // clipping region for it. Since the latter isn't possible on OS/2, it // inserts a dummy window between the plugin widget and its parent to // act as a clipping rectangle. The dummy window's dimensions and the // plugin widget's position within the window are adjusted to correspond // to the bounding box of the supplied array of clipping rectangles. // Note: this uses PM calls rather than existing methods like Resize() // and Update() because none of them support the options needed here. void nsWindow::SetPluginClipRegion(const Configuration& aConfiguration) { NS_ASSERTION((mParent && mParent->mWnd), "Child window has no parent"); // If nothing has changed, exit. if (!StoreWindowClipRegion(aConfiguration.mClipRegion) && mBounds.IsEqualInterior(aConfiguration.mBounds)) { return; } // Set the widget's x/y to its nominal unclipped value. It doesn't // affect our calculations but other code relies on it being correct. mBounds.MoveTo(aConfiguration.mBounds.TopLeft()); // Get or create the PM window we use as a clipping rectangle. HWND hClip = GetPluginClipWindow(mParent->mWnd); NS_ASSERTION(hClip, "No clipping window for plugin"); if (!hClip) { return; } // Create the bounding box for the clip region. const nsTArray& rects = aConfiguration.mClipRegion; nsIntRect r; for (uint32_t i = 0; i < rects.Length(); ++i) { r.UnionRect(r, rects[i]); } // Size and position hClip to match the bounding box. SWP swp; POINTL ptl; WinQueryWindowPos(hClip, &swp); ptl.x = aConfiguration.mBounds.x + r.x; ptl.y = mParent->mBounds.height - (aConfiguration.mBounds.y + r.y + r.height); ULONG clipFlags = 0; if (swp.x != ptl.x || swp.y != ptl.y) { clipFlags |= SWP_MOVE; } if (swp.cx != r.width || swp.cy != r.height) { clipFlags |= SWP_SIZE; } if (clipFlags) { WinSetWindowPos(hClip, 0, ptl.x, ptl.y, r.width, r.height, clipFlags); } // Reducing the size of hClip clips the right & top sides of the // plugin widget. To clip the left & bottom sides, we have to move // the widget so its origin's x and/or y is negative wrt hClip. WinQueryWindowPos(mWnd, &swp); ptl.x = -r.x; ptl.y = r.height + r.y - aConfiguration.mBounds.height; ULONG wndFlags = 0; if (swp.x != ptl.x || swp.y != ptl.y) { wndFlags |= SWP_MOVE; } if (mBounds.Size() != aConfiguration.mBounds.Size()) { wndFlags |= SWP_SIZE; } if (wndFlags) { WinSetWindowPos(mWnd, 0, ptl.x, ptl.y, aConfiguration.mBounds.width, aConfiguration.mBounds.height, wndFlags); } // Some plugins don't resize themselves when the plugin widget changes // size, so help them out by resizing the first child (usually a frame). if (wndFlags & SWP_SIZE) { HWND hChild = WinQueryWindow(mWnd, QW_TOP); if (hChild) { WinSetWindowPos(hChild, 0, 0, 0, aConfiguration.mBounds.width, aConfiguration.mBounds.height, SWP_MOVE | SWP_SIZE); } } // When hClip is resized, mWnd and its children may not get updated // automatically, so invalidate & repaint them if (clipFlags & SWP_SIZE) { WinInvalidateRect(mWnd, 0, TRUE); WinUpdateWindow(mWnd); } } //----------------------------------------------------------------------------- // This gets or creates a window that's inserted between the main window // and its plugin children. This window does nothing except act as a // clipping rectangle for the plugin widget. HWND nsWindow::GetPluginClipWindow(HWND aParentWnd) { if (mClipWnd) { return mClipWnd; } // Insert a new clip window in the hierarchy between mWnd & aParentWnd. mClipWnd = WinCreateWindow(aParentWnd, kClipWndClass, "", WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, 0, mWnd, 0, 0, 0); if (mClipWnd) { if (!WinSetParent(mWnd, mClipWnd, FALSE)) { WinDestroyWindow(mClipWnd); mClipWnd = 0; } } return mClipWnd; } //============================================================================= // Top-level (frame window) Operations //============================================================================= // When a window gets the focus, call os2FrameWindow's version of this // method. It will fire an NS_ACTIVATE event on the top-level widget // if appropriate. void nsWindow::ActivateTopLevelWidget() { if (mFrame) { mFrame->ActivateTopLevelWidget(); } else { nsWindow* top = static_cast(GetTopLevelWidget()); if (top && top->mFrame) { top->mFrame->ActivateTopLevelWidget(); } } return; } //----------------------------------------------------------------------------- // All of these methods are inherently toplevel-only, and are in fact // only invoked on toplevel widgets. If they're invoked on a child // window, there's an error upstream. NS_IMETHODIMP nsWindow::SetSizeMode(int32_t aMode) { NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED); return mFrame->SetSizeMode(aMode); } NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide) { NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED); return mFrame->HideWindowChrome(aShouldHide); } NS_METHOD nsWindow::SetTitle(const nsAString& aTitle) { NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED); return mFrame->SetTitle(aTitle); } NS_METHOD nsWindow::SetIcon(const nsAString& aIconSpec) { NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED); return mFrame->SetIcon(aIconSpec); } NS_METHOD nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) { NS_ENSURE_TRUE(mFrame, NS_ERROR_UNEXPECTED); return mFrame->ConstrainPosition(aAllowSlop, aX, aY); } //============================================================================= // Mouse Pointers //============================================================================= // Set one of the standard mouse pointers. NS_METHOD nsWindow::SetCursor(nsCursor aCursor) { HPOINTER newPointer = 0; switch (aCursor) { case eCursor_select: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_TEXT, FALSE); break; case eCursor_wait: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE); break; case eCursor_hyperlink: newPointer = sPtrArray[IDC_SELECTANCHOR-IDC_BASE]; break; case eCursor_standard: case eCursor_context_menu: // XXX See bug 258960. newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW, FALSE); break; case eCursor_n_resize: case eCursor_s_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENS, FALSE); break; case eCursor_w_resize: case eCursor_e_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZEWE, FALSE); break; case eCursor_nw_resize: case eCursor_se_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENWSE, FALSE); break; case eCursor_ne_resize: case eCursor_sw_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENESW, FALSE); break; case eCursor_crosshair: newPointer = sPtrArray[IDC_CROSS-IDC_BASE]; break; case eCursor_move: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_MOVE, FALSE); break; case eCursor_help: newPointer = sPtrArray[IDC_HELP-IDC_BASE]; break; case eCursor_copy: // CSS3 newPointer = sPtrArray[IDC_COPY-IDC_BASE]; break; case eCursor_alias: newPointer = sPtrArray[IDC_ALIAS-IDC_BASE]; break; case eCursor_cell: newPointer = sPtrArray[IDC_CELL-IDC_BASE]; break; case eCursor_grab: newPointer = sPtrArray[IDC_GRAB-IDC_BASE]; break; case eCursor_grabbing: newPointer = sPtrArray[IDC_GRABBING-IDC_BASE]; break; case eCursor_spinning: newPointer = sPtrArray[IDC_ARROWWAIT-IDC_BASE]; break; case eCursor_zoom_in: newPointer = sPtrArray[IDC_ZOOMIN-IDC_BASE]; break; case eCursor_zoom_out: newPointer = sPtrArray[IDC_ZOOMOUT-IDC_BASE]; break; case eCursor_not_allowed: case eCursor_no_drop: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_ILLEGAL, FALSE); break; case eCursor_col_resize: newPointer = sPtrArray[IDC_COLRESIZE-IDC_BASE]; break; case eCursor_row_resize: newPointer = sPtrArray[IDC_ROWRESIZE-IDC_BASE]; break; case eCursor_vertical_text: newPointer = sPtrArray[IDC_VERTICALTEXT-IDC_BASE]; break; case eCursor_all_scroll: // XXX not 100% appropriate perhaps newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_MOVE, FALSE); break; case eCursor_nesw_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENESW, FALSE); break; case eCursor_nwse_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENWSE, FALSE); break; case eCursor_ns_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZENS, FALSE); break; case eCursor_ew_resize: newPointer = WinQuerySysPointer(HWND_DESKTOP, SPTR_SIZEWE, FALSE); break; case eCursor_none: newPointer = sPtrArray[IDC_NONE-IDC_BASE]; break; default: NS_ERROR("Invalid cursor type"); break; } if (newPointer) { WinSetPointer(HWND_DESKTOP, newPointer); } return NS_OK; } //----------------------------------------------------------------------------- // Create a mouse pointer on the fly to support the CSS 'cursor' style. // This code is based on the Win version by C. Biesinger but has been // substantially modified to accommodate platform differences and to // improve efficiency. NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) { // if this is the same image as last time, reuse the saved hptr; // it will be destroyed when we create a new one or when the // current window is destroyed if (mCssCursorImg == aCursor && mCssCursorHPtr) { WinSetPointer(HWND_DESKTOP, mCssCursorHPtr); return NS_OK; } nsRefPtr surface; aCursor->GetFrame(imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE, getter_AddRefs(surface)); NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE); nsRefPtr frame(surface->GetAsReadableARGB32ImageSurface()); NS_ENSURE_TRUE(frame, NS_ERROR_NOT_AVAILABLE); // if the image is ridiculously large, exit because // it will be unrecognizable when shrunk to 32x32 int32_t width = frame->Width(); int32_t height = frame->Height(); NS_ENSURE_TRUE(width <= 128 && height <= 128, NS_ERROR_FAILURE); uint8_t* data = frame->Data(); // create the color bitmap HBITMAP hBmp = CreateBitmapRGB(data, width, height); NS_ENSURE_TRUE(hBmp, NS_ERROR_FAILURE); // create a transparency mask from the alpha bytes HBITMAP hAlpha = CreateTransparencyMask(frame->Format(), data, width, height); if (!hAlpha) { GpiDeleteBitmap(hBmp); return NS_ERROR_FAILURE; } POINTERINFO info = {0}; info.fPointer = TRUE; info.xHotspot = aHotspotX; info.yHotspot = height - aHotspotY - 1; info.hbmPointer = hAlpha; info.hbmColor = hBmp; // create the pointer HPOINTER cursor = WinCreatePointerIndirect(HWND_DESKTOP, &info); GpiDeleteBitmap(hBmp); GpiDeleteBitmap(hAlpha); NS_ENSURE_TRUE(cursor, NS_ERROR_FAILURE); // use it WinSetPointer(HWND_DESKTOP, cursor); // destroy the previous hptr; this has to be done after the // new pointer is set or else WinDestroyPointer() will fail if (mCssCursorHPtr) { WinDestroyPointer(mCssCursorHPtr); } // save the hptr and a reference to the image for next time mCssCursorHPtr = cursor; mCssCursorImg = aCursor; return NS_OK; } //----------------------------------------------------------------------------- // Render image or modified alpha data as a native bitmap. // aligned bytes per row, rounded up to next dword bounday #define ALIGNEDBPR(cx,bits) ( ( ( ((cx)*(bits)) + 31) / 32) * 4) HBITMAP nsWindow::DataToBitmap(uint8_t* aImageData, uint32_t aWidth, uint32_t aHeight, uint32_t aDepth) { // get a presentation space for this window HPS hps = (HPS)GetNativeData(NS_NATIVE_GRAPHIC); if (!hps) { return 0; } // a handy structure that does double duty // as both BITMAPINFOHEADER2 & BITMAPINFO2 struct { BITMAPINFOHEADER2 head; RGB2 black; RGB2 white; } bi; memset(&bi, 0, sizeof(bi)); bi.white.bBlue = (BYTE)255; bi.white.bGreen = (BYTE)255; bi.white.bRed = (BYTE)255; // fill in the particulars bi.head.cbFix = sizeof(bi.head); bi.head.cx = aWidth; bi.head.cy = aHeight; bi.head.cPlanes = 1; bi.head.cBitCount = aDepth; bi.head.ulCompression = BCA_UNCOMP; bi.head.cbImage = ALIGNEDBPR(aWidth, aDepth) * aHeight; bi.head.cclrUsed = (aDepth == 1 ? 2 : 0); // create a bitmap from the image data HBITMAP hBmp = GpiCreateBitmap(hps, &bi.head, CBM_INIT, reinterpret_cast(aImageData), (BITMAPINFO2*)&bi); // free the hps, then return the bitmap FreeNativeData((void*)hps, NS_NATIVE_GRAPHIC); return hBmp; } //----------------------------------------------------------------------------- // Create an RGB24 bitmap from Cairo image data. HBITMAP nsWindow::CreateBitmapRGB(uint8_t* aImageData, uint32_t aWidth, uint32_t aHeight) { // calc width in bytes, rounding up to a dword boundary const uint32_t bpr = ALIGNEDBPR(aWidth, 24); uint8_t* bmp = (uint8_t*)malloc(bpr * aHeight); if (!bmp) { return 0; } uint32_t* pSrc = (uint32_t*)aImageData; for (uint32_t row = aHeight; row > 0; --row) { uint8_t* pDst = bmp + bpr * (row - 1); for (uint32_t col = aWidth; col > 0; --col) { // In Cairo a color is encoded as ARGB in a DWORD // stored in machine endianess. uint32_t color = *pSrc++; *pDst++ = color; // Blue *pDst++ = color >> 8; // Green *pDst++ = color >> 16; // Red } } // create the bitmap HBITMAP hAlpha = DataToBitmap(bmp, aWidth, aHeight, 24); // free the buffer, then return the bitmap free(bmp); return hAlpha; } //----------------------------------------------------------------------------- // Create a monochrome AND/XOR bitmap from 0, 1, or 8-bit alpha data. HBITMAP nsWindow::CreateTransparencyMask(gfxImageFormat format, uint8_t* aImageData, uint32_t aWidth, uint32_t aHeight) { // calc width in bytes, rounding up to a dword boundary uint32_t abpr = ALIGNEDBPR(aWidth, 1); uint32_t cbData = abpr * aHeight; // alloc and clear space to hold both the AND & XOR bitmaps uint8_t* mono = (uint8_t*)calloc(cbData, 2); if (!mono) { return 0; } // Non-alpha formats are already taken care of // by initializing the XOR and AND masks to zero if (format == gfxImageFormatARGB32) { // make the AND mask the inverse of the 8-bit alpha data int32_t* pSrc = (int32_t*)aImageData; for (uint32_t row = aHeight; row > 0; --row) { // Point to the right row in the AND mask uint8_t* pDst = mono + cbData + abpr * (row - 1); uint8_t mask = 0x80; for (uint32_t col = aWidth; col > 0; --col) { // Use the sign bit to test for transparency, as the alpha byte // is highest byte. Positive means, alpha < 128, so consider it // as transparent and set the AND mask. if (*pSrc++ >= 0) { *pDst |= mask; } mask >>= 1; if (!mask) { pDst++; mask = 0x80; } } } } // create the bitmap HBITMAP hAlpha = DataToBitmap(mono, aWidth, aHeight * 2, 1); // free the buffer, then return the bitmap free(mono); return hAlpha; } //============================================================================= // Rollup Event Handlers //============================================================================= NS_IMETHODIMP nsWindow::CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture) { gRollupListener = aDoCapture ? aListener : nullptr; return NS_OK; } //----------------------------------------------------------------------------- // static bool nsWindow::EventIsInsideWindow(nsWindow* aWindow) { RECTL rcl; POINTL ptl; NS_ENSURE_TRUE(aWindow, false); if (WinQueryMsgPos(0, &ptl)) { WinMapWindowPoints(HWND_DESKTOP, aWindow->mWnd, &ptl, 1); WinQueryWindowRect(aWindow->mWnd, &rcl); // now make sure that it wasn't one of our children if (ptl.x < rcl.xLeft || ptl.x > rcl.xRight || ptl.y > rcl.yTop || ptl.y < rcl.yBottom) { return false; } } return true; } //----------------------------------------------------------------------------- // Handle events that would cause a popup (combobox, menu, etc) to rollup. // static bool nsWindow::RollupOnButtonDown(ULONG aMsg) { nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); nsCOMPtr rollupWidget; if (rollupListener) { rollupWidget = rollupListener->GetRollupWidget(); } // Exit if the event is inside the most recent popup. if (EventIsInsideWindow((nsWindow*)rollupWidget)) { return false; } // See if we're dealing with a menu. If so, exit if the // event was inside a parent of the current submenu. uint32_t popupsToRollup = UINT32_MAX; if (rollupListener) { nsAutoTArray widgetChain; uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); for (uint32_t i = 0; i < widgetChain.Length(); ++i) { nsIWidget* widget = widgetChain[i]; if (EventIsInsideWindow((nsWindow*)widget)) { if (i < sameTypeCount) { return false; } popupsToRollup = sameTypeCount; break; } } // for each parent menu widget } // if rollup listener knows about menus // We only need to deal with the last rollup for left mouse down events. NS_ASSERTION(!mLastRollup, "mLastRollup is null"); bool consumeRollupEvent = rollupListener->Rollup(popupsToRollup, aMsg == WM_LBUTTONDOWN ? &mLastRollup : nullptr); NS_IF_ADDREF(mLastRollup); // If true, the buttondown event won't be passed on to the wndproc. return consumeRollupEvent; } //----------------------------------------------------------------------------- // static void nsWindow::RollupOnFocusLost(HWND aFocus) { nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); nsCOMPtr rollupWidget; if (rollupListener) { rollupWidget = rollupListener->GetRollupWidget(); } HWND hRollup = rollupWidget ? ((nsWindow*)rollupWidget)->mWnd : nullptr; // Exit if focus was lost to the most recent popup. if (hRollup == aFocus) { return; } // Exit if focus was lost to a parent of the current submenu. if (rollupListener) { nsAutoTArray widgetChain; rollupListener->GetSubmenuWidgetChain(&widgetChain); for (uint32_t i = 0; i < widgetChain.Length(); ++i) { if (((nsWindow*)widgetChain[i])->mWnd == aFocus) { return; } } // Rollup all popups. rollupListener->Rollup(UINT32_MAX); } } //============================================================================= // nsWindow's Window Procedure //============================================================================= // This is the actual wndproc; it does some preprocessing then passes // the msgs to the ProcessMessage() method which does most of the work. MRESULT EXPENTRY fnwpNSWindow(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { nsAutoRollup autoRollup; // If this window doesn't have an object ptr, // send the msg to the default wndproc. nsWindow* wnd = nsWindow::GetNSWindowPtr(hwnd); if (!wnd) { return WinDefWindowProc(hwnd, msg, mp1, mp2); } // If we're not in the destructor, hold on to the object for the // life of this method, in case it gets deleted during processing. // Yes, it's a double hack since someWindow is not really an interface. nsCOMPtr kungFuDeathGrip; if (!wnd->mIsDestroying) { kungFuDeathGrip = do_QueryInterface((nsBaseWidget*)wnd); } // Pre-process msgs that may cause a rollup. } switch (msg) { case WM_BUTTON1DOWN: case WM_BUTTON2DOWN: case WM_BUTTON3DOWN: if (nsWindow::RollupOnButtonDown(msg)) { return (MRESULT)true; } break; case WM_SETFOCUS: if (!mp2) { nsWindow::RollupOnFocusLost((HWND)mp1); } break; } return wnd->ProcessMessage(msg, mp1, mp2); } //----------------------------------------------------------------------------- // In effect, nsWindow's real wndproc. MRESULT nsWindow::ProcessMessage(ULONG msg, MPARAM mp1, MPARAM mp2) { bool isDone = false; MRESULT mresult = 0; switch (msg) { // Interpret WM_QUIT as a close request so that // windows can be closed from the Window List case WM_CLOSE: case WM_QUIT: { mWindowState |= nsWindowState_eClosing; WidgetGUIEvent event(true, NS_XUL_CLOSE, this); InitEvent(event); DispatchWindowEvent(&event); // abort window closure isDone = true; break; } case WM_DESTROY: OnDestroy(); isDone = true; break; case WM_PAINT: isDone = OnPaint(); break; case WM_TRANSLATEACCEL: isDone = OnTranslateAccelerator((PQMSG)mp1); break; case WM_CHAR: isDone = DispatchKeyEvent(mp1, mp2); break; // Mouseclicks: we don't dispatch CLICK events because they just cause // trouble: gecko seems to expect EITHER buttondown/up OR click events // and so that's what we give it. case WM_BUTTON1DOWN: WinSetCapture(HWND_DESKTOP, mWnd); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2); // If this msg is forwarded to a popup's owner, Moz will cause the // popup to be rolled-up in error when the owner processes the msg. if (mWindowType == eWindowType_popup) { isDone = true; } // there's no need to clear this on button-up sLastButton1Down.x = XFROMMP(mp1); sLastButton1Down.y = YFROMMP(mp1); break; case WM_BUTTON1UP: WinSetCapture(HWND_DESKTOP, 0); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2); break; case WM_BUTTON1DBLCLK: isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2); break; case WM_BUTTON2DOWN: WinSetCapture(HWND_DESKTOP, mWnd); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2, false, WidgetMouseEvent::eRightButton); break; case WM_BUTTON2UP: WinSetCapture(HWND_DESKTOP, 0); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2, false, WidgetMouseEvent::eRightButton); break; case WM_BUTTON2DBLCLK: isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2, false, WidgetMouseEvent::eRightButton); break; case WM_BUTTON3DOWN: WinSetCapture(HWND_DESKTOP, mWnd); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, mp1, mp2, false, WidgetMouseEvent::eMiddleButton); break; case WM_BUTTON3UP: WinSetCapture(HWND_DESKTOP, 0); isDone = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, mp1, mp2, false, WidgetMouseEvent::eMiddleButton); break; case WM_BUTTON3DBLCLK: isDone = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, mp1, mp2, false, WidgetMouseEvent::eMiddleButton); break; case WM_CONTEXTMENU: if (SHORT2FROMMP(mp2)) { HWND hFocus = WinQueryFocus(HWND_DESKTOP); if (hFocus != mWnd) { WinSendMsg(hFocus, msg, mp1, mp2); } else { isDone = DispatchMouseEvent(NS_CONTEXTMENU, mp1, mp2, true, WidgetMouseEvent::eLeftButton); } } else { isDone = DispatchMouseEvent(NS_CONTEXTMENU, mp1, mp2, false, WidgetMouseEvent::eRightButton); } break; // If MB1 & MB2 are both pressed, perform a copy or paste. case WM_CHORD: isDone = OnMouseChord(mp1, mp2); break; case WM_MOUSEMOVE: { static POINTL ptlLastPos = { -1, -1 }; // If mouse has actually moved, remember the new position, // then dispatch the event. if (ptlLastPos.x != (SHORT)SHORT1FROMMP(mp1) || ptlLastPos.y != (SHORT)SHORT2FROMMP(mp1)) { ptlLastPos.x = (SHORT)SHORT1FROMMP(mp1); ptlLastPos.y = (SHORT)SHORT2FROMMP(mp1); DispatchMouseEvent(NS_MOUSE_MOVE, mp1, mp2); } // don't propagate mouse move or the OS will change the pointer isDone = true; break; } case WM_MOUSEENTER: isDone = DispatchMouseEvent(NS_MOUSE_ENTER, mp1, mp2); break; case WM_MOUSELEAVE: isDone = DispatchMouseEvent(NS_MOUSE_EXIT, mp1, mp2); break; case WM_APPCOMMAND: { uint32_t appCommand = SHORT2FROMMP(mp2) & 0xfff; switch (appCommand) { case APPCOMMAND_BROWSER_BACKWARD: case APPCOMMAND_BROWSER_FORWARD: case APPCOMMAND_BROWSER_REFRESH: case APPCOMMAND_BROWSER_STOP: DispatchCommandEvent(appCommand); // tell the driver that we handled the event mresult = (MRESULT)1; isDone = true; break; } break; } case WM_HSCROLL: case WM_VSCROLL: isDone = DispatchScrollEvent(msg, mp1, mp2); break; // Do not act on WM_ACTIVATE - it is handled by os2FrameWindow. // case WM_ACTIVATE: // break; // This msg is used to activate top-level and plugin widgets // after PM is done changing the focus. We're only interested // in windows gaining focus, not in those losing it. case WM_FOCUSCHANGED: DEBUGFOCUS(WM_FOCUSCHANGED); if (SHORT1FROMMP(mp2)) { ActivateTopLevelWidget(); ActivatePlugin(HWNDFROMMP(mp1)); } break; case WM_WINDOWPOSCHANGED: isDone = OnReposition((PSWP) mp1); break; // all msgs that occur when this window is the target of a drag case DM_DRAGOVER: case DM_DRAGLEAVE: case DM_DROP: case DM_RENDERCOMPLETE: case DM_DROPHELP: OnDragDropMsg(msg, mp1, mp2, mresult); isDone = true; break; case WM_QUERYCONVERTPOS: isDone = OnQueryConvertPos(mp1, mresult); break; case WM_IMEREQUEST: isDone = OnImeRequest(mp1, mp2); break; } // If an event handler signalled that we should consume the event, // return. Otherwise, pass it on to the default wndproc. if (!isDone) { mresult = WinDefWindowProc(mWnd, msg, mp1, mp2); } return mresult; } //============================================================================= // Window Message Handlers //============================================================================= // WM_DESTROY has been called. void nsWindow::OnDestroy() { mOnDestroyCalled = true; SetNSWindowPtr(mWnd, 0); mWnd = 0; // release references to context and children nsBaseWidget::OnDestroy(); // dispatching of the event may cause the reference count to drop to 0 // and result in this object being deleted. To avoid that, add a // reference and then release it after dispatching the event. // // It's important *not* to do this if we're being called from the // destructor -- this would result in our destructor being called *again* // from the Release() below. This is very bad... if (!(nsWindowState_eDoingDelete & mWindowState)) { AddRef(); NotifyWindowDestroyed(); Release(); } // dead widget mWindowState |= nsWindowState_eDead; mWindowState &= ~(nsWindowState_eLive|nsWindowState_ePrecreate| nsWindowState_eInCreate); } //----------------------------------------------------------------------------- bool nsWindow::OnReposition(PSWP pSwp) { bool result = false; if (pSwp->fl & SWP_MOVE && !(pSwp->fl & SWP_MINIMIZE)) { HWND hParent = mParent ? mParent->mWnd : WinQueryWindow(mWnd, QW_PARENT); // need screen coords. POINTL ptl = { pSwp->x, pSwp->y + pSwp->cy - 1 }; // XXX - this is peculiar... WinMapWindowPoints(WinQueryWindow(mWnd, QW_PARENT), hParent, &ptl, 1); PM2NS_PARENT(ptl); mBounds.x = ptl.x; mBounds.y = ptl.y; WinMapWindowPoints(hParent, HWND_DESKTOP, &ptl, 1); result = DispatchMoveEvent(ptl.x, ptl.y); } if (pSwp->fl & SWP_SIZE && !(pSwp->fl & SWP_MINIMIZE)) { mBounds.width = pSwp->cx; mBounds.height = pSwp->cy; // If the window is supposed to have a thebes surface, resize it. if (ConfirmThebesSurface()) { mThebesSurface->Resize(gfxIntSize(mBounds.width, mBounds.height)); } result = DispatchResizeEvent(mBounds.width, mBounds.height); } return result; } //----------------------------------------------------------------------------- bool nsWindow::OnPaint() { HPS hPS; HPS hpsDrag; HRGN hrgn; nsEventStatus eventStatus = nsEventStatus_eIgnore; #ifdef DEBUG_PAINT HRGN debugPaintFlashRegion = 0; HPS debugPaintFlashPS = 0; if (debug_WantPaintFlashing()) { debugPaintFlashPS = WinGetPS(mWnd); debugPaintFlashRegion = GpiCreateRegion(debugPaintFlashPS, 0, 0); WinQueryUpdateRegion(mWnd, debugPaintFlashRegion); } #endif // Use a dummy do..while(0) loop to facilitate error handling & early-outs. do { // Get the current drag status. If we're in a Moz-originated drag, // it will return a special drag HPS to pass to WinBeginPaint(). // Oherwise, get a cached micro PS. CheckDragStatus(ACTION_PAINT, &hpsDrag); hPS = hpsDrag ? hpsDrag : WinGetPS(mWnd); // If we can't get an HPS, validate the window so we don't // keep getting the same WM_PAINT msg over & over again. RECTL rcl = { 0 }; if (!hPS) { WinQueryWindowRect(mWnd, &rcl); WinValidateRect(mWnd, &rcl, FALSE); break; } // Get the update region before WinBeginPaint() resets it. hrgn = GpiCreateRegion(hPS, 0, 0); WinQueryUpdateRegion(mWnd, hrgn); WinBeginPaint(mWnd, hPS, &rcl); // Exit if the update rect is empty. if (WinIsRectEmpty(0, &rcl)) { break; } // Exit if a thebes surface can not/should not be created, // but first fill the area with the default background color // to erase any visual artifacts. if (!ConfirmThebesSurface()) { WinDrawBorder(hPS, &rcl, 0, 0, 0, 0, DB_INTERIOR | DB_AREAATTRS); break; } // Even if there is no callback to update the content (unlikely) // we still want to update the screen with whatever's available. if (!mEventCallback) { mThebesSurface->Refresh(&rcl, hPS); break; } // Create an event & a Thebes context. nsPaintEvent event(true, NS_PAINT, this); InitEvent(event); nsRefPtr thebesContext = new gfxContext(mThebesSurface); // Intersect the update region with the paint rectangle to clip areas // that aren't visible (e.g. offscreen or covered by another window). HRGN hrgnPaint; hrgnPaint = GpiCreateRegion(hPS, 1, &rcl); if (hrgnPaint) { GpiCombineRegion(hPS, hrgn, hrgn, hrgnPaint, CRGN_AND); GpiDestroyRegion(hPS, hrgnPaint); } // See how many rects comprise the update region. If there are 8 // or fewer, update them individually. If there are more or the call // failed, update the bounding rectangle returned by WinBeginPaint(). #define MAX_CLIPRECTS 8 RGNRECT rgnrect = { 1, MAX_CLIPRECTS, 0, RECTDIR_LFRT_TOPBOT }; RECTL arect[MAX_CLIPRECTS]; RECTL* pr = arect; if (!GpiQueryRegionRects(hPS, hrgn, 0, &rgnrect, 0) || rgnrect.crcReturned > MAX_CLIPRECTS) { rgnrect.crcReturned = 1; arect[0] = rcl; } else { GpiQueryRegionRects(hPS, hrgn, 0, &rgnrect, arect); } // Create clipping regions for the event & the Thebes context. thebesContext->NewPath(); for (uint32_t i = 0; i < rgnrect.crcReturned; i++, pr++) { event.region.Or(event.region, nsIntRect(pr->xLeft, mBounds.height - pr->yTop, pr->xRight - pr->xLeft, pr->yTop - pr->yBottom)); thebesContext->Rectangle(gfxRect(pr->xLeft, mBounds.height - pr->yTop, pr->xRight - pr->xLeft, pr->yTop - pr->yBottom)); } thebesContext->Clip(); #ifdef DEBUG_PAINT debug_DumpPaintEvent(stdout, this, &event, nsAutoCString("noname"), (int32_t)mWnd); #endif // Init the Layers manager then dispatch the event. // If it returns false there's nothing to paint, so exit. AutoLayerManagerSetup setupLayerManager(this, thebesContext, BasicLayerManager::BUFFER_NONE); if (!DispatchWindowEvent(&event, eventStatus)) { break; } // Paint the surface, then use Refresh() to blit each rect to the screen. thebesContext->PopGroupToSource(); thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE); thebesContext->Paint(); pr = arect; for (uint32_t i = 0; i < rgnrect.crcReturned; i++, pr++) { mThebesSurface->Refresh(pr, hPS); } } while (0); // Cleanup. if (hPS) { WinEndPaint(hPS); if (hrgn) { GpiDestroyRegion(hPS, hrgn); } if (!hpsDrag || !ReleaseIfDragHPS(hpsDrag)) { WinReleasePS(hPS); } } #ifdef DEBUG_PAINT if (debug_WantPaintFlashing()) { // Only flash paint events which have not ignored the paint message. // Those that ignore the paint message aren't painting anything so there // is only the overhead of the dispatching the paint event. if (eventStatus != nsEventStatus_eIgnore) { LONG CurMix = GpiQueryMix(debugPaintFlashPS); GpiSetMix(debugPaintFlashPS, FM_INVERT); GpiPaintRegion(debugPaintFlashPS, debugPaintFlashRegion); PR_Sleep(PR_MillisecondsToInterval(30)); GpiPaintRegion(debugPaintFlashPS, debugPaintFlashRegion); PR_Sleep(PR_MillisecondsToInterval(30)); GpiSetMix(debugPaintFlashPS, CurMix); } GpiDestroyRegion(debugPaintFlashPS, debugPaintFlashRegion); WinReleasePS(debugPaintFlashPS); } #endif return true; } //----------------------------------------------------------------------------- // If MB1 & MB2 are both pressed, perform a copy or paste. bool nsWindow::OnMouseChord(MPARAM mp1, MPARAM mp2) { if (!isKeyDown(VK_BUTTON1) || !isKeyDown(VK_BUTTON2)) { return false; } // See how far the mouse has moved since MB1-down to determine // the operation (this really ought to look for selected content). bool isCopy = false; if (abs(XFROMMP(mp1) - sLastButton1Down.x) > (WinQuerySysValue(HWND_DESKTOP, SV_CXMOTIONSTART) / 2) || abs(YFROMMP(mp1) - sLastButton1Down.y) > (WinQuerySysValue(HWND_DESKTOP, SV_CYMOTIONSTART) / 2)) { isCopy = true; } // XXX Using keypress event here is wrong approach, this should be replaced // with content command event. WidgetKeyboardEvent event(true, NS_KEY_PRESS, this); nsIntPoint point(0,0); InitEvent(event, &point); event.keyCode = NS_VK_INSERT; if (isCopy) { event.modifiers = MODIFIER_CONTROL; } else { event.modifiers = MODIFIER_SHIFT; } event.eventStructType = NS_KEY_EVENT; event.charCode = 0; // OS/2 does not set the Shift, Ctrl, or Alt on keyup if (SHORT1FROMMP(mp1) & (KC_VIRTUALKEY | KC_KEYUP | KC_LONEKEY)) { USHORT usVKey = SHORT2FROMMP(mp2); if (usVKey == VK_SHIFT) { event.modifiers |= MODIFIER_SHIFT; } if (usVKey == VK_CTRL) { event.modifiers |= MODIFIER_CONTROL; } if (usVKey == VK_ALTGRAF || usVKey == VK_ALT) { event.modifiers |= MODIFIER_ALT; } } return DispatchWindowEvent(&event); } //============================================================================= // Drag & Drop - Target methods //============================================================================= // // nsWindow knows almost nothing about d&d except that it can cause // video corruption if the screen is updated during a drag. It relies // on nsIDragSessionOS2 to handle native d&d messages and to return // the status flags it uses to control screen updates. // // OnDragDropMsg() handles all of the DM_* messages messages nsWindow // should ever receive. CheckDragStatus() determines if a screen update // is safe and may return a drag HPS if doing so will avoid corruption. // As far as its author (R.Walsh) can tell, every use is required. // // For Moz drags, all while-you-drag features should be fully enabled & // corruption free; for native drags, popups & scrolling are suppressed // but some niceties, e.g. moving the cursor in text fields, are enabled. // //----------------------------------------------------------------------------- // This method was designed to be totally ignorant of drag and drop. // It gives nsIDragSessionOS2 (near) complete control over handling. bool nsWindow::OnDragDropMsg(ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT& mr) { nsresult rv; uint32_t eventType = 0; uint32_t dragFlags = 0; mr = 0; nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv); if (dragService) { nsCOMPtr dragSession( do_QueryInterface(dragService, &rv)); if (dragSession) { // handle all possible input without regard to outcome switch (msg) { case DM_DRAGOVER: dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG); rv = dragSession->DragOverMsg((PDRAGINFO)mp1, mr, &dragFlags); eventType = NS_DRAGDROP_OVER; break; case DM_DRAGLEAVE: rv = dragSession->DragLeaveMsg((PDRAGINFO)mp1, &dragFlags); eventType = NS_DRAGDROP_EXIT; break; case DM_DROP: rv = dragSession->DropMsg((PDRAGINFO)mp1, mWnd, &dragFlags); eventType = NS_DRAGDROP_DROP; break; case DM_DROPHELP: rv = dragSession->DropHelpMsg((PDRAGINFO)mp1, &dragFlags); eventType = NS_DRAGDROP_EXIT; break; case DM_RENDERCOMPLETE: rv = dragSession->RenderCompleteMsg((PDRAGTRANSFER)mp1, SHORT1FROMMP(mp2), &dragFlags); eventType = NS_DRAGDROP_DROP; break; default: rv = NS_ERROR_FAILURE; } // handle all possible outcomes without regard to their source if (NS_SUCCEEDED(rv)) { mDragStatus = sDragStatus = (dragFlags & DND_DragStatus); if (dragFlags & DND_DispatchEnterEvent) { DispatchDragDropEvent(NS_DRAGDROP_ENTER); } if (dragFlags & DND_DispatchEvent) { DispatchDragDropEvent(eventType); } if (dragFlags & DND_GetDragoverResult) { dragSession->GetDragoverResult(mr); } if (dragFlags & DND_ExitSession) { dragSession->ExitSession(&dragFlags); } } } } // save final drag status sDragStatus = mDragStatus = (dragFlags & DND_DragStatus); return true; } //----------------------------------------------------------------------------- // CheckDragStatus() concentrates all the hacks needed to avoid video // corruption during d&d into one place. The caller specifies an action // that might be a problem; the method tells it whether to proceed and // provides a Drg HPS if the situation calls for one. bool nsWindow::CheckDragStatus(uint32_t aAction, HPS* aHps) { bool rtn = true; bool getHps = false; switch (aAction) { // OnPaint() & Scroll..() - only Moz drags get a Drg hps case ACTION_PAINT: case ACTION_SCROLL: if (sDragStatus & DND_MozDrag) { getHps = true; } break; // GetNativeData() - Moz drags + native drags over this nsWindow case ACTION_DRAW: if ((sDragStatus & DND_MozDrag) || (mDragStatus & DND_NativeDrag)) { getHps = true; } break; // Show() - don't show popups during a native dragover case ACTION_SHOW: if ((sDragStatus & (DND_NativeDrag | DND_InDrop)) == DND_NativeDrag) { rtn = false; } break; // InitEvent() - use PtrPos while in drag, MsgPos otherwise case ACTION_PTRPOS: if (!sDragStatus) { rtn = false; } break; default: rtn = false; } // If the caller wants an HPS, and the current drag status // calls for one, *and* a drag hps hasn't already been requested // for this window, get the hps; otherwise, return zero; // (if we provide a 2nd hps for a window, the cursor in text // fields won't be erased when it's moved to another position) if (aHps) { if (getHps && !mDragHps) { mDragHps = DrgGetPS(mWnd); *aHps = mDragHps; } else { *aHps = 0; } } return rtn; } //----------------------------------------------------------------------------- // If there's an outstanding drag hps & it matches the one passed in, // release it. bool nsWindow::ReleaseIfDragHPS(HPS aHps) { if (mDragHps && aHps == mDragHps) { DrgReleasePS(mDragHps); mDragHps = 0; return true; } return false; } //============================================================================= // Keyboard Handlers //============================================================================= // Figure out which keyboard LEDs are on. NS_IMETHODIMP nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) { uint32_t vkey; NS_ENSURE_ARG_POINTER(aLEDState); switch (aKeyCode) { case NS_VK_CAPS_LOCK: vkey = VK_CAPSLOCK; break; case NS_VK_NUM_LOCK: vkey = VK_NUMLOCK; break; case NS_VK_SCROLL_LOCK: vkey = VK_SCRLLOCK; break; default: *aLEDState = false; return NS_OK; } *aLEDState = (WinGetKeyState(HWND_DESKTOP, vkey) & 1) != 0; return NS_OK; } //----------------------------------------------------------------------------- // Prevent PM from translating some keys & key-combos into accelerators. bool nsWindow::OnTranslateAccelerator(PQMSG pQmsg) { if (pQmsg->msg != WM_CHAR) { return false; } LONG mp1 = (LONG)pQmsg->mp1; LONG mp2 = (LONG)pQmsg->mp2; LONG sca = SHORT1FROMMP(mp1) & (KC_SHIFT | KC_CTRL | KC_ALT); if (SHORT1FROMMP(mp1) & KC_VIRTUALKEY) { // standalone F1 & F10 if (SHORT2FROMMP(mp2) == VK_F1 || SHORT2FROMMP(mp2) == VK_F10) { return (!sca ? true : false); } // Shift+Enter if (SHORT2FROMMP(mp2) == VK_ENTER) { return (sca == KC_SHIFT ? true : false); } // Alt+Enter if (SHORT2FROMMP(mp2) == VK_NEWLINE) { return (sca == KC_ALT ? true : false); } // standalone Alt & AltGraf if ((SHORT2FROMMP(mp2) == VK_ALT || SHORT2FROMMP(mp2) == VK_ALTGRAF) && (SHORT1FROMMP(mp1) & (KC_KEYUP | KC_LONEKEY)) == (KC_KEYUP | KC_LONEKEY)) { return true; } } return false; } bool nsWindow::OnQueryConvertPos(MPARAM mp1, MRESULT& mresult) { PRECTL pCursorPos = (PRECTL)mp1; nsIntPoint point(0, 0); WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, this); InitEvent(selection, &point); DispatchWindowEvent(&selection); if (!selection.mSucceeded) return false; WidgetQueryContentEvent caret(true, NS_QUERY_CARET_RECT, this); caret.InitForQueryCaretRect(selection.mReply.mOffset); InitEvent(caret, &point); DispatchWindowEvent(&caret); if (!caret.mSucceeded) return false; pCursorPos->xLeft = caret.mReply.mRect.x; pCursorPos->yBottom = caret.mReply.mRect.y; pCursorPos->xRight = pCursorPos->xLeft + caret.mReply.mRect.width; pCursorPos->yTop = pCursorPos->yBottom + caret.mReply.mRect.height; NS2PM(*pCursorPos); mresult = (MRESULT)QCP_CONVERT; return true; } bool nsWindow::ImeResultString(HIMI himi) { ULONG ulBufLen; // Get a buffer size ulBufLen = 0; if (spfnImGetResultString(himi, IMR_RESULT_RESULTSTRING, nullptr, &ulBufLen)) return false; nsAutoTArray compositionStringA; compositionStringA.SetCapacity(ulBufLen / sizeof(CHAR)); if (spfnImGetResultString(himi, IMR_RESULT_RESULTSTRING, compositionStringA.Elements(), &ulBufLen)) { return false; } if (!mIsComposing) { mLastDispatchedCompositionString.Truncate(); WidgetCompositionEvent start(true, NS_COMPOSITION_START, this); InitEvent(start); DispatchWindowEvent(&start); mIsComposing = true; } nsAutoChar16Buffer outBuf; int32_t outBufLen; MultiByteToWideChar(0, compositionStringA.Elements(), ulBufLen, outBuf, outBufLen); nsAutoString compositionString(outBuf.Elements()); if (mLastDispatchedCompositionString != compositionString) { WidgetCompositionEvent update(true, NS_COMPOSITION_UPDATE, this); InitEvent(update); update.data = compositionString; mLastDispatchedCompositionString = compositionString; DispatchWindowEvent(&update); } WidgetTextEvent text(true, NS_TEXT_TEXT, this); InitEvent(text); text.theText = compositionString; DispatchWindowEvent(&text); WidgetCompositionEvent end(true, NS_COMPOSITION_END, this); InitEvent(end); end.data = compositionString; DispatchWindowEvent(&end); mIsComposing = false; mLastDispatchedCompositionString.Truncate(); return true; } static uint32_t PlatformToNSAttr(uint8_t aAttr) { switch (aAttr) { case CP_ATTR_INPUT_ERROR: case CP_ATTR_INPUT: return NS_TEXTRANGE_RAWINPUT; case CP_ATTR_CONVERTED: return NS_TEXTRANGE_CONVERTEDTEXT; case CP_ATTR_TARGET_NOTCONVERTED: return NS_TEXTRANGE_SELECTEDRAWTEXT; case CP_ATTR_TARGET_CONVERTED: return NS_TEXTRANGE_SELECTEDCONVERTEDTEXT; default: MOZ_CRASH("unknown attribute"); } } bool nsWindow::ImeConversionString(HIMI himi) { ULONG ulBufLen; // Get a buffer size ulBufLen = 0; if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONSTRING, nullptr, &ulBufLen)) return false; nsAutoTArray compositionStringA; compositionStringA.SetCapacity(ulBufLen / sizeof(CHAR)); if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONSTRING, compositionStringA.Elements(), &ulBufLen)) { return false; } if (!mIsComposing) { mLastDispatchedCompositionString.Truncate(); WidgetCompositionEvent start(true, NS_COMPOSITION_START, this); InitEvent(start); DispatchWindowEvent(&start); mIsComposing = true; } nsAutoChar16Buffer outBuf; int32_t outBufLen; MultiByteToWideChar(0, compositionStringA.Elements(), ulBufLen, outBuf, outBufLen); nsAutoString compositionString(outBuf.Elements()); // Is a conversion string changed ? if (mLastDispatchedCompositionString != compositionString) { WidgetCompositionEvent update(true, NS_COMPOSITION_UPDATE, this); InitEvent(update); update.data = compositionString; mLastDispatchedCompositionString = compositionString; DispatchWindowEvent(&update); } nsAutoTArray textRanges; if (!compositionString.IsEmpty()) { bool oneClause = false; ulBufLen = 0; if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONCLAUSE, 0, &ulBufLen)) { oneClause = true; // Assume that there is only one clause } ULONG ulClauseCount = std::max(2UL, ulBufLen / sizeof(ULONG)); nsAutoTArray clauseOffsets; nsAutoTArray clauseAttr; ULONG ulCursorPos; clauseOffsets.SetCapacity(ulClauseCount); clauseAttr.SetCapacity(ulClauseCount); if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONCLAUSE, clauseOffsets.Elements(), &ulBufLen)) { oneClause = true; // Assume that there is only one clause } // Korean IME does not provide clause and cursor infomation // Or if getting a clause inforamtion was failed if (ulBufLen == 0 && !oneClause) { ulCursorPos = compositionString.Length(); oneClause = true; } else { while (!oneClause) { ulBufLen = 0; if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONATTR, 0, &ulBufLen)) { oneClause = true; break; } nsAutoTArray attr; attr.SetCapacity(ulBufLen / sizeof(UCHAR)); if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONATTR, attr.Elements(), &ulBufLen)) { oneClause = true; break; } // Assume that all the conversion attribute in a clause are same for (ULONG i = 0; i < ulClauseCount - 1; ++i) { clauseAttr[i] = attr[clauseOffsets[i]]; } // Convert ANSI string offsets to Unicode string offsets clauseOffsets[0] = 0; for (ULONG i = 1; i < ulClauseCount - 1; ++i) { MultiByteToWideChar(0, compositionStringA.Elements(), clauseOffsets[i], outBuf, outBufLen); clauseOffsets[i] = outBufLen; } break; } ulBufLen = sizeof(ULONG); if (spfnImGetConversionString(himi, IMR_CONV_CURSORPOS, &ulCursorPos, &ulBufLen)) { ulCursorPos = NO_IME_CARET; } else { // Convert ANSI string position to Unicode string position MultiByteToWideChar(0, compositionStringA.Elements(), ulCursorPos, outBuf, outBufLen); ulCursorPos = outBufLen; } } if (oneClause) { ulClauseCount = 2; clauseOffsets[0] = 0; clauseOffsets[1] = compositionString.Length(); clauseAttr[0] = NS_TEXTRANGE_SELECTEDRAWTEXT; } TextRange newRange; for (ULONG i = 0; i < ulClauseCount - 1; ++i) { newRange.mStartOffset = clauseOffsets[i]; newRange.mEndOffset = clauseOffsets[i + 1]; newRange.mRangeType = PlatformToNSAttr(clauseAttr[i]); textRanges.AppendElement(newRange); } if (ulCursorPos != NO_IME_CARET) { newRange.mStartOffset = newRange.mEndOffset = ulCursorPos; newRange.mRangeType = NS_TEXTRANGE_CARETPOSITION; textRanges.AppendElement(newRange); } } WidgetTextEvent text(true, NS_TEXT_TEXT, this); InitEvent(text); text.theText = compositionString; text.rangeArray = textRanges.Elements(); text.rangeCount = textRanges.Length(); DispatchWindowEvent(&text); if (compositionString.IsEmpty()) { // IME conversion was canceled ? WidgetCompositionEvent end(true, NS_COMPOSITION_END, this); InitEvent(end); end.data = compositionString; DispatchWindowEvent(&end); mIsComposing = false; mLastDispatchedCompositionString.Truncate(); } return true; } bool nsWindow::OnImeRequest(MPARAM mp1, MPARAM mp2) { HIMI himi; bool rc; if (!sIm32Mod) return false; if (SHORT1FROMMP(mp1) != IMR_CONVRESULT) return false; if (spfnImGetInstance(mWnd, &himi)) return false; if (LONGFROMMP(mp2) & IMR_RESULT_RESULTSTRING) rc = ImeResultString(himi); else if (LONGFROMMP(mp2) & IMR_CONV_CONVERSIONSTRING) rc = ImeConversionString(himi); else rc = true; spfnImReleaseInstance(mWnd, himi); return rc; } NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { HIMI himi; if (sIm32Mod && spfnImGetInstance(mWnd, &himi)) { mInputContext.mNativeIMEContext = static_cast(himi); } if (!mInputContext.mNativeIMEContext) { mInputContext.mNativeIMEContext = this; } return mInputContext; } //----------------------------------------------------------------------------- // Key handler. Specs for the various text messages are really confused; // see other platforms for best results of how things are supposed to work. // // Perhaps more importantly, the main man listening to these events // (besides random bits of javascript) is ender -- see // mozilla/editor/base/nsEditorEventListeners.cpp. bool nsWindow::DispatchKeyEvent(MPARAM mp1, MPARAM mp2) { WidgetKeyboardEvent pressEvent(true, 0, nullptr); USHORT fsFlags = SHORT1FROMMP(mp1); USHORT usVKey = SHORT2FROMMP(mp2); USHORT usChar = SHORT1FROMMP(mp2); UCHAR uchScan = CHAR4FROMMP(mp1); // It appears we're not supposed to transmit shift, // control, & alt events to gecko. if (fsFlags & KC_VIRTUALKEY && !(fsFlags & KC_KEYUP) && (usVKey == VK_SHIFT || usVKey == VK_CTRL || usVKey == VK_ALTGRAF)) { return false; } // Workaround bug where using Alt+Esc let an Alt key creep through // Only handle alt by itself if the LONEKEY bit is set if ((fsFlags & KC_VIRTUALKEY) && (usVKey == VK_ALT) && !usChar && (!(fsFlags & KC_LONEKEY)) && (fsFlags & KC_KEYUP)) { return false; } // Now check if it's a dead-key if (fsFlags & KC_DEADKEY) { return true; } // Now dispatch a keyup/keydown event. This one is *not* meant to // have the unicode charcode in. nsIntPoint point(0,0); WidgetKeyboardEvent event(true, (fsFlags & KC_KEYUP) ? NS_KEY_UP : NS_KEY_DOWN, this); InitEvent(event, &point); event.keyCode = WMChar2KeyCode(mp1, mp2); event.InitBasicModifiers(fsFlags & KC_CTRL, fsFlags & KC_ALT, fsFlags & KC_SHIFT, false); event.charCode = 0; event.mIsRepeat = event.message == NS_KEY_DOWN && CHAR3FROMMP(mp1) != 0; // Check for a scroll mouse event vs. a keyboard event. The way we know // this is that the repeat count is 0 and the key is not physically down. // Unfortunately, there is an exception here - if alt or ctrl are held // down, repeat count is set so we have to add special checks for them. if (((event.keyCode == NS_VK_UP) || (event.keyCode == NS_VK_DOWN)) && !(fsFlags & KC_KEYUP) && (!CHAR3FROMMP(mp1) || fsFlags & KC_CTRL || fsFlags & KC_ALT)) { if (!(WinGetPhysKeyState(HWND_DESKTOP, uchScan) & 0x8000)) { MPARAM mp2; if (event.keyCode == NS_VK_UP) { mp2 = MPFROM2SHORT(0, SB_LINEUP); } else { mp2 = MPFROM2SHORT(0, SB_LINEDOWN); } WinSendMsg(mWnd, WM_VSCROLL, 0, mp2); return FALSE; } } pressEvent = event; bool rc = DispatchWindowEvent(&event); // Break off now if this was a key-up. if (fsFlags & KC_KEYUP) { return rc; } // Don't dispatch keypress event if keydown event is consumed. if (rc) { return rc; } // Break off if we've got an "invalid composition" -- that is, // the user typed a deadkey last time, but has now typed something // that doesn't make sense in that context. if (fsFlags & KC_INVALIDCOMP) { // actually, not sure whether we're supposed to abort the keypress // or process it as though the dead key has been pressed. return rc; } // Now we need to dispatch a keypress event which has the unicode char. // If keydown default was prevented, do same for keypress pressEvent.message = NS_KEY_PRESS; if (usChar) { USHORT inbuf[2]; inbuf[0] = usChar; inbuf[1] = '\0'; nsAutoChar16Buffer outbuf; int32_t bufLength; MultiByteToWideChar(0, (const char*)inbuf, 2, outbuf, bufLength); pressEvent.charCode = outbuf[0]; if (pressEvent.IsControl() && !(fsFlags & (KC_VIRTUALKEY | KC_DEADKEY))) { if (!pressEvent.IsShift() && (pressEvent.charCode >= 'A' && pressEvent.charCode <= 'Z')) { pressEvent.charCode = tolower(pressEvent.charCode); } if (pressEvent.IsShift() && (pressEvent.charCode >= 'a' && pressEvent.charCode <= 'z')) { pressEvent.charCode = toupper(pressEvent.charCode); } pressEvent.keyCode = 0; } else if (!pressEvent.IsControl() && !pressEvent.IsAlt() && pressEvent.charCode != 0) { if (!(fsFlags & KC_VIRTUALKEY) || // not virtual key ((fsFlags & KC_CHAR) && !pressEvent.keyCode)) { pressEvent.keyCode = 0; } else if (usVKey == VK_SPACE) { // space key, do nothing here } else if ((fsFlags & KC_VIRTUALKEY) && isNumPadScanCode(uchScan) && pressEvent.keyCode != 0 && isNumlockOn) { // this is NumLock+Numpad (no Alt), handle this like a normal number pressEvent.keyCode = 0; } else { // Real virtual key pressEvent.charCode = 0; } } rc = DispatchWindowEvent(&pressEvent); } return rc; } //----------------------------------------------------------------------------- // Helper function to translate from a WM_CHAR to an NS_VK_ constant. static uint32_t WMChar2KeyCode(MPARAM mp1, MPARAM mp2) { uint32_t rc = SHORT1FROMMP(mp2); // character code uint32_t rcmask = rc & 0x00FF; // masked character code for key up events USHORT sc = CHAR4FROMMP(mp1); // scan code USHORT flags = SHORT1FROMMP(mp1); // flag word // First check for characters. // This is complicated by keystrokes such as Ctrl+K not having the KC_CHAR // bit set, but thankfully they do have the character actually there. // Assume that `if not vkey or deadkey or valid number then char' if (!(flags & (KC_VIRTUALKEY | KC_DEADKEY)) || (rcmask >= '0' && rcmask <= '9' && // handle keys on Numpad, too, (isNumPadScanCode(sc) ? isNumlockOn : 1))) { // if NumLock is on if (flags & KC_KEYUP) { // On OS/2 the scancode is in the upper byte of // usChar when KC_KEYUP is set so mask it off rc = rcmask; } else { // not KC_KEYUP if (!(flags & KC_CHAR)) { if ((flags & KC_ALT) || (flags & KC_CTRL)) { rc = rcmask; } else { rc = 0; } } } if (rc < 0xFF) { if (rc >= 'a' && rc <= 'z') { // The DOM_VK are for upper case only so // if rc is lower case upper case it. rc = rc - 'a' + NS_VK_A; } else if (rc >= 'A' && rc <= 'Z') { // Upper case rc = rc - 'A' + NS_VK_A; } else if (rc >= '0' && rc <= '9') { // Number keys, including Numpad if NumLock is not set rc = rc - '0' + NS_VK_0; } else { // For some characters, map the scan code to the NS_VK value // This only happens in the char case NOT the VK case! switch (sc) { case 0x02: rc = NS_VK_1; break; case 0x03: rc = NS_VK_2; break; case 0x04: rc = NS_VK_3; break; case 0x05: rc = NS_VK_4; break; case 0x06: rc = NS_VK_5; break; case 0x07: rc = NS_VK_6; break; case 0x08: rc = NS_VK_7; break; case 0x09: rc = NS_VK_8; break; case 0x0A: rc = NS_VK_9; break; case 0x0B: rc = NS_VK_0; break; case 0x0D: rc = NS_VK_EQUALS; break; case 0x1A: rc = NS_VK_OPEN_BRACKET; break; case 0x1B: rc = NS_VK_CLOSE_BRACKET; break; case 0x27: rc = NS_VK_SEMICOLON; break; case 0x28: rc = NS_VK_QUOTE; break; case 0x29: rc = NS_VK_BACK_QUOTE; break; case 0x2B: rc = NS_VK_BACK_SLASH; break; case 0x33: rc = NS_VK_COMMA; break; case 0x34: rc = NS_VK_PERIOD; break; case 0x35: rc = NS_VK_SLASH; break; case 0x37: rc = NS_VK_MULTIPLY; break; case 0x4A: rc = NS_VK_SUBTRACT; break; case 0x4C: rc = NS_VK_CLEAR; break; // numeric case is handled above case 0x4E: rc = NS_VK_ADD; break; case 0x5C: rc = NS_VK_DIVIDE; break; default: break; } // switch } // else } // if (rc < 0xFF) } else if (flags & KC_VIRTUALKEY) { USHORT vk = SHORT2FROMMP(mp2); if (flags & KC_KEYUP) { // On OS/2 there are extraneous bits in the upper byte of // usChar when KC_KEYUP is set so mask them off rc = rcmask; } if (isNumPadScanCode(sc) && (((flags & KC_ALT) && (sc != PMSCAN_PADPERIOD)) || ((flags & (KC_CHAR | KC_SHIFT)) == KC_CHAR) || ((flags & KC_KEYUP) && rc != 0))) { CHAR numpadMap[] = {NS_VK_NUMPAD7, NS_VK_NUMPAD8, NS_VK_NUMPAD9, 0, NS_VK_NUMPAD4, NS_VK_NUMPAD5, NS_VK_NUMPAD6, 0, NS_VK_NUMPAD1, NS_VK_NUMPAD2, NS_VK_NUMPAD3, NS_VK_NUMPAD0, NS_VK_DECIMAL}; // If this is the Numpad must not return VK for ALT+Numpad or ALT+NumLock+Numpad // NumLock+Numpad is OK if (numpadMap[sc - PMSCAN_PAD7] != 0) { // not plus or minus on Numpad if (flags & KC_ALT) { // do not react on Alt plus ASCII-code sequences rc = 0; } else { rc = numpadMap[sc - PMSCAN_PAD7]; } } else { // plus or minus of Numpad rc = 0; // No virtual key for Alt+Numpad or NumLock+Numpad } } else if (!(flags & KC_CHAR) || isNumPadScanCode(sc) || (vk == VK_BACKSPACE) || (vk == VK_TAB) || (vk == VK_BACKTAB) || (vk == VK_ENTER) || (vk == VK_NEWLINE) || (vk == VK_SPACE)) { if (vk >= VK_F1 && vk <= VK_F24) { rc = NS_VK_F1 + (vk - VK_F1); } else switch (vk) { case VK_NUMLOCK: rc = NS_VK_NUM_LOCK; break; case VK_SCRLLOCK: rc = NS_VK_SCROLL_LOCK; break; case VK_ESC: rc = NS_VK_ESCAPE; break; // NS_VK_CANCEL case VK_BACKSPACE: rc = NS_VK_BACK; break; case VK_TAB: rc = NS_VK_TAB; break; case VK_BACKTAB: rc = NS_VK_TAB; break; // layout tests for isShift case VK_CLEAR: rc = NS_VK_CLEAR; break; case VK_NEWLINE: rc = NS_VK_RETURN; break; case VK_ENTER: rc = NS_VK_RETURN; break; case VK_SHIFT: rc = NS_VK_SHIFT; break; case VK_CTRL: rc = NS_VK_CONTROL; break; case VK_ALT: rc = NS_VK_ALT; break; case VK_PAUSE: rc = NS_VK_PAUSE; break; case VK_CAPSLOCK: rc = NS_VK_CAPS_LOCK; break; case VK_SPACE: rc = NS_VK_SPACE; break; case VK_PAGEUP: rc = NS_VK_PAGE_UP; break; case VK_PAGEDOWN: rc = NS_VK_PAGE_DOWN; break; case VK_END: rc = NS_VK_END; break; case VK_HOME: rc = NS_VK_HOME; break; case VK_LEFT: rc = NS_VK_LEFT; break; case VK_UP: rc = NS_VK_UP; break; case VK_RIGHT: rc = NS_VK_RIGHT; break; case VK_DOWN: rc = NS_VK_DOWN; break; case VK_PRINTSCRN: rc = NS_VK_PRINTSCREEN; break; case VK_INSERT: rc = NS_VK_INSERT; break; case VK_DELETE: rc = NS_VK_DELETE; break; } // switch } } // KC_VIRTUALKEY return rc; } //============================================================================= // Event Dispatch //============================================================================= // Initialize an event to dispatch. void nsWindow::InitEvent(WidgetGUIEvent& event, nsIntPoint* aPoint) { // if no point was supplied, calculate it if (!aPoint) { // for most events, get the message position; for drag events, // msg position may be incorrect, so get the current position instead POINTL ptl; if (CheckDragStatus(ACTION_PTRPOS, 0)) { WinQueryPointerPos(HWND_DESKTOP, &ptl); } else { WinQueryMsgPos(0, &ptl); } WinMapWindowPoints(HWND_DESKTOP, mWnd, &ptl, 1); PM2NS(ptl); event.refPoint.x = ptl.x; event.refPoint.y = ptl.y; } else { // use the point override if provided event.refPoint.x = aPoint->x; event.refPoint.y = aPoint->y; } event.time = WinQueryMsgTime(0); return; } //----------------------------------------------------------------------------- // Invoke the Event Listener object's callback. NS_IMETHODIMP nsWindow::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus) { aStatus = nsEventStatus_eIgnore; if (!mEventCallback) { return NS_OK; } // if state is eDoingDelete, don't send out anything if (mWindowState & nsWindowState_eLive) { aStatus = (*mEventCallback)(event); } return NS_OK; } //----------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) { NS_PRECONDITION(aNewParent, ""); return NS_ERROR_NOT_IMPLEMENTED; } //----------------------------------------------------------------------------- bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event) { nsEventStatus status; DispatchEvent(event, status); return (status == nsEventStatus_eConsumeNoDefault); } bool nsWindow::DispatchWindowEvent(WidgetGUIEvent*event, nsEventStatus &aStatus) { DispatchEvent(event, aStatus); return (aStatus == nsEventStatus_eConsumeNoDefault); } //----------------------------------------------------------------------------- bool nsWindow::DispatchCommandEvent(uint32_t aEventCommand) { nsCOMPtr command; switch (aEventCommand) { case APPCOMMAND_BROWSER_BACKWARD: command = nsGkAtoms::Back; break; case APPCOMMAND_BROWSER_FORWARD: command = nsGkAtoms::Forward; break; case APPCOMMAND_BROWSER_REFRESH: command = nsGkAtoms::Reload; break; case APPCOMMAND_BROWSER_STOP: command = nsGkAtoms::Stop; break; default: return false; } WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this); InitEvent(event); return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- bool nsWindow::DispatchDragDropEvent(uint32_t aMsg) { WidgetDragEvent event(true, aMsg, this); InitEvent(event); event.InitBasicModifiers(isKeyDown(VK_CTRL), isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF), isKeyDown(VK_SHIFT), false); return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- bool nsWindow::DispatchMoveEvent(int32_t aX, int32_t aY) { // Params here are in XP-space for the desktop WidgetGUIEvent event(true, NS_MOVE, this); nsIntPoint point(aX, aY); InitEvent(event, &point); return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- bool nsWindow::DispatchResizeEvent(int32_t aX, int32_t aY) { nsSizeEvent event(true, NS_SIZE, this); nsIntRect rect(0, 0, aX, aY); InitEvent(event); event.windowSize = ▭ // this is the *client* rectangle event.mWinWidth = mBounds.width; event.mWinHeight = mBounds.height; return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- // Deal with all sorts of mouse events. bool nsWindow::DispatchMouseEvent(uint32_t aEventType, MPARAM mp1, MPARAM mp2, bool aIsContextMenuKey, int16_t aButton) { NS_ENSURE_TRUE(aEventType, false); WidgetMouseEvent event(true, aEventType, this, WidgetMouseEvent::eReal, aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey : WidgetMouseEvent::eNormal); event.button = aButton; if (aEventType == NS_MOUSE_BUTTON_DOWN && mIsComposing) { // If IME is composing, let it complete. HIMI himi; spfnImGetInstance(mWnd, &himi); spfnImRequestIME(himi, REQ_CONVERSIONSTRING, CNV_COMPLETE, 0); spfnImReleaseInstance(mWnd, himi); } if (aEventType == NS_MOUSE_ENTER || aEventType == NS_MOUSE_EXIT) { // Ignore enter/leave msgs forwarded from the frame to FID_CLIENT // because we're only interested msgs involving the content area. if (HWNDFROMMP(mp1) != mWnd) { return FALSE; } // If the mouse has exited the content area and entered either an // unrelated window or what Windows would call the nonclient area // (i.e. frame, titlebar, etc.), mark this as a toplevel exit. // Note: exits to and from menus will also be marked toplevel. if (aEventType == NS_MOUSE_EXIT) { HWND hTop = 0; HWND hCur = mWnd; HWND hDesk = WinQueryDesktopWindow(0, 0); while (hCur && hCur != hDesk) { hTop = hCur; hCur = WinQueryWindow(hCur, QW_PARENT); } // event.exit was init'ed to eChild, so we don't need an 'else' hTop = WinWindowFromID(hTop, FID_CLIENT); if (!hTop || !WinIsChild(HWNDFROMMP(mp2), hTop)) { event.exit = WidgetMouseEvent::eTopLevel; } } InitEvent(event, nullptr); event.InitBasicModifiers(isKeyDown(VK_CTRL), isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF), isKeyDown(VK_SHIFT), false); } else { POINTL ptl; if (aEventType == NS_CONTEXTMENU && aIsContextMenuKey) { WinQueryPointerPos(HWND_DESKTOP, &ptl); WinMapWindowPoints(HWND_DESKTOP, mWnd, &ptl, 1); } else { ptl.x = (SHORT)SHORT1FROMMP(mp1); ptl.y = (SHORT)SHORT2FROMMP(mp1); } PM2NS(ptl); nsIntPoint pt(ptl.x, ptl.y); InitEvent(event, &pt); USHORT usFlags = SHORT2FROMMP(mp2); event.InitBasicModifiers(usFlags & KC_CTRL, usFlags & KC_ALT, usFlags & KC_SHIFT, false); } // Dblclicks are used to set the click count, then changed to mousedowns if (aEventType == NS_MOUSE_DOUBLECLICK && (aButton == WidgetMouseEvent::eLeftButton || aButton == WidgetMouseEvent::eRightButton)) { event.message = NS_MOUSE_BUTTON_DOWN; event.button = (aButton == WidgetMouseEvent::eLeftButton) ? WidgetMouseEvent::eLeftButton : WidgetMouseEvent::eRightButton; event.clickCount = 2; } else { event.clickCount = 1; } NPEvent pluginEvent; switch (aEventType) { case NS_MOUSE_BUTTON_DOWN: switch (aButton) { case WidgetMouseEvent::eLeftButton: pluginEvent.event = WM_BUTTON1DOWN; break; case WidgetMouseEvent::eMiddleButton: pluginEvent.event = WM_BUTTON3DOWN; break; case WidgetMouseEvent::eRightButton: pluginEvent.event = WM_BUTTON2DOWN; break; default: break; } break; case NS_MOUSE_BUTTON_UP: switch (aButton) { case WidgetMouseEvent::eLeftButton: pluginEvent.event = WM_BUTTON1UP; break; case WidgetMouseEvent::eMiddleButton: pluginEvent.event = WM_BUTTON3UP; break; case WidgetMouseEvent::eRightButton: pluginEvent.event = WM_BUTTON2UP; break; default: break; } break; case NS_MOUSE_DOUBLECLICK: switch (aButton) { case WidgetMouseEvent::eLeftButton: pluginEvent.event = WM_BUTTON1DBLCLK; break; case WidgetMouseEvent::eMiddleButton: pluginEvent.event = WM_BUTTON3DBLCLK; break; case WidgetMouseEvent::eRightButton: pluginEvent.event = WM_BUTTON2DBLCLK; break; default: break; } break; case NS_MOUSE_MOVE: pluginEvent.event = WM_MOUSEMOVE; break; } pluginEvent.wParam = 0; pluginEvent.lParam = MAKELONG(event.refPoint.x, event.refPoint.y); event.pluginEvent = (void*)&pluginEvent; return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- // Signal plugin & top-level window activation. bool nsWindow::DispatchActivationEvent(uint32_t aEventType) { WidgetGUIEvent event(true, aEventType, this); // These events should go to their base widget location, // not current mouse position. nsIntPoint point(0, 0); InitEvent(event, &point); NPEvent pluginEvent; switch (aEventType) { case NS_ACTIVATE: pluginEvent.event = WM_SETFOCUS; break; case NS_DEACTIVATE: pluginEvent.event = WM_FOCUSCHANGED; break; case NS_PLUGIN_ACTIVATE: pluginEvent.event = WM_FOCUSCHANGED; break; } event.pluginEvent = (void*)&pluginEvent; return DispatchWindowEvent(&event); } //----------------------------------------------------------------------------- bool nsWindow::DispatchScrollEvent(ULONG msg, MPARAM mp1, MPARAM mp2) { WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this); InitEvent(wheelEvent); wheelEvent.InitBasicModifiers(isKeyDown(VK_CTRL), isKeyDown(VK_ALT) || isKeyDown(VK_ALTGRAF), isKeyDown(VK_SHIFT), false); // The SB_* constants for analogous vertical & horizontal ops have the // the same values, so only use the verticals to avoid compiler errors. int32_t delta; switch (SHORT2FROMMP(mp2)) { case SB_LINEUP: // SB_LINELEFT: wheelEvent.deltaMode = nsIDOMWheelEvent.DOM_DELTA_LINE; delta = -1; break; case SB_LINEDOWN: // SB_LINERIGHT: wheelEvent.deltaMode = nsIDOMWheelEvent.DOM_DELTA_LINE; delta = 1; break; case SB_PAGEUP: // SB_PAGELEFT: wheelEvent.deltaMode = nsIDOMWheelEvent.DOM_DELTA_PAGE; delta = -1; break; case SB_PAGEDOWN: // SB_PAGERIGHT: wheelEvent.deltaMode = nsIDOMWheelEvent.DOM_DELTA_PAGE; delta = 1; break; default: return false; } if (msg == WM_HSCROLL) { wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = delta; } else { wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = delta; } DispatchWindowEvent(&wheelEvent); return false; } //=============================================================================