/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim:expandtab:shiftwidth=4:tabstop=4: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Christopher Blizzard * . Portions created by the Initial Developer * are Copyright (C) 2001 the Initial Developer. All Rights Reserved. * * Contributor(s): * Mats Palmgren * Masayuki Nakano * Romashin Oleg * Vladimir Vukicevic * Chris Lord * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsWindow.h" #include "nsIDeviceContext.h" #include "nsIRenderingContext.h" #include "nsIRollupListener.h" #include "moz-drawing-area.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsGfxCIID.h" #include "gfxPlatformHeadless.h" #include "gfxContext.h" #include "gfxImageSurface.h" #include #include "cairo.h" #ifdef MOZ_ENABLE_GTK2_PLUGINS #include #include #include #include "gtk2xtbin.h" #include #endif /* initialization static functions */ static nsresult initialize_prefs (void); static PRBool gGlobalsInitialized = PR_FALSE; static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID); static NS_DEFINE_CID(kRegionCID, NS_REGION_CID); #ifdef MOZ_ENABLE_GTK2_PLUGINS static nsWindow *gPluginFocusWindow = NULL; static gboolean bypass_plugin_port_deleted (GtkWidget *widget, GdkEvent *event, gpointer user_data); // from gtk2 widget implementation struct nsKeyConverter { int vkCode; // Platform independent key code int keysym; // GDK keysym key code }; struct nsKeyConverter nsKeycodes[] = { { NS_VK_CANCEL, GDK_Cancel }, { NS_VK_BACK, GDK_BackSpace }, { NS_VK_TAB, GDK_Tab }, { NS_VK_TAB, GDK_ISO_Left_Tab }, { NS_VK_CLEAR, GDK_Clear }, { NS_VK_RETURN, GDK_Return }, { NS_VK_SHIFT, GDK_Shift_L }, { NS_VK_SHIFT, GDK_Shift_R }, { NS_VK_CONTROL, GDK_Control_L }, { NS_VK_CONTROL, GDK_Control_R }, { NS_VK_ALT, GDK_Alt_L }, { NS_VK_ALT, GDK_Alt_R }, { NS_VK_META, GDK_Meta_L }, { NS_VK_META, GDK_Meta_R }, { NS_VK_PAUSE, GDK_Pause }, { NS_VK_CAPS_LOCK, GDK_Caps_Lock }, { NS_VK_ESCAPE, GDK_Escape }, { NS_VK_SPACE, GDK_space }, { NS_VK_PAGE_UP, GDK_Page_Up }, { NS_VK_PAGE_DOWN, GDK_Page_Down }, { NS_VK_END, GDK_End }, { NS_VK_HOME, GDK_Home }, { NS_VK_LEFT, GDK_Left }, { NS_VK_UP, GDK_Up }, { NS_VK_RIGHT, GDK_Right }, { NS_VK_DOWN, GDK_Down }, { NS_VK_PRINTSCREEN, GDK_Print }, { NS_VK_INSERT, GDK_Insert }, { NS_VK_DELETE, GDK_Delete }, // keypad keys { NS_VK_LEFT, GDK_KP_Left }, { NS_VK_RIGHT, GDK_KP_Right }, { NS_VK_UP, GDK_KP_Up }, { NS_VK_DOWN, GDK_KP_Down }, { NS_VK_PAGE_UP, GDK_KP_Page_Up }, // Not sure what these are //{ NS_VK_, GDK_KP_Prior }, //{ NS_VK_, GDK_KP_Next }, // GDK_KP_Begin is the 5 on the non-numlock keypad //{ NS_VK_, GDK_KP_Begin }, { NS_VK_PAGE_DOWN, GDK_KP_Page_Down }, { NS_VK_HOME, GDK_KP_Home }, { NS_VK_END, GDK_KP_End }, { NS_VK_INSERT, GDK_KP_Insert }, { NS_VK_DELETE, GDK_KP_Delete }, { NS_VK_MULTIPLY, GDK_KP_Multiply }, { NS_VK_ADD, GDK_KP_Add }, { NS_VK_SEPARATOR, GDK_KP_Separator }, { NS_VK_SUBTRACT, GDK_KP_Subtract }, { NS_VK_DECIMAL, GDK_KP_Decimal }, { NS_VK_DIVIDE, GDK_KP_Divide }, { NS_VK_RETURN, GDK_KP_Enter }, { NS_VK_NUM_LOCK, GDK_Num_Lock }, { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock }, { NS_VK_COMMA, GDK_comma }, { NS_VK_PERIOD, GDK_period }, { NS_VK_SLASH, GDK_slash }, { NS_VK_BACK_SLASH, GDK_backslash }, { NS_VK_BACK_QUOTE, GDK_grave }, { NS_VK_OPEN_BRACKET, GDK_bracketleft }, { NS_VK_CLOSE_BRACKET, GDK_bracketright }, { NS_VK_SEMICOLON, GDK_colon }, { NS_VK_QUOTE, GDK_apostrophe }, // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft) // x86 keyboards, located between right 'Windows' key and right Ctrl key { NS_VK_CONTEXT_MENU, GDK_Menu }, // NS doesn't have dash or equals distinct from the numeric keypad ones, // so we'll use those for now. See bug 17008: { NS_VK_SUBTRACT, GDK_minus }, { NS_VK_EQUALS, GDK_equal }, // Some shifted keys, see bug 15463 as well as 17008. // These should be subject to different keyboard mappings. { NS_VK_QUOTE, GDK_quotedbl }, { NS_VK_OPEN_BRACKET, GDK_braceleft }, { NS_VK_CLOSE_BRACKET, GDK_braceright }, { NS_VK_BACK_SLASH, GDK_bar }, { NS_VK_SEMICOLON, GDK_semicolon }, { NS_VK_BACK_QUOTE, GDK_asciitilde }, { NS_VK_COMMA, GDK_less }, { NS_VK_PERIOD, GDK_greater }, { NS_VK_SLASH, GDK_question }, { NS_VK_1, GDK_exclam }, { NS_VK_2, GDK_at }, { NS_VK_3, GDK_numbersign }, { NS_VK_4, GDK_dollar }, { NS_VK_5, GDK_percent }, { NS_VK_6, GDK_asciicircum }, { NS_VK_7, GDK_ampersand }, { NS_VK_8, GDK_asterisk }, { NS_VK_9, GDK_parenleft }, { NS_VK_0, GDK_parenright }, { NS_VK_SUBTRACT, GDK_underscore }, { NS_VK_EQUALS, GDK_plus } }; #endif nsWindow::nsWindow() { //printf ("Window created (%p)\n", (void *)this); mIsDestroyed = PR_FALSE; mIsShown = PR_FALSE; mIsModal = PR_FALSE; mEnabled = PR_TRUE; mPreferredWidth = 0; mPreferredHeight = 0; mParent = nsnull; mDrawingarea = nsnull; mThebesSurface = nsnull; mInvalidRegion = nsnull; mScrollRegion = nsnull; mScratchRegion = nsnull; for (unsigned int i = 0; i < G_N_ELEMENTS (mExposeSignals); i++) mExposeSignals[i] = 0; mSurfaceSignal = 0; for (unsigned int i = 0; i < G_N_ELEMENTS (mBoundsSignals); i++) mBoundsSignals[i] = 0; for (unsigned int i = 0; i < G_N_ELEMENTS (mEventSignals); i++) mEventSignals[i] = 0; mUpdateDebug = 0; memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags)); #ifdef MOZ_ENABLE_GTK2_PLUGINS mPluginPort = NULL; mPluginType = PluginType_NONE; mOldFocusWindow = 0; #endif #ifdef SUPPORT_IM mIMEEnabled = nsIWidget::IME_STATUS_DISABLED; #endif if (!gGlobalsInitialized) { gGlobalsInitialized = PR_TRUE; // It's OK if either of these fail, but it may not be one day. initialize_prefs(); } } nsWindow::~nsWindow() { //printf ("Window deleted (%p)\n", (void *)this); Destroy(); } /* static */ nsresult initialize_prefs(void) { /* nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); if (!prefs) return NS_OK; PRBool val = PR_TRUE; nsresult rv; rv = prefs->GetBoolPref("mozilla.widget.raise-on-setfocus", &val); if (NS_SUCCEEDED(rv)) gRaiseWindows = val; rv = prefs->GetBoolPref("mozilla.widget.force-24bpp", &val); if (NS_SUCCEEDED(rv)) gForce24bpp = val; rv = prefs->GetBoolPref("mozilla.widget.use-buffer-pixmap", &val); if (NS_SUCCEEDED(rv)) gUseBufferPixmap = val; rv = prefs->GetBoolPref("mozilla.widget.disable-native-theme", &val); if (NS_SUCCEEDED(rv)) gDisableNativeTheme = val;*/ return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED1(nsWindow, nsBaseWidget, nsISupportsWeakReference) static void pre_expose_cb (MozDrawingArea *area) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Pre-expose cb (%p)\n", data); if (data) { nsWindow *window = static_cast(data); window->PropagateInvalidRegions (); } } static void expose_cb (MozDrawingArea *area) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Expose cb (%p)\n", data); if (data) { nsWindow *window = static_cast(data); window->Redraw (); } } /* static void debug_output (MozDrawingArea *area) { cairo_surface_t *surface = moz_drawing_area_get_surface (area, NULL, NULL); static gint number = 0; printf ("Debug (%d) (%p)", number, (void *)area); if (surface) { printf ("(%dx%d)", cairo_image_surface_get_width (surface), cairo_image_surface_get_height (surface)); #if 0 cairo_surface_write_to_png (surface, "/tmp/gecko-headless-debug.png"); #else const gchar *data = (const gchar *)cairo_image_surface_get_data (surface); if (data) { gchar *filename = g_strdup_printf ("/tmp/gecko-headless-debug-%p-%02d.raw", (void *)area, number); g_file_set_contents (filename, data, cairo_image_surface_get_height (surface) * cairo_image_surface_get_stride (surface), NULL); g_free (filename); number ++; } else printf ("(no data)"); #endif } printf ("\n"); //moz_drawing_area_print_tree (area); //printf ("\n"); } static void update_debug_cb (MozDrawingArea *area, MozDrawingAreaRect *rect) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); if (data) { nsWindow *window = static_cast(data); MozDrawingArea *area = (MozDrawingArea *)window->GetNativeData (NS_NATIVE_WINDOW); if (!moz_drawing_area_get_parent (area)) { debug_output (area); printf ("Damaged area: +%d+%d, %dx%d\n", rect->x, rect->y, rect->width, rect->height); } } } */ static void surface_notify (MozDrawingArea *area) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); if (data) { nsWindow *window = static_cast(data); window->Invalidate (PR_FALSE); } } static void move_notify (MozDrawingArea *area) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Move notify (%p)\n", data); if (data) { nsWindow *window = static_cast(data); nsGUIEvent event(PR_TRUE, NS_MOVE, window); gint x, y; moz_drawing_area_get_bounds (area, &x, &y, NULL, NULL); event.refPoint.x = x; event.refPoint.y = y; nsEventStatus status; window->DispatchEvent (&event, status); } } static void size_notify (MozDrawingArea *area) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Size notify (%p)\n", data); if (data) { nsWindow *window = static_cast(data); nsGUIEvent event(PR_TRUE, NS_MOVE, window); gint x, y, width, height; moz_drawing_area_get_bounds (area, &x, &y, &width, &height); nsEventStatus status; nsIntRect bounds = nsIntRect(x, y, width, height); window->DispatchResizeEvent (bounds, status); } } static void init_key_event (nsKeyEvent *event, MozDrawingAreaModifier modifiers) { event->isShift = (modifiers & MOZ_DA_SHIFT_MASK) ? PR_TRUE : PR_FALSE; event->isControl = (modifiers & MOZ_DA_CONTROL_MASK) ? PR_TRUE : PR_FALSE; event->isAlt = (modifiers & MOZ_DA_ALT_MASK) ? PR_TRUE : PR_FALSE; event->isMeta = (modifiers & MOZ_DA_META_MASK) ? PR_TRUE : PR_FALSE; event->time = 0; } static void init_mouse_event (nsMouseEvent *event, MozDrawingAreaModifier modifiers) { event->isShift = (modifiers & MOZ_DA_SHIFT_MASK) ? PR_TRUE : PR_FALSE; event->isControl = (modifiers & MOZ_DA_CONTROL_MASK) ? PR_TRUE : PR_FALSE; event->isAlt = (modifiers & MOZ_DA_ALT_MASK) ? PR_TRUE : PR_FALSE; event->isMeta = (modifiers & MOZ_DA_META_MASK) ? PR_TRUE : PR_FALSE; event->time = 0; } static void motion_cb (MozDrawingArea *area, gint x, gint y, MozDrawingAreaModifier modifiers) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); // printf ("Motion (%p, %d, %d)\n", data, x, y); if (data) { nsWindow *window = static_cast(data); nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, window, nsMouseEvent::eReal); init_mouse_event (&event, modifiers); event.refPoint.x = x; event.refPoint.y = y; nsEventStatus status; window->DispatchEvent (&event, status); } } static void button_press_cb (MozDrawingArea *area, gint x, gint y, gint button, gint click_count, MozDrawingAreaModifier modifiers) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Button-press (%p, %d, %d, %d, %d)\n", data, x, y, button, click_count); if (data) { nsWindow *window = static_cast(data); PRUint16 domButton; switch (button) { case 1: domButton = nsMouseEvent::eLeftButton; break; case 2: domButton = nsMouseEvent::eMiddleButton; break; case 3: domButton = nsMouseEvent::eRightButton; break; case 4: case 5: case 6: case 7: { nsMouseScrollEvent event(PR_TRUE, NS_MOUSE_SCROLL, window); event.refPoint.x = x; event.refPoint.y = y; if (button > 5) { event.scrollFlags = nsMouseScrollEvent::kIsHorizontal; event.delta = (button == 6) ? -2 : 2; } else { event.scrollFlags = nsMouseScrollEvent::kIsVertical; event.delta = (button == 4) ? -3 : 3; } event.time = 0; nsEventStatus status; window->DispatchEvent(&event, status); return; } case 8: window->DispatchCommandEvent(nsWidgetAtoms::Back); return; case 9: window->DispatchCommandEvent(nsWidgetAtoms::Forward); return; default: return; } nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_DOWN, window, nsMouseEvent::eReal); init_mouse_event (&event, modifiers); event.refPoint.x = x; event.refPoint.y = y; event.button = domButton; event.clickCount = click_count; nsEventStatus status; window->DispatchEvent (&event, status); if (domButton == nsMouseEvent::eRightButton) { nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, window, nsMouseEvent::eReal); init_mouse_event (&contextMenuEvent, modifiers); contextMenuEvent.refPoint.x = x; contextMenuEvent.refPoint.y = y; contextMenuEvent.button = domButton; contextMenuEvent.clickCount = click_count; window->DispatchEvent(&contextMenuEvent, status); } } } static void button_release_cb (MozDrawingArea *area, gint x, gint y, gint button, MozDrawingAreaModifier modifiers) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); //printf ("Button-release (%p, %d, %d, %d)\n", data, x, y, button); if (data) { nsWindow *window = static_cast(data); PRUint16 domButton; switch (button) { case 1: domButton = nsMouseEvent::eLeftButton; break; case 2: domButton = nsMouseEvent::eMiddleButton; break; case 3: domButton = nsMouseEvent::eRightButton; break; default: return; } nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_UP, window, nsMouseEvent::eReal); init_mouse_event (&event, modifiers); event.refPoint.x = x; event.refPoint.y = y; event.button = domButton; event.clickCount = 1; // Probably don't need to initialise this? nsEventStatus status; window->DispatchEvent (&event, status); // FIXME: Do we need to do the context menu event thing like in gtk? } } #ifdef MOZ_ENABLE_GTK2_PLUGINS // To pass the GdkKeyEvent to plugin static int DOMKeyCodeToGdkKeyCode(int aKeysym) { int i, length = 0; // First, try to handle alphanumeric input, not listed in nsKeycodes: // most likely, more letters will be getting typed in than things in // the key list, so we will look through these first. if (aKeysym >= NS_VK_A && aKeysym <= NS_VK_Z) // gdk and DOM both use the ASCII codes for these keys. return aKeysym; // numbers if (aKeysym >= NS_VK_0 && aKeysym <= NS_VK_9) // gdk and DOM both use the ASCII codes for these keys. return aKeysym - GDK_0 + NS_VK_0; // keypad numbers if (aKeysym >= NS_VK_NUMPAD0 && aKeysym <= NS_VK_NUMPAD9) return aKeysym - NS_VK_NUMPAD0 + GDK_KP_0; // misc other things length = NS_ARRAY_LENGTH(nsKeycodes); for (i = 0; i < length; ++i) { if (nsKeycodes[i].vkCode == aKeysym) { return nsKeycodes[i].keysym; } } // function keys if (aKeysym >= NS_VK_F1 && aKeysym <= NS_VK_F9) return aKeysym - NS_VK_F1 + GDK_F1; return 0; } static GdkModifierType MozModiferToGdkModifer (MozDrawingAreaModifier modifiers) { guint state = 0; state |= (modifiers & MOZ_DA_SHIFT_MASK) ? GDK_SHIFT_MASK : 0; state |= (modifiers & MOZ_DA_CONTROL_MASK) ? GDK_CONTROL_MASK : 0; state |= (modifiers & MOZ_DA_ALT_MASK) ? GDK_MOD1_MASK : 0; state |= (modifiers & MOZ_DA_META_MASK) ? GDK_MOD2_MASK : 0; state |= (modifiers & MOZ_DA_LOCK_MASK) ? GDK_LOCK_MASK : 0; return (GdkModifierType)state; } static void init_gdk_key_event (GdkEventKey *gdk_key, guint key, MozDrawingAreaModifier modifiers) { GdkKeymapKey *keys; gint n_keys; gdk_key->keyval = DOMKeyCodeToGdkKeyCode (key); if (gdk_keymap_get_entries_for_keyval (NULL, gdk_key->keyval, &keys, &n_keys)) { gdk_key->hardware_keycode = keys[0].keycode; g_free (keys); } gdk_key->state = MozModiferToGdkModifer (modifiers); } #endif static void key_press_cb (MozDrawingArea *area, guint key, gunichar unicode_value, MozDrawingAreaModifier modifiers) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); #ifdef MOZ_ENABLE_GTK2_PLUGINS GdkEventKey gdk_key; init_gdk_key_event (&gdk_key, key, modifiers); #endif //g_debug ("Key-press (%p, %d)\n", data, key); if (data) { nsEventStatus status; nsWindow *window = static_cast(data); nsCOMPtr kungFuDeathGrip = window; PRBool isKeyDownCancelled = PR_FALSE; if (!window->IsKeyDown(key)) { window->SetKeyDownFlag(key); nsKeyEvent event(PR_TRUE, NS_KEY_DOWN, window); init_key_event (&event, modifiers); event.keyCode = key; event.charCode = unicode_value; #ifdef MOZ_ENABLE_GTK2_PLUGINS event.pluginEvent = (void *)&gdk_key; #endif //g_debug ("Sending key-down"); window->DispatchEvent (&event, status); if (NS_UNLIKELY(!g_object_get_data (G_OBJECT (area), "nsWindow"))) return; isKeyDownCancelled = (status == nsEventStatus_eConsumeNoDefault); } // Don't pass modifiers as NS_KEY_PRESS events. if ((key == NS_VK_SHIFT) || (key == NS_VK_CONTROL) || (key == NS_VK_ALT)) return; nsKeyEvent event(PR_TRUE, NS_KEY_PRESS, window); init_key_event (&event, modifiers); event.keyCode = key; event.charCode = unicode_value; #ifdef MOZ_ENABLE_GTK2_PLUGINS event.pluginEvent = (void *)&gdk_key; #endif if (isKeyDownCancelled) event.flags |= NS_EVENT_FLAG_NO_DEFAULT; //g_debug ("Sending key-press"); window->DispatchEvent (&event, status); } } static void key_release_cb (MozDrawingArea *area, gint key, MozDrawingAreaModifier modifiers) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); #ifdef MOZ_ENABLE_GTK2_PLUGINS GdkEventKey gdk_key; init_gdk_key_event (&gdk_key, key, modifiers); #endif //g_debug ("Key-release (%p, %d)\n", data, key); if (data) { nsWindow *window = static_cast(data); nsCOMPtr kungFuDeathGrip = window; nsKeyEvent event(PR_TRUE, NS_KEY_UP, window); init_key_event (&event, modifiers); event.keyCode = key; #ifdef MOZ_ENABLE_GTK2_PLUGINS event.pluginEvent = (void *)&gdk_key; #endif window->ClearKeyDownFlag(key); nsEventStatus status; //g_debug ("Sending key-release"); window->DispatchEvent (&event, status); } } #ifdef SUPPORT_IM static void im_commit_cb (MozDrawingArea *area, gchar *commit_str) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); if (data) { nsWindow *window = static_cast(data); gunichar2 *uniStr = nsnull; glong uniStrLen = 0; uniStr = g_utf8_to_utf16(commit_str, -1, NULL, &uniStrLen, NULL); if(!uniStr) { LOGIM(("utf8toutf16 string transfer failed!\n")); return; } if(uniStrLen) { window->IMEComposeText((const PRUnichar *)uniStr, (PRInt32)uniStrLen, nsnull, 0); window->IMEComposeEnd(); } g_free(uniStr); } } static void im_preedit_changed_cb (MozDrawingArea *area, gchar *preedit_str, gint cursor_pos) { gpointer data = g_object_get_data (G_OBJECT (area), "nsWindow"); if (data) { nsWindow *window = static_cast(data); LOGIM(("preedit string is: %s length is: %d\n", preedit_str, strlen(preedit_str))); if (!preedit_str || !*preedit_str) { LOGIM(("preedit ended\n")); window->IMEComposeText(NULL, 0, NULL, 0); window->IMEComposeEnd(); return; } LOGIM(("preedit len %d\n", strlen(preedit_str))); gunichar2 * uniStr; glong uniStrLen; uniStr = NULL; uniStrLen = 0; uniStr = g_utf8_to_utf16(preedit_str, -1, NULL, &uniStrLen, NULL); if (!uniStr) { LOG(("utf8-utf16 string tranfer failed!\n")); return; } if (uniStrLen) { window->IMEComposeText(static_cast(uniStr), uniStrLen, preedit_str, cursor_pos); } g_free(uniStr); } } #endif NS_IMETHODIMP nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsIDeviceContext *aContext, nsIAppShell *aAppShell, nsIToolkit *aToolkit, nsWidgetInitData *aInitData) { MozDrawingArea *old_drawingarea = mDrawingarea; gboolean isPopup = (aInitData && (aInitData->mWindowType == eWindowType_popup)) ? TRUE : FALSE; if (aParent) { //printf ("Child window initialised (%p->%p) ", (void *)aParent, (void *)this); mDrawingarea = moz_drawing_area_new ((MozDrawingArea *) aParent->GetNativeData (NS_NATIVE_WINDOW), isPopup); } else if (aNativeParent) { //printf ("Native child window initialised (%p->%p) ", aNativeParent, (void *)this); mDrawingarea = moz_drawing_area_new ((MozDrawingArea *)aNativeParent, isPopup); aParent = (nsIWidget *)g_object_get_data (G_OBJECT (aNativeParent), "nsWindow"); } else { //printf ("Window initialised (%p) ", (void *)this); mDrawingarea = moz_drawing_area_new (NULL, isPopup); } //printf ("(+%d+%d, %dx%d)\n", aRect.x, aRect.y, aRect.width, aRect.height); // initialize all the common bits of this class BaseCreate(aParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData); mParent = aParent; if (old_drawingarea) { g_object_set_data (G_OBJECT (old_drawingarea), "nsWindow", NULL); for (unsigned int i = 0; i < G_N_ELEMENTS (mExposeSignals); i++) g_signal_handler_disconnect (old_drawingarea, mExposeSignals[i]); //g_signal_handler_disconnect (old_drawingarea, mUpdateDebug); for (unsigned int i = 0; i < G_N_ELEMENTS (mBoundsSignals); i++) g_signal_handler_disconnect (old_drawingarea, mBoundsSignals[i]); for (unsigned int i = 0; i < G_N_ELEMENTS (mEventSignals); i++) g_signal_handler_disconnect (old_drawingarea, mEventSignals[i]); g_object_unref (old_drawingarea); } // We don't want to be notified of this boundary change, so do it before // connecting up signals. moz_drawing_area_set_bounds (mDrawingarea, aRect.x, aRect.y, aRect.width, aRect.height); mExposeSignals[0] = g_signal_connect (mDrawingarea, "pre-expose", G_CALLBACK (pre_expose_cb), NULL); mExposeSignals[1] = g_signal_connect (mDrawingarea, "expose", G_CALLBACK (expose_cb), NULL); mSurfaceSignal = g_signal_connect (mDrawingarea, "notify::surface", G_CALLBACK (surface_notify), NULL); /* mUpdateDebug = g_signal_connect (mDrawingarea, "updated", G_CALLBACK (update_debug_cb), NULL);*/ mBoundsSignals[0] = g_signal_connect (mDrawingarea, "notify::x", G_CALLBACK (move_notify), NULL); mBoundsSignals[1] = g_signal_connect (mDrawingarea, "notify::y", G_CALLBACK (move_notify), NULL); mBoundsSignals[2] = g_signal_connect (mDrawingarea, "notify::width", G_CALLBACK (size_notify), NULL); mBoundsSignals[3] = g_signal_connect (mDrawingarea, "notify::height", G_CALLBACK (size_notify), NULL); mEventSignals[0] = g_signal_connect (mDrawingarea, "motion", G_CALLBACK (motion_cb), NULL); mEventSignals[1] = g_signal_connect (mDrawingarea, "button-press", G_CALLBACK (button_press_cb), NULL); mEventSignals[2] = g_signal_connect (mDrawingarea, "button-release", G_CALLBACK (button_release_cb), NULL); mEventSignals[3] = g_signal_connect (mDrawingarea, "key-press", G_CALLBACK (key_press_cb), NULL); mEventSignals[4] = g_signal_connect (mDrawingarea, "key-release", G_CALLBACK (key_release_cb), NULL); #ifdef SUPPORT_IM mEventSignals[5] = g_signal_connect (mDrawingarea, "ime-commit", G_CALLBACK (im_commit_cb), NULL); mEventSignals[6] = g_signal_connect (mDrawingarea, "ime-preedit-changed", G_CALLBACK (im_preedit_changed_cb), NULL); #endif g_object_set_data (G_OBJECT (mDrawingarea), "nsWindow", this); nsresult res = CallCreateInstance (kRegionCID, &mInvalidRegion); if (NS_SUCCEEDED(res)) mInvalidRegion->Init(); NS_ASSERTION(mInvalidRegion != NULL, "NULL region :("); res = CallCreateInstance (kRegionCID, &mScrollRegion); NS_ASSERTION(mScrollRegion != NULL, "NULL region :("); res = CallCreateInstance (kRegionCID, &mScratchRegion); NS_ASSERTION(mScratchRegion != NULL, "NULL region :("); // Is this event necessary? GTK/Qt don't seem to bother... nsGUIEvent event(PR_TRUE, NS_CREATE, this); nsEventStatus status; DispatchEvent(&event, status); PRInt32 z; res = GetZIndex (&z); if (NS_SUCCEEDED (res)) moz_drawing_area_set_z (mDrawingarea, z); if (mIsShown) Show (PR_TRUE); return NS_OK; } nsIWidget * nsWindow::GetParent(void) { return mParent; } #ifdef MOZ_ENABLE_GTK2_PLUGINS static gboolean destroy_gtk_widget (GtkWidget *widget) { gtk_widget_destroy (widget); return FALSE; } #endif NS_IMETHODIMP nsWindow::Destroy(void) { if (mIsDestroyed || !mDrawingarea) return NS_OK; LOG(("nsWindow::Destroy [%p]\n", (void *)this)); //printf ("nsWindow::Destroy (%p)\n", (void *)this); mIsDestroyed = PR_TRUE; // Detach from our drawing area g_object_set_data (G_OBJECT (mDrawingarea), "nsWindow", NULL); for (unsigned int i = 0; i < G_N_ELEMENTS(mExposeSignals); i++) g_signal_handler_disconnect (mDrawingarea, mExposeSignals[i]); g_signal_handler_disconnect (mDrawingarea, mSurfaceSignal); //g_signal_handler_disconnect (mDrawingarea, mUpdateDebug); for (unsigned int i = 0; i < G_N_ELEMENTS(mBoundsSignals); i++) g_signal_handler_disconnect (mDrawingarea, mBoundsSignals[i]); for (unsigned int i = 0; i < G_N_ELEMENTS(mEventSignals); i++) g_signal_handler_disconnect (mDrawingarea, mEventSignals[i]); Show(PR_FALSE); g_object_unref (mDrawingarea); mDrawingarea = nsnull; // walk the list of children and call destroy on them. Have to be // careful, though -- calling destroy on a kid may actually remove // it from our child list, losing its sibling links. for (nsIWidget* kid = mFirstChild; kid; ) { nsIWidget* next = kid->GetNextSibling(); kid->Destroy(); kid = next; } // Destroy thebes surface now. NS_IF_RELEASE(mInvalidRegion); NS_IF_RELEASE(mScrollRegion); NS_IF_RELEASE(mScratchRegion); mThebesSurface = nsnull; nsCOMPtr kungFuDeathGrip = this; nsGUIEvent event(PR_TRUE, NS_DESTROY, this); nsEventStatus status; DispatchEvent(&event, status); // Unlink the child from the toplevel window to release the window tree. // This is required because we re-use the toplevel window, which can keep // a hold of this reference. if (mParent && !mParent->GetParent()) mParent->RemoveChild(this); mParent = nsnull; OnDestroy(); #ifdef MOZ_ENABLE_GTK2_PLUGINS // make sure that we remove ourself as the plugin focus window if (gPluginFocusWindow == this) { gPluginFocusWindow->LoseNonXEmbedPluginFocus(); } if (mPluginPort) { //XXX: For some reason calling LoseNonXEmbedPluginFocus above //isn't handling this... gdk_window_remove_filter (gtk_widget_get_window(this->mPluginPort), PluginWindowXEventFilterFunc, this); g_signal_handlers_disconnect_by_func (this->mPluginPort, (void *)bypass_plugin_port_deleted, this); gtk_widget_hide(mPluginPort); //flash seems to get upset if we immediately destroy the plugin //port due to some events that later get dispatched with //references to the port widget. g_idle_add((GSourceFunc)destroy_gtk_widget, mPluginPort); this->mPluginPort = nsnull; } #endif return NS_OK; } NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { LOG(("nsWindow::SetParent\n")); //printf ("nsWindow::SetParent (%p)\n", (void *)this); nsCOMPtr kungFuDeathGrip = this; if (mParent) mParent->RemoveChild (this); mParent = aNewParent; if (mDrawingarea) moz_drawing_area_set_parent (mDrawingarea, aNewParent ? (MozDrawingArea *) aNewParent->GetNativeData (NS_NATIVE_WINDOW) : NULL); return NS_OK; } #ifdef MOZ_ENABLE_GTK2_PLUGINS void nsWindow::UpdatePluginWindowBounds() { if (mPluginPort) { nsRect bounds; GetAbsBounds (bounds); if ((bounds.x == mPluginPortBounds.x) && (bounds.y == mPluginPortBounds.y) && (bounds.width == mPluginPortBounds.width) && (bounds.height == mPluginPortBounds.height)) return; mPluginPortBounds = bounds; // Let the frontend handle the position and size via socket window LOG(("UpdatePluginWindowBounds %p, %p\n", mPluginPort, gtk_plug_get_id(GTK_PLUG(mPluginPort)))); moz_drawing_area_update_plugin_bounds (mDrawingarea, gtk_plug_get_id (GTK_PLUG (mPluginPort)), mPluginPortBounds.x, mPluginPortBounds.y, mPluginPortBounds.width, mPluginPortBounds.height); } } void nsWindow::ShowPluginWindows(MozDrawingArea *aDrawingArea, PRBool aState) { const GList *c; nsWindow *window; GtkWidget *pluginPort; if (aDrawingArea != NULL) { for (c = moz_drawing_area_get_children (aDrawingArea); c; c = c->next) { MozDrawingArea *child = (MozDrawingArea *)c->data; if (moz_drawing_area_get_is_visible(child) == aState) ShowPluginWindows(child, aState); } window = (nsWindow *) g_object_get_data (G_OBJECT (aDrawingArea), "nsWindow"); if (window && (pluginPort = window->GetPluginPort())) { nsRect bounds; guint plug_id; window->GetAbsBounds(bounds); plug_id = gtk_plug_get_id(GTK_PLUG(pluginPort)); if (aState && mIsShown) { // Using gdk_window_show/hide to update the plug window // will cause to invalidate draw region of socket window. // Notify front-end to show/hide the socket window instead. LOG(("ShowPluginWindows %p %p\n", pluginPort, plug_id)); moz_drawing_area_update_plugin_visibility(mDrawingarea, plug_id, TRUE); } else { LOG(("HidePluginWindows %p %p\n", pluginPort, plug_id)); moz_drawing_area_update_plugin_visibility(mDrawingarea, plug_id, FALSE); } } } } #endif NS_IMETHODIMP nsWindow::Show(PRBool aState) { LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState)); //printf ("nsWindow::Show (%p, %d)\n", (void *)this, aState); mIsShown = aState; if (mDrawingarea) { moz_drawing_area_set_visible (mDrawingarea, mIsShown); #ifdef MOZ_ENABLE_GTK2_PLUGINS ShowPluginWindows(mDrawingarea, mIsShown); #endif if (mIsShown) { Invalidate (PR_FALSE); } else if (moz_drawing_area_get_parent (mDrawingarea)) { // When hiding, add our boundaries to the invalid region of our top-level // parent, and possibly to the siblings of the top-level parent if // we're inside a drawing-area that houses other windows. const GList *c; nsWindow *window; gint x, y, width, height; MozDrawingArea *toplevel_area; moz_drawing_area_get_abs_bounds (mDrawingarea, &x, &y, &width, &height); toplevel_area = moz_drawing_area_get_toplevel (mDrawingarea); window = (nsWindow *)g_object_get_data (G_OBJECT (toplevel_area), "nsWindow"); if (window) { window->Invalidate (nsIntRect (x, y, width, height), PR_FALSE); } else { for (c = moz_drawing_area_get_children (toplevel_area); c; c = c->next) { gint offset_x, offset_y; MozDrawingArea *child = (MozDrawingArea *)c->data; window = (nsWindow *) g_object_get_data (G_OBJECT (child), "nsWindow"); if (!window) continue; moz_drawing_area_get_bounds (child, &offset_x, &offset_y, NULL, NULL); window->Invalidate (nsIntRect (x - offset_x, y - offset_y, width, height), PR_FALSE); } } } } return NS_OK; } NS_IMETHODIMP nsWindow::SetModal(PRBool aModal) { LOG(("nsWindow::SetModal [%p] %d, widget[%p]\n", (void *)this, aModal, mDrawingarea)); // printf ("nsWindow::SetModal (%p, %d)\n", (void *)this, aModal); mIsModal = aModal; return NS_OK; } NS_IMETHODIMP nsWindow::IsVisible(PRBool & aState) { // printf ("nsWindow::IsVisible (%p)\n", (void *)this); aState = mDrawingarea ? (moz_drawing_area_get_visible (mDrawingarea) ? PR_TRUE : PR_FALSE) : PR_FALSE; return NS_OK; } NS_IMETHODIMP nsWindow::ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::Move(PRInt32 aX, PRInt32 aY) { LOG(("nsWindow::Move [%p] %d %d\n", (void *)this, aX, aY)); //printf ("nsWindow::Move (%p)\n", (void *)this); if (!mDrawingarea) return NS_OK; Invalidate (PR_FALSE); moz_drawing_area_set_position (mDrawingarea, aX, aY); Invalidate (PR_FALSE); return NS_OK; } NS_IMETHODIMP nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint) { if (!mDrawingarea) return NS_OK; moz_drawing_area_set_size (mDrawingarea, aWidth, aHeight); if (aRepaint) Invalidate (PR_FALSE); return NS_OK; } NS_IMETHODIMP nsWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint) { //printf ("nsWindow::Resize(%d,%d, %dx%d, %d) (%p)\n", aX, aY, aWidth, aHeight, aRepaint, (void *)this); if (!mDrawingarea) return NS_OK; moz_drawing_area_set_bounds (mDrawingarea, aX, aY, aWidth, aHeight); if (aRepaint) Invalidate (PR_FALSE); return NS_OK; } NS_IMETHODIMP nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget *aWidget, PRBool aActivate) { // printf ("nsWindow::PlaceBehind (%p, %p, %d)\n", (void *)this, (void *)aWidget, aActivate); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::SetZIndex(PRInt32 aZIndex) { //printf ("nsWindow::SetZIndex (%p, %d)\n", (void *)this, aZIndex); nsBaseWidget::SetZIndex(aZIndex); if (mDrawingarea) moz_drawing_area_set_z (mDrawingarea, aZIndex); Invalidate(PR_FALSE); return NS_OK; } NS_IMETHODIMP nsWindow::SetSizeMode(PRInt32 aMode) { LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode)); // printf ("nsWindow::SetSizeMode(%p, %d)\n", (void *)this, aMode); return nsBaseWidget::SetSizeMode(aMode); } NS_IMETHODIMP nsWindow::Enable(PRBool aState) { // printf ("nsWindow::Enable (%p, %d)\n", (void *)this, aState); mEnabled = aState; return NS_OK; } NS_IMETHODIMP nsWindow::IsEnabled(PRBool *aState) { // printf ("nsWindow::IsEnabled (%p)\n", (void *)this); *aState = mEnabled; return NS_OK; } NS_IMETHODIMP nsWindow::SetFocus(PRBool aRaise) { // printf ("nsWindow::SetFocus (%p, %d)\n", (void *)this, aRaise); // FIXME: Handle aRaise? if (mDrawingarea) moz_drawing_area_set_focus (mDrawingarea); return NS_OK; } NS_IMETHODIMP nsWindow::SetForegroundColor(const nscolor &aColor) { // printf ("nsWindow::SetForegroundColor (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::SetBackgroundColor(const nscolor &aColor) { // printf ("nsWindow::SetBackgroundColor (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } /* static */ MozHeadlessCursorType get_moz_cursor(nsCursor aCursor) { switch (aCursor) { case eCursor_standard: return MOZ_HEADLESS_CURSOR_TYPE_LEFT_PTR; case eCursor_wait: return MOZ_HEADLESS_CURSOR_TYPE_WATCH; case eCursor_select: return MOZ_HEADLESS_CURSOR_TYPE_XTERM; case eCursor_hyperlink: return MOZ_HEADLESS_CURSOR_TYPE_HAND2; case eCursor_n_resize: return MOZ_HEADLESS_CURSOR_TYPE_TOP_SIDE; case eCursor_s_resize: return MOZ_HEADLESS_CURSOR_TYPE_BOTTOM_SIDE; case eCursor_w_resize: return MOZ_HEADLESS_CURSOR_TYPE_LEFT_SIDE; case eCursor_e_resize: return MOZ_HEADLESS_CURSOR_TYPE_RIGHT_SIDE; case eCursor_nw_resize: return MOZ_HEADLESS_CURSOR_TYPE_TOP_LEFT_CORNER; case eCursor_se_resize: return MOZ_HEADLESS_CURSOR_TYPE_BOTTOM_RIGHT_CORNER; case eCursor_ne_resize: return MOZ_HEADLESS_CURSOR_TYPE_TOP_RIGHT_CORNER; case eCursor_sw_resize: return MOZ_HEADLESS_CURSOR_TYPE_BOTTOM_LEFT_CORNER; case eCursor_crosshair: return MOZ_HEADLESS_CURSOR_TYPE_CROSSHAIR; case eCursor_move: return MOZ_HEADLESS_CURSOR_TYPE_FLEUR; case eCursor_help: return MOZ_HEADLESS_CURSOR_TYPE_QUESTION_ARROW; case eCursor_copy: // CSS3 return MOZ_HEADLESS_CURSOR_TYPE_COPY; case eCursor_alias: return MOZ_HEADLESS_CURSOR_TYPE_ALIAS; case eCursor_context_menu: return MOZ_HEADLESS_CURSOR_TYPE_CONTEXT_MENU; case eCursor_cell: return MOZ_HEADLESS_CURSOR_TYPE_PLUS; case eCursor_grab: return MOZ_HEADLESS_CURSOR_TYPE_HAND_GRAB; case eCursor_grabbing: return MOZ_HEADLESS_CURSOR_TYPE_HAND_GRABBING; case eCursor_spinning: return MOZ_HEADLESS_CURSOR_TYPE_SPINNING; case eCursor_zoom_in: return MOZ_HEADLESS_CURSOR_TYPE_ZOOM_IN; case eCursor_zoom_out: return MOZ_HEADLESS_CURSOR_TYPE_ZOOM_OUT; case eCursor_not_allowed: case eCursor_no_drop: return MOZ_HEADLESS_CURSOR_TYPE_NOT_ALLOWED; case eCursor_col_resize: return MOZ_HEADLESS_CURSOR_TYPE_COL_RESIZE; case eCursor_row_resize: return MOZ_HEADLESS_CURSOR_TYPE_ROW_RESIZE; case eCursor_vertical_text: return MOZ_HEADLESS_CURSOR_TYPE_VERTICAL_TEXT; case eCursor_all_scroll: return MOZ_HEADLESS_CURSOR_TYPE_FLEUR; case eCursor_nesw_resize: return MOZ_HEADLESS_CURSOR_TYPE_NESW_RESIZE; case eCursor_nwse_resize: return MOZ_HEADLESS_CURSOR_TYPE_NWSE_RESIZE; case eCursor_ns_resize: return MOZ_HEADLESS_CURSOR_TYPE_SB_V_DOUBLE_ARROW; case eCursor_ew_resize: return MOZ_HEADLESS_CURSOR_TYPE_SB_H_DOUBLE_ARROW; case eCursor_none: return MOZ_HEADLESS_CURSOR_TYPE_NONE; default: NS_ASSERTION(aCursor, "Invalid cursor type"); return MOZ_HEADLESS_CURSOR_TYPE_LEFT_PTR; } } NS_IMETHODIMP nsWindow::SetCursor(nsCursor aCursor) { moz_drawing_area_set_cursor (mDrawingarea, get_moz_cursor (aCursor)); return NS_OK; } NS_IMETHODIMP nsWindow::HideWindowChrome(PRBool aShouldHide) { // printf ("nsWindow::HideWindowChrome (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::MakeFullScreen(PRBool aFullScreen) { // printf ("nsWindow::MakeFullScreen (%p, %d)\n", (void *)this, aFullScreen); return nsBaseWidget::MakeFullScreen (aFullScreen); } NS_IMETHODIMP nsWindow::Validate() { // printf ("nsWindow::Validate (%p)\n", (void *)this); return NS_OK; } void nsWindow::ScheduleExpose(PRBool aSynchronous) { if (mDrawingarea) moz_drawing_area_redraw (mDrawingarea, aSynchronous); } static void print_region (nsIRegion *aRegion) { /* nsRegionRectSet *rects = nsnull; aRegion->GetRects(&rects); for (unsigned int i = 0; i < rects->mNumRects; i++) { printf ("\t+%d+%d, %dx%d\n", rects->mRects[i].x, rects->mRects[i].y, rects->mRects[i].width, rects->mRects[i].height); } aRegion->FreeRects(rects);*/ PRInt32 ix, iy, iwidth, iheight; aRegion->GetBoundingBox (&ix, &iy, &iwidth, &iheight); printf ("\tBounds %d,%d, %dx%d\n", ix, iy, iwidth, iheight); } NS_IMETHODIMP nsWindow::Invalidate(PRBool aIsSynchronous) { //printf ("nsWindow::Invalidate (%p, %d)\n", (void *)this, aIsSynchronous); if (mDrawingarea) { gint width, height; moz_drawing_area_get_bounds (mDrawingarea, NULL, NULL, &width, &height); mInvalidRegion->SetTo(0, 0, width, height); //print_region (mInvalidRegion); ScheduleExpose (aIsSynchronous); } return NS_OK; } NS_IMETHODIMP nsWindow::Invalidate(const nsIntRect &aRect, PRBool aIsSynchronous) { // We can be called after destruction, make sure we don't try // to access mInvalidRegion if that's the case if (mIsDestroyed) return NS_OK; /*printf ("nsWindow::Invalidate (%p, +%d+%d, %dx%d, %d)\n", (void *)this, aRect.x, aRect.y, aRect.width, aRect.height, aIsSynchronous);*/ mInvalidRegion->Union(aRect.x, aRect.y, aRect.width, aRect.height); //print_region (mInvalidRegion); ScheduleExpose (aIsSynchronous); return NS_OK; } void nsWindow::InvalidateRegion(nsIRegion *aRegion, PRBool aIsSynchronous) { if (mIsDestroyed) return; mInvalidRegion->Union(*aRegion); //printf ("nsWindow::InvalidateRegion (%p, %d)\n", (void *)this, aIsSynchronous); //print_region (aRegion); ScheduleExpose(aIsSynchronous); } NS_IMETHODIMP nsWindow::Update() { //printf ("nsWindow::Update (%p)\n", (void *)this); ScheduleExpose (PR_TRUE); return NS_OK; } nsresult nsWindow::ConfigureChildren(const nsTArray& aConfigurations) { for (PRUint32 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"); // FIXME: Add window clip regions //nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion); //NS_ENSURE_SUCCESS(rv, rv); if (w->mBounds.Size() != configuration.mBounds.Size()) { w->Resize(configuration.mBounds.x, configuration.mBounds.y, configuration.mBounds.width, configuration.mBounds.height, PR_TRUE); } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { w->Move(configuration.mBounds.x, configuration.mBounds.y); } } return NS_OK; } void nsWindow::Scroll(const nsIntPoint& aDelta, const nsTArray& aDestRects, const nsTArray& aConfigurations) { if (!mDrawingarea) { NS_ERROR("Cannot scroll widget"); return; } mScrollRegion->Init(); // Scroll the surface given aDestRects // We create a cairo_rectangle_list_t to pass into MozDrawingArea // to clip to when it scrolls. cairo_rectangle_list_t rect_list = { CAIRO_STATUS_SUCCESS, NULL, aDestRects.Length() }; rect_list.rectangles = (cairo_rectangle_t *) g_slice_alloc (sizeof (cairo_rectangle_t) * rect_list.num_rectangles); for (PRUint32 i = 0; i < rect_list.num_rectangles; i++) { const nsIntRect& r = aDestRects[i]; rect_list.rectangles[i].x = r.x; rect_list.rectangles[i].y = r.y; rect_list.rectangles[i].width = r.width; rect_list.rectangles[i].height = r.height; // Union the rectangle with a scroll region that we can intersect // with the bounds of the widget to find the exposed area to // invalidate. mScrollRegion->Union(r.x, r.y, r.width, r.height); } // Offset the destination region by the scroll amount // (to get where it's scrolling from), then subtract the bounds of // the window to find out how much of it lies outside of those // bounds - then offset again and intersect it with the bounds // to find out the visible invalidated area. // // The intersection may actually just do nothing as drawing is // restricted to the bounds anyway, but it doesn't hurt to be correct. nsIntRect bounds; GetBounds(bounds); mScrollRegion->Offset(-aDelta.x, -aDelta.y); // We want to move the intersecting area of the invalid region here first mScratchRegion->SetTo(*mInvalidRegion); mScratchRegion->Intersect(*mScrollRegion); mInvalidRegion->Subtract(*mScratchRegion); mScratchRegion->Offset(aDelta.x, aDelta.y); mInvalidRegion->Union(*mScratchRegion); // Then carry on finding out the newly invalidated area mScrollRegion->Subtract(bounds.x, bounds.y, bounds.width, bounds.height); mScrollRegion->Offset(aDelta.x, aDelta.y); mScrollRegion->Intersect(bounds.x, bounds.y, bounds.width, bounds.height); // Scroll the surface moz_drawing_area_scroll (mDrawingarea, &rect_list, aDelta.x, aDelta.y); g_slice_free1 (sizeof (cairo_rectangle_t) * rect_list.num_rectangles, rect_list.rectangles); // Scroll the intersecting invalidated region too // Invalidate any newly-exposed area InvalidateRegion (mScrollRegion, PR_FALSE); // FIXME: configuring children will cause a load of invalidations // that we could possibly optimise out. ConfigureChildren(aConfigurations); } NS_IMETHODIMP nsWindow::SetBorderStyle(nsBorderStyle aBorderStyle) { // printf ("nsWindow::SetBorderStyle (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::SetTitle(const nsAString& aTitle) { NS_ConvertUTF16toUTF8 titleUTF8(aTitle); // printf ("nsWindow::SetTitle (%p, %s)\n", (void *)this, (const char *)titleUTF8.get()); return NS_OK; } NS_IMETHODIMP nsWindow::SetIcon(const nsAString& aIconSpec) { // printf ("nsWindow::SetIcon (%p)\n", (void *)this); return NS_OK; } NS_IMETHODIMP nsWindow::SetMenuBar(void * aMenuBar) { // printf ("nsWindow::SetMenuBar (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::ShowMenuBar(PRBool aShow) { // printf ("nsWindow::ShowMenuBar (%p, %d)\n", (void *)this, aShow); return NS_ERROR_NOT_IMPLEMENTED; } nsIntPoint nsWindow::WidgetToScreenOffset() { gint x, y; if (!mDrawingarea) return nsIntPoint (0, 0); moz_drawing_area_get_abs_bounds (mDrawingarea, &x, &y, NULL, NULL); return nsIntPoint (x, y); } NS_IMETHODIMP nsWindow::BeginResizingChildren(void) { // printf ("nsWindow::BeginResizingChildren (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::EndResizingChildren(void) { // printf ("nsWindow::EndResizingChildren (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::GetPreferredSize(PRInt32 &aWidth, PRInt32 &aHeight) { // printf ("nsWindow::GetPreferredSize (%p)\n", (void *)this); aWidth = mPreferredWidth; aHeight = mPreferredHeight; return (mPreferredWidth != 0 && mPreferredHeight != 0) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight) { // printf ("nsWindow::SetPreferredSize (%p, %d, %d)\n", (void *)this, aWidth, aHeight); mPreferredWidth = aWidth; mPreferredHeight = aHeight; return NS_OK; } PRBool nsWindow::DispatchCommandEvent(nsIAtom* aCommand) { nsEventStatus status; nsCommandEvent event(PR_TRUE, nsWidgetAtoms::onAppCommand, aCommand, this); DispatchEvent(&event, status); return TRUE; } void nsWindow::DispatchResizeEvent(nsIntRect &aRect, nsEventStatus &aStatus) { nsSizeEvent event(PR_TRUE, NS_SIZE, this); event.windowSize = &aRect; event.refPoint.x = aRect.x; event.refPoint.y = aRect.y; event.mWinWidth = aRect.width; event.mWinHeight = aRect.height; nsEventStatus status; DispatchEvent(&event, status); } NS_IMETHODIMP nsWindow::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus &aStatus) { #ifdef DEBUG debug_DumpEvent(stdout, aEvent->widget, aEvent, nsCAutoString("something"), 0); #endif aStatus = nsEventStatus_eIgnore; // send it to the standard callback if (mEventCallback) aStatus = (* mEventCallback)(aEvent); return NS_OK; } void nsWindow::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY) { // printf ("nsWindow::ConvertToDeviceCoordinates (%p)\n", (void *)this); } NS_IMETHODIMP nsWindow::CaptureMouse(PRBool aCapture) { LOG(("CaptureMouse %p\n", (void *)this)); // printf ("nsWindow::CaptureMouse (%p, %d)\n", (void *)this, aCapture); if (aCapture) { } else { } return NS_OK; } NS_IMETHODIMP nsWindow::SetWindowClass(const nsAString &xulWinType) { // printf ("nsWindow::SetWindowClass (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::GetBounds(nsIntRect &aRect) { if (mDrawingarea) { gint x, y, width, height; moz_drawing_area_get_bounds (mDrawingarea, &x, &y, &width, &height); aRect.x = x; aRect.y = y; aRect.width = width; aRect.height = height; } else { aRect.x = 0; aRect.y = 0; aRect.width = 0; aRect.height = 0; } return NS_OK; } NS_IMETHODIMP nsWindow::SetBounds(const nsIntRect &aRect) { if (mDrawingarea) moz_drawing_area_set_bounds (mDrawingarea, aRect.x, aRect.y, aRect.width, aRect.height); return NS_OK; } NS_IMETHODIMP nsWindow::CaptureRollupEvents(nsIRollupListener *aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent) { LOG(("CaptureRollupEvents %p\n", (void *)this)); // printf ("nsWindow::CaptureRollupEvents (%p, %d, %d)\n", (void *)this, aDoCapture, aConsumeRollupEvent); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::GetAttention(PRInt32 aCycleCount) { LOG(("nsWindow::GetAttention [%p]\n", (void *)this)); // printf ("nsWindow::GetAttention (%p, %d)\n", (void *)this, aCycleCount); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical) { // printf ("nsWindow::BeginResizeDrag (%p)\n", (void *)this); return NS_ERROR_NOT_IMPLEMENTED; } // return the gfxASurface for rendering to this widget gfxASurface* nsWindow::GetThebesSurface() { // printf ("nsWindow::GetThebesSurface (%p)\n", (void *)this); if (!mDrawingarea) return nsnull; gint x, y; cairo_surface_t *surface = moz_drawing_area_create_cairo (mDrawingarea, &x, &y); mThebesSurface = gfxASurface::Wrap (surface); if (mThebesSurface && mThebesSurface->CairoStatus() != 0) { mThebesSurface = nsnull; } else { mThebesSurface->SetDeviceOffset (gfxPoint(x,y)); } return mThebesSurface; } void nsWindow::PropagateInvalidRegionDown(nsIRegion *aRegion, PRBool first_call) { if (!first_call) { mInvalidRegion->Union (*aRegion); return; } for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) { nsWindow *kid_window = static_cast(kid); MozDrawingArea *area = (MozDrawingArea *)kid_window->GetNativeData (NS_NATIVE_WINDOW); if (!area) continue; gint x, y; moz_drawing_area_get_bounds (area, &x, &y, NULL, NULL); mInvalidRegion->Offset (-x, -y); kid_window->PropagateInvalidRegionDown(mInvalidRegion, PR_FALSE); mInvalidRegion->Offset (x, y); } } void nsWindow::PropagateInvalidRegionUp(nsIRegion *aRegion, PRBool aFirstCall) { if (!aFirstCall && aRegion) mInvalidRegion->Union (*aRegion); // If we're a pop-up window, we don't need to send invalidations upwards. // We get drawn on top anyway. if (moz_drawing_area_get_popup (mDrawingarea)) return; gint x, y; moz_drawing_area_get_bounds (mDrawingarea, &x, &y, NULL, NULL); MozDrawingArea *parent_area; nsWindow *parent_window = static_cast(GetParent ()); if (parent_window) { // Propagate invalid area to parent mInvalidRegion->Offset (x, y); parent_window->PropagateInvalidRegionUp(mInvalidRegion, PR_FALSE); mInvalidRegion->Offset (-x, -y); } else if ((parent_area = moz_drawing_area_get_parent (mDrawingarea))) { PRInt32 rx, ry, rw, rh; mInvalidRegion->GetBoundingBox (&rx, &ry, &rw, &rh); rx += x; ry += y; // Propagate across (these will get propagated back down during expose) const GList *children, *c; children = moz_drawing_area_get_children (parent_area); for (c = children; c; c = c->next) { MozDrawingArea *child_area = (MozDrawingArea *)c->data; if (child_area == mDrawingarea) continue; nsWindow *child_window = (nsWindow *) g_object_get_data (G_OBJECT (child_area), "nsWindow"); if (child_window) { gint cx, cy; moz_drawing_area_get_bounds (child_area, &cx, &cy, NULL, NULL); child_window->Invalidate (nsIntRect (rx - cx, ry - cy, rw, rh), PR_TRUE); /*printf ("Invalidating across: %d, %d, %dx%d\n", rx - cx, ry - cy, rw, rh);*/ } } } } void nsWindow::PropagateInvalidRegions() { // Propagate invalid regions up (and when we reach the top, across) if (!moz_drawing_area_get_children (mDrawingarea) && moz_drawing_area_get_is_visible (mDrawingarea)) { //printf ("PropagateInvalidRegions (%p)\n", (void *)this); PropagateInvalidRegionUp (mInvalidRegion, PR_TRUE); } } void nsWindow::Redraw() { NS_ASSERTION (mDrawingarea, "NULL drawing area"); #ifdef MOZ_ENABLE_GTK2_PLUGINS // Make sure plugin windows are positioned correctly UpdatePluginWindowBounds (); #endif // Check if there's any invalid area to draw (and intersect with widget size) gint width, height; moz_drawing_area_get_bounds (mDrawingarea, NULL, NULL, &width, &height); mInvalidRegion->Intersect (0, 0, width, height); if (mInvalidRegion->IsEmpty ()) return; // Make sure that our children will redraw any area that we redraw PropagateInvalidRegionDown (nsnull, PR_TRUE); // Debugging //moz_drawing_area_print_tree (mDrawingarea); /*printf ("nsWindow::Redraw (%p)\n", (void *)this); print_region (mInvalidRegion);*/ // Get rendering context nsCOMPtr rc = getter_AddRefs(GetRenderingContext()); if (NS_UNLIKELY(!rc)) return; // Create rendering surface and graphics context nsRefPtr ctx = rc->ThebesContext(); //float app = GetDeviceContext()->AppUnitsPerDevPixel(); // Set damaged area PRInt32 ix, iy, iwidth, iheight; mInvalidRegion->GetBoundingBox (&ix, &iy, &iwidth, &iheight); MozDrawingAreaRect mda_rect = { ix, iy, iwidth, iheight }; moz_drawing_area_set_damage (mDrawingarea, &mda_rect); // Debugging //printf ("Invalid rect: +%d+%d, %dx%d\n", ix, iy, iwidth, iheight); // Clip on damaged area rc->SetClipRegion(*mInvalidRegion, nsClipCombine_kReplace); // Debugging /*rc->SetColor (NS_RGBA(255,0,0,128)); rc->FillRect (0, 0, width * app, height * app);*/ // Make a copy of the region as dispatching this event can cause // further invalidations and we can't clear it afterwards nsCOMPtr invalidRegion = do_CreateInstance (kRegionCID); invalidRegion->Init (); invalidRegion->SetTo (*mInvalidRegion); mInvalidRegion->Init(); nsPaintEvent event(PR_TRUE, NS_PAINT, this); event.refPoint.x = 0; event.refPoint.y = 0; event.rect = nsnull;//&(nsRect(0, 0, width, height)); event.region = invalidRegion; event.renderingContext = rc; nsEventStatus status; DispatchEvent (&event, status); invalidRegion = nsnull; // Note, we don't need to return here I don't think, but if we start // seeing crashes later when we start supporting X11 surfaces, // have a look at bug #378273. //if (NS_UNLIKELY(mIsDestroyed)) // return; } #ifdef MOZ_ENABLE_GTK2_PLUGINS void nsWindow::SetPluginType(PluginType aPluginType) { mPluginType = aPluginType; } static Window get_toplevel_xwindow (Window xwin) { Window toplevel = 0; Window parent_win; Window root_win; Window *children; unsigned int n_children; Status status; while (1) { status = XQueryTree (GDK_DISPLAY(), xwin, &root_win, &parent_win, &children, &n_children); if (status == 0) break; if (parent_win == root_win) { toplevel = xwin; break; } xwin = parent_win; } XFree (children); return toplevel; } void nsWindow::SetNonXEmbedPluginFocus() { if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) { return; } if (gPluginFocusWindow) { nsRefPtr kungFuDeathGrip = gPluginFocusWindow; gPluginFocusWindow->LoseNonXEmbedPluginFocus(); } LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n")); Window curFocusWindow; int focusState; GdkWindow *gdk_window = gtk_widget_get_window (mPluginPort); XGetInputFocus(GDK_WINDOW_XDISPLAY(gdk_window), &curFocusWindow, &focusState); LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow)); /* FIXME - this isn't going to work as expected with headless since * our toplevel window is probably owned by another client and we * won't have a gdk window as a parent which will confuse * gdk_window_get_toplevel... */ #warning "FIXME gdk_window_get_toplevel probably isn't going work" // leverage xwindow hierarchy to find if plugin's toplevel equals to // focused's toplevel Window plugin_toplevel = get_toplevel_xwindow(GDK_WINDOW_XWINDOW(gdk_window)); Window focus_toplevel = get_toplevel_xwindow(curFocusWindow); // lookup with the focus proxy window is supposed to get the // same GdkWindow as toplevel. If the current focused window // is not the focus proxy, we return without any change. if (plugin_toplevel != focus_toplevel) { return; } // switch the focus from the focus proxy to the plugin window mOldFocusWindow = curFocusWindow; XRaiseWindow(GDK_WINDOW_XDISPLAY(gdk_window), GDK_WINDOW_XWINDOW(gdk_window)); gdk_error_trap_push(); XSetInputFocus(GDK_WINDOW_XDISPLAY(gdk_window), GDK_WINDOW_XWINDOW(gdk_window), RevertToNone, CurrentTime); gdk_flush(); gdk_error_trap_pop(); gPluginFocusWindow = this; gdk_window_add_filter(NULL, PluginWindowXClientMessageFilter, this); LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n", mOldFocusWindow, GDK_WINDOW_XWINDOW(gdk_window))); } void nsWindow::LoseNonXEmbedPluginFocus() { LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n")); // This method is only for the nsWindow which contains a // Non-XEmbed plugin, for example, JAVA plugin. // XEmbed plugin also needs this method to lose focus if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) { return; } Window curFocusWindow; int focusState; GdkWindow *gdk_window = gtk_widget_get_window (mPluginPort); XGetInputFocus(GDK_WINDOW_XDISPLAY(gdk_window), &curFocusWindow, &focusState); // we only switch focus between plugin window and focus proxy. If the // current focused window is not the plugin window, just removing the // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2 // will take care of the focus later. if (!curFocusWindow || curFocusWindow == GDK_WINDOW_XWINDOW(gdk_window)) { gdk_error_trap_push(); XRaiseWindow(GDK_WINDOW_XDISPLAY(gdk_window), mOldFocusWindow); XSetInputFocus(GDK_WINDOW_XDISPLAY(gdk_window), mOldFocusWindow, RevertToParent, CurrentTime); gdk_flush(); gdk_error_trap_pop(); } gPluginFocusWindow = NULL; mOldFocusWindow = 0; gdk_window_remove_filter(NULL, PluginWindowXClientMessageFilter, this); LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n")); } GdkFilterReturn nsWindow::PluginWindowXEventFilterFunc(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { GtkWidget *widget; GdkWindow *plugin_window; gpointer user_data; XEvent *xevent; nsRefPtr nswindow = (nsWindow*)data; GdkFilterReturn return_val; xevent = (XEvent *)gdk_xevent; return_val = GDK_FILTER_CONTINUE; switch (xevent->type) { case CreateNotify: case ReparentNotify: if (xevent->type==CreateNotify) { plugin_window = gdk_window_lookup(xevent->xcreatewindow.window); } else { if (xevent->xreparent.event != xevent->xreparent.parent) break; plugin_window = gdk_window_lookup (xevent->xreparent.window); } if (plugin_window) { user_data = nsnull; gdk_window_get_user_data(plugin_window, &user_data); widget = GTK_WIDGET(user_data); if (GTK_IS_XTBIN(widget)) { nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED); break; } else if(GTK_IS_SOCKET(widget)) { nswindow->SetPluginType(nsWindow::PluginType_XEMBED); break; } } nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED); return_val = GDK_FILTER_REMOVE; break; case EnterNotify: nswindow->SetNonXEmbedPluginFocus(); break; case LeaveNotify: nswindow->LoseNonXEmbedPluginFocus(); break; case DestroyNotify: // Currently we consider all plugins are non-xembed and calls // LoseNonXEmbedPluginFocus without any checking. nswindow->LoseNonXEmbedPluginFocus(); default: break; } return return_val; } GdkFilterReturn nsWindow::PluginWindowXClientMessageFilter(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { XEvent *xevent; xevent = (XEvent *)gdk_xevent; GdkFilterReturn return_val; return_val = GDK_FILTER_CONTINUE; if (!gPluginFocusWindow || xevent->type!=ClientMessage) { return return_val; } // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus // to set the focus to the focus proxy. To prevent this happen // while the focus is on the plugin, we filter the WM_TAKE_FOCUS // out. Display *dpy ; //dpy = GDK_WINDOW_XDISPLAY((GdkWindow*)(gPluginFocusWindow-> // GetNativeData(NS_NATIVE_WINDOW))); dpy = GDK_DISPLAY(); if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS") != xevent->xclient.message_type) { return return_val; } if ((Atom) xevent->xclient.data.l[0] == gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) { // block it from gtk2.0 focus proxy return_val = GDK_FILTER_REMOVE; } return return_val; } static gboolean bypass_plugin_port_deleted (GtkWidget *widget, GdkEvent *event, gpointer user_data) { // Destroying GtkSocket will cause GtkPlug to be destroyed. // Bypass this and let nsWindow::Destroy to handle. return TRUE; } void* nsWindow::SetupPluginPort() { GdkWindow *gdk_window; if (!mPluginPort) { MozDrawingArea *top_area = moz_drawing_area_get_toplevel (mDrawingarea); nsRect bounds; GetAbsBounds (bounds); // new a GtkPlug as toplevel window for windowed plugin // and pass the XID of the GtkPlug to front-end to plug // into a GtkSocket mPluginPort = gtk_plug_new (0); g_signal_connect (G_OBJECT (mPluginPort), "delete-event", G_CALLBACK (bypass_plugin_port_deleted), (gpointer)this); gtk_widget_realize (mPluginPort); GtkAllocation alloc; alloc.x = bounds.x; alloc.y = bounds.y; alloc.width = bounds.width; alloc.height = bounds.height; gtk_widget_size_allocate (mPluginPort, &alloc); gdk_window = gtk_widget_get_window (mPluginPort); gdk_window_set_user_data (gdk_window, mPluginPort); guint plug_id = gtk_plug_get_id (GTK_PLUG (mPluginPort)); LOG(("SetupPluginPort %p, %p %dx%d+%d+%d\n", mPluginPort, GDK_WINDOW_XWINDOW(gdk_window), bounds.width, bounds.height, bounds.x, bounds.y)); gtk_widget_show (mPluginPort); moz_drawing_area_add_plugin (top_area, plug_id, bounds.x, bounds.y, bounds.width, bounds.height); mPluginPortBounds = bounds; } else { gdk_window = gtk_widget_get_window (mPluginPort); } // we have to flush the X queue here so that any plugins that // might be running on separate X connections will be able to use // this window in case it was just created XWindowAttributes xattrs; XGetWindowAttributes(GDK_DISPLAY (), GDK_WINDOW_XWINDOW(gdk_window), &xattrs); XSelectInput (GDK_DISPLAY (), GDK_WINDOW_XWINDOW(gdk_window), xattrs.your_event_mask | SubstructureNotifyMask); gdk_window_add_filter(gdk_window, PluginWindowXEventFilterFunc, this); XSync(GDK_DISPLAY(), False); return (void *)GDK_WINDOW_XWINDOW(gdk_window); } #endif /* MOZ_ENABLE_GTK2_PLUGINS */ void nsWindow::GetAbsBounds(nsRect &aRect) { nsIWidget *widget = this; nsIntRect winBounds; GetBounds(winBounds); aRect.x = winBounds.x; aRect.y = winBounds.y; aRect.width = winBounds.width; aRect.height = winBounds.height; while ((widget = widget->GetParent())) { nsIntRect parentBounds; widget->GetBounds(parentBounds); aRect.x += parentBounds.x; aRect.y += parentBounds.y; } } void* nsWindow::GetNativeData(PRUint32 aDataType) { /* printf ("nsWindow::GetNativeData (%p, %s)\n", (void *)this, (aDataType == NS_NATIVE_WINDOW) ? "NS_NATIVE_WINDOW" : (aDataType == NS_NATIVE_WIDGET) ? "NS_NATIVE_WIDGET" : (aDataType == NS_NATIVE_PLUGIN_PORT) ? "NS_NATIVE_PLUGIN_PORT" : (aDataType == NS_NATIVE_DISPLAY) ? "NS_NATIVE_DISPLAY" : (aDataType == NS_NATIVE_GRAPHIC) ? "NS_NATIVE_GRAPHIC" : (aDataType == NS_NATIVE_SHELLWIDGET) ? "NS_NATIVE_SHELLWIDGET" : "Unknown");*/ switch (aDataType) { case NS_NATIVE_WINDOW: case NS_NATIVE_WIDGET: if (!mDrawingarea) return nsnull; return mDrawingarea; case NS_NATIVE_PLUGIN_PORT: #ifdef MOZ_ENABLE_GTK2_PLUGINS return SetupPluginPort (); #else return nsnull; #endif case NS_NATIVE_DISPLAY: #ifdef MOZ_ENABLE_GTK2_PLUGINS return GDK_DISPLAY(); #else return nsnull; #endif /* MOZ_ENABLE_GTK2_PLUGINS */ case NS_NATIVE_GRAPHIC: //NS_ASSERTION(nsnull != mToolkit, "NULL toolkit, unable to get a GC"); //return (void *)static_cast(mToolkit)->GetSharedGC(); return nsnull; case NS_NATIVE_SHELLWIDGET: return nsnull; default: NS_WARNING("nsWindow::GetNativeData called with bad value"); return nsnull; } } nsTransparencyMode nsWindow::GetTransparencyMode() { if (!mDrawingarea) return eTransparencyOpaque; else return moz_drawing_area_get_transparent (mDrawingarea) ? eTransparencyTransparent : eTransparencyOpaque; } nsChildWindow::nsChildWindow() { //printf ("Child Window created (%p)\n", (void *)this); } nsChildWindow::~nsChildWindow() { //printf ("Child Window deleted (%p)\n", (void *)this); } #ifdef SUPPORT_IM PRBool nsWindow::IMEIsComposing(void) { return mIsComposing; } PRBool nsWindow::GetCaretRect(nsIntRect &aCaretRect) { nsEventStatus status; nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, this); DispatchEvent(&selection, status); if (!selection.mSucceeded) { LOGIM(("SELECTED_TEXT failed\n")); return PR_FALSE; } PRUint32 offset = selection.mReply.mOffset; nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, this); caretRect.InitForQueryCaretRect(offset); DispatchEvent(&caretRect, status); if (!caretRect.mSucceeded) { LOGIM(("CARET_RECT failed\n")); return PR_FALSE; } aCaretRect = caretRect.mReply.mRect; LOGIM(("GetCaretRect, SUCCEEDED, aCaretRect={ x: %ld, y: %ld, width: %ld, height: %ld }\n", aCaretRect.x, aCaretRect.y, aCaretRect.width, aCaretRect.height)); return PR_TRUE; } void nsWindow::IMESetCursorPosition() { nsIntRect rect; if(!GetCaretRect(rect)) { //printf("GetCaretRect faild\n"); return; } //printf("curosor position: %d, %d, %d, %d\n", // rect.x, rect.y, rect.width, rect.height); moz_drawing_area_ime_set_cursor(mDrawingarea, rect.x, rect.y, rect.width, rect.height); } #define START_OFFSET(I) \ (*aTextRangeListResult)[I].mStartOffset #define END_OFFSET(I) \ (*aTextRangeListResult)[I].mEndOffset #define SET_FEEDBACKTYPE(I,T) (*aTextRangeListResult)[I].mRangeType = T /* static */ void IM_set_text_range(const PRInt32 aLen, const gchar *aPreeditString, const gint aCursorPos, PRUint32 *aTextRangeListLengthResult, nsTextRangeArray *aTextRangeListResult) { if (aLen == 0) { aTextRangeListLengthResult = 0; aTextRangeListResult = NULL; return; } PRInt32 aMaxLenOfTextRange; aMaxLenOfTextRange = 2*aLen + 1; *aTextRangeListResult = new nsTextRange[aMaxLenOfTextRange]; NS_ASSERTION(*aTextRangeListResult, "No enough memory."); // Set caret's postion SET_FEEDBACKTYPE(0, NS_TEXTRANGE_CARETPOSITION); START_OFFSET(0) = aCursorPos; END_OFFSET(0) = aCursorPos; int count = 0; gunichar2 * uniStr; glong uniStrLen; PRUint32 feedbackType = 0; // due to avoid pass pango attribute list via IPC // just set attribute as XIMReverse | XIMUnderline here feedbackType = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT; count++; START_OFFSET(count) = 0; END_OFFSET(count) = 0; uniStr = NULL; uniStr = g_utf8_to_utf16(aPreeditString, -1, NULL, &uniStrLen, NULL); if (uniStr) { END_OFFSET(count) = START_OFFSET(count) + uniStrLen; SET_FEEDBACKTYPE(count, feedbackType); g_free(uniStr); } *aTextRangeListLengthResult = count + 1; } void nsWindow::IMEComposeStart(void) { //printf("IMEComposeStart [%p]\n", (void *)this); if (IMEIsComposing()) { NS_WARNING("tried to re-start text composition\n"); return; } mIsComposing = PR_TRUE; nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_START, this); nsEventStatus status; DispatchEvent(&compEvent, status); IMESetCursorPosition(); } void nsWindow::IMEComposeText(const PRUnichar *aText, const PRInt32 aLen, const gchar *aPreeditString, const gint aCursorPos) { if (!IMEIsComposing()) { IMEComposeStart(); } //printf("IMEComposeText [%p]\n", (void *)this); nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, this); if (aLen != 0) { textEvent.theText = (PRUnichar*)aText; if (aPreeditString && (aLen > 0)) { IM_set_text_range(aLen, aPreeditString, aCursorPos, &(textEvent.rangeCount), &(textEvent.rangeArray)); } } nsEventStatus status; DispatchEvent(&textEvent, status); if (textEvent.rangeArray) { delete[] textEvent.rangeArray; } IMESetCursorPosition(); } void nsWindow::IMEComposeEnd(void) { //printf("IMEComposeEnd [%p]\n", (void *)this); if (!IMEIsComposing()) { NS_WARNING("tried to end text composition before it was started"); return; } mIsComposing = PR_FALSE; nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_END, this); nsEventStatus status; DispatchEvent(&compEvent, status); } NS_IMETHODIMP nsWindow::ResetInputState() { //printf("ResetInputState [%p]\n", (void*)this); CancelIMEComposition(); return NS_OK; } NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState) { //printf("SetIMEEnabled %d [%p]\n", aState, (void*)this); mIMEEnabled = aState; gboolean enabled = (aState == nsIWidget::IME_STATUS_ENABLED || aState == nsIWidget::IME_STATUS_PLUGIN) ? TRUE : FALSE; if (mDrawingarea) { moz_drawing_area_ime_enable(mDrawingarea, enabled); } return NS_OK; } NS_IMETHODIMP nsWindow::GetIMEEnabled(PRUint32* aState) { //printf("GetIMEEnabled [%p]\n", (void*)this); *aState = mIMEEnabled; return NS_OK; } NS_IMETHODIMP nsWindow::CancelIMEComposition() { //printf("CancelIMEComposition [%p]\n", (void*)this); if (mDrawingarea) { moz_drawing_area_ime_reset(mDrawingarea); } if (IMEIsComposing()) { IMEComposeText(nsnull, 0, nsnull, 0); IMEComposeEnd(); } return NS_OK; } NS_IMETHODIMP nsWindow::OnIMEFocusChange(PRBool aFocus) { //printf("OnIMEFocusChange %d [%p]\n", aFocus, (void*)this); gboolean in = aFocus ? TRUE : FALSE; if (mDrawingarea) { moz_drawing_area_ime_focus_change(mDrawingarea, in); } return NS_OK; } #endif