Bug #191072. Fixing focus for plugins. Patch from robin.lu@sun.com r=me

This commit is contained in:
blizzard%redhat.com 2003-02-17 15:36:37 +00:00
parent 78eed0300d
commit a8f66c1626
2 changed files with 196 additions and 46 deletions

View File

@ -119,9 +119,19 @@ static gboolean window_state_event_cb (GtkWidget *widget,
GdkEventWindowState *event);
static gboolean property_notify_event_cb (GtkWidget *widget,
GdkEventProperty *event);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent,
GdkEvent *event,
gpointer data);
static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent,
GdkEvent *event,
gpointer data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
static gboolean drag_motion_event_cb (GtkWidget *aWidget,
GdkDragContext *aDragContext,
gint aX,
@ -159,6 +169,7 @@ static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
static nsWindow *gFocusWindow = NULL;
static PRBool gGlobalsInitialized = PR_FALSE;
static PRBool gRaiseWindows = PR_TRUE;
static nsWindow *gPluginFocusWindow = NULL;
nsCOMPtr <nsIRollupListener> gRollupListener;
nsWeakPtr gRollupWindow;
@ -406,6 +417,11 @@ nsWindow::Destroy(void)
gFocusWindow = nsnull;
}
// make sure that we remove ourself as the plugin focus window
if (gPluginFocusWindow == this) {
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
// Remove our reference to the window group. If there was a window
// group destroying the widget will have automatically unreferenced
// the group, destroying it if necessary. And, if we're a child
@ -978,7 +994,7 @@ nsWindow::SetIcon(const nsAString& aIconSpec)
PRInt32 n = iconSpec.Find(key) + key.Length();
// Append that to icon resource path.
iconPath.Append(iconSpec.get() + n - 1 );
iconPath.Append(iconSpec.get() + n - 1);
nsCOMPtr<nsILocalFile> pathConverter;
rv = NS_NewLocalFile(iconPath, PR_TRUE,
@ -1282,24 +1298,6 @@ nsWindow::OnEnterNotifyEvent(GtkWidget *aWidget, GdkEventCrossing *aEvent)
LOG(("OnEnterNotify: %p\n", (void *)this));
// if we have a non-xembed plugin (java, basically) dispatch focus
// to it because it's too dumb not to know how to do it itself.
if( mHasNonXembedPlugin ) {
Window curFocusWindow;
int focusState;
XGetInputFocus(GDK_WINDOW_XDISPLAY (aEvent->window),
&curFocusWindow,
&focusState);
if(curFocusWindow != GDK_WINDOW_XWINDOW(aEvent->window)) {
mOldFocusWindow = curFocusWindow;
XSetInputFocus(GDK_WINDOW_XDISPLAY (aEvent->window),
GDK_WINDOW_XWINDOW(aEvent->window),
RevertToParent,
gtk_get_current_event_time ());
}
gdk_flush();
}
nsEventStatus status;
DispatchEvent(&event, status);
}
@ -1315,16 +1313,6 @@ nsWindow::OnLeaveNotifyEvent(GtkWidget *aWidget, GdkEventCrossing *aEvent)
LOG(("OnLeaveNotify: %p\n", (void *)this));
// if we have a non-xembed plugin (java, basically) take focus
// back since it's not going to give it up by itself.
if( mHasNonXembedPlugin ) {
XSetInputFocus(GDK_WINDOW_XDISPLAY (aEvent->window),
mOldFocusWindow,
RevertToParent,
gtk_get_current_event_time ());
gdk_flush();
}
nsEventStatus status;
DispatchEvent(&event, status);
}
@ -1341,6 +1329,11 @@ nsWindow::OnMotionNotifyEvent(GtkWidget *aWidget, GdkEventMotion *aEvent)
synthEvent = PR_TRUE;
}
// if plugins still keeps the focus, get it back
if (gPluginFocusWindow && gPluginFocusWindow != this) {
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
nsMouseEvent event;
InitMouseEvent(event, NS_MOUSE_MOVE);
@ -1477,6 +1470,12 @@ void
nsWindow::OnContainerFocusOutEvent(GtkWidget *aWidget, GdkEventFocus *aEvent)
{
LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
// plugin lose focus
if (gPluginFocusWindow) {
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
// Figure out if the focus widget is the child of this window. If
// it is, send a focus out and deactivate event for it.
if (!gFocusWindow)
@ -1488,7 +1487,7 @@ nsWindow::OnContainerFocusOutEvent(GtkWidget *aWidget, GdkEventFocus *aEvent)
while (tmpWindow && tmpnsWindow) {
// found it!
if(tmpnsWindow == gFocusWindow)
if (tmpnsWindow == gFocusWindow)
goto foundit;
tmpWindow = gdk_window_get_parent(tmpWindow);
@ -1575,7 +1574,7 @@ nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
// if none of the other modifier keys are pressed then we need to
// clear isShift so the character can be inserted in the editor
if ( event.isControl || event.isAlt || event.isMeta ) {
if (event.isControl || event.isAlt || event.isMeta) {
// make Ctrl+uppercase functional as same as Ctrl+lowercase
// when Ctrl+uppercase(eg.Ctrl+C) is pressed,convert the charCode
// from uppercase to lowercase(eg.Ctrl+c),so do Alt and Meta Key
@ -1583,9 +1582,9 @@ nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
// Windows platform in widget/src/windows/nsWindow.cpp: See bug 16486
// Note: if Shift is pressed at the same time, do not to_lower()
// Because Ctrl+Shift has different function with Ctrl
if ( !event.isShift &&
event.charCode >= GDK_A &&
event.charCode <= GDK_Z )
if (!event.isShift &&
event.charCode >= GDK_A &&
event.charCode <= GDK_Z)
event.charCode = gdk_keyval_to_lower(event.charCode);
}
}
@ -2111,11 +2110,12 @@ nsWindow::NativeCreate(nsIWidget *aParent,
}
else if (mWindowType == eWindowType_popup) {
mShell = gtk_window_new(GTK_WINDOW_POPUP);
if(topLevelParent) {
if (topLevelParent) {
gtk_window_set_transient_for(GTK_WINDOW(mShell),
topLevelParent);
mTransientParent = topLevelParent;
if(topLevelParent->group) {
if (topLevelParent->group) {
gtk_window_group_add_window(topLevelParent->group,
GTK_WINDOW(mShell));
mWindowGroup = topLevelParent->group;
@ -2167,7 +2167,7 @@ nsWindow::NativeCreate(nsIWidget *aParent,
}
// Disable the double buffer because it will make the caret crazy
// For bug#153805 (Gtk2 double buffer makes carets misbehave)
if( mContainer )
if (mContainer)
gtk_widget_set_double_buffered (GTK_WIDGET(mContainer),FALSE);
// label the drawing area with this object so we can find our way
@ -2529,7 +2529,7 @@ nsWindow::SetupPluginPort(void)
XSelectInput (GDK_DISPLAY (),
GDK_WINDOW_XWINDOW(mDrawingarea->inner_window),
xattrs.your_event_mask |
SubstructureNotifyMask );
SubstructureNotifyMask);
gdk_window_add_filter(mDrawingarea->inner_window,
plugin_window_filter_func,
@ -2592,6 +2592,101 @@ nsWindow::SetPluginType(PRBool aIsXembed)
mHasNonXembedPlugin = !aIsXembed;
}
void
nsWindow::SetNonXEmbedPluginFocus()
{
if (gPluginFocusWindow == this) {
return;
}
if (gPluginFocusWindow) {
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n"));
Window curFocusWindow;
int focusState;
XGetInputFocus(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_window),
&curFocusWindow,
&focusState);
LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow));
GdkWindow* toplevel = gdk_window_get_toplevel
(mDrawingarea->inner_window);
GdkWindow *gdkfocuswin = gdk_window_lookup(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 (gdkfocuswin != toplevel) {
return;
}
// switch the focus from the focus proxy to the plugin window
mOldFocusWindow = curFocusWindow;
XRaiseWindow(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_window),
GDK_WINDOW_XWINDOW(mDrawingarea->inner_window));
gdk_error_trap_push();
XSetInputFocus(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_window),
GDK_WINDOW_XWINDOW(mDrawingarea->inner_window),
RevertToNone,
CurrentTime);
gdk_flush();
gdk_error_trap_pop();
gPluginFocusWindow = this;
gdk_window_add_filter(NULL, plugin_client_message_filter, this);
LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n",
mOldFocusWindow,
GDK_WINDOW_XWINDOW(mDrawingarea->inner_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.
if (gPluginFocusWindow != this) {
return;
}
Window curFocusWindow;
int focusState;
XGetInputFocus(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_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(mDrawingarea->inner_window)) {
gdk_error_trap_push();
XRaiseWindow(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_window),
mOldFocusWindow);
XSetInputFocus(GDK_WINDOW_XDISPLAY(mDrawingarea->inner_window),
mOldFocusWindow,
RevertToParent,
CurrentTime);
gdk_flush();
gdk_error_trap_pop();
}
gPluginFocusWindow = NULL;
mOldFocusWindow = 0;
gdk_window_remove_filter(NULL, plugin_client_message_filter, this);
LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
}
PRBool
check_for_rollup(GdkWindow *aWindow, gdouble aMouseX, gdouble aMouseY,
PRBool aIsWheel)
@ -2688,8 +2783,8 @@ is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
gdk_window_get_size(aWindow, &w, &h);
if ( aMouseX > x && aMouseX < x + w &&
aMouseY > y && aMouseY < y + h )
if (aMouseX > x && aMouseX < x + w &&
aMouseY > y && aMouseY < y + h)
return PR_TRUE;
return PR_FALSE;
@ -3055,31 +3150,83 @@ plugin_window_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data
{
case CreateNotify:
case ReparentNotify:
if(xevent->type==CreateNotify) {
if (xevent->type==CreateNotify) {
plugin_window = gdk_window_lookup(xevent->xcreatewindow.window);
}
else {
if(xevent->xreparent.event != xevent->xreparent.parent)
if (xevent->xreparent.event != xevent->xreparent.parent)
break;
plugin_window = gdk_window_lookup (xevent->xreparent.window);
}
if(plugin_window) {
if (plugin_window) {
user_data = nsnull;
gdk_window_get_user_data(plugin_window, &user_data);
widget = GTK_WIDGET(user_data);
if(GTK_IS_SOCKET(widget)){
if (GTK_IS_SOCKET(widget)) {
break;
}
}
nswindow->SetPluginType(PR_FALSE);
return_val = GDK_FILTER_REMOVE;
break;
case EnterNotify:
// Currently we consider all plugins are non-xembed and calls
// SetNonXEmbedPluginFocus without any checking.
nswindow->SetNonXEmbedPluginFocus();
break;
case DestroyNotify:
gdk_window_remove_filter
((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)),
plugin_window_filter_func,
nswindow);
// Currently we consider all plugins are non-xembed and calls
// LoseNonXEmbedPluginFocus without any checking.
nswindow->LoseNonXEmbedPluginFocus();
break;
default:
break;
}
return return_val;
}
/* static */
GdkFilterReturn
plugin_client_message_filter (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)));
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
key_press_event_cb (GtkWidget *widget, GdkEventKey *event)
@ -3410,8 +3557,8 @@ get_inner_gdk_window (GdkWindow *aWindow,
if (child) {
GdkWindow * childWindow = (GdkWindow *) child->data;
gdk_window_get_geometry (childWindow, &cx, &cy, &cw, &ch, &cd);
if (( cx < x ) && (x < (cx + cw)) &&
( cy < y ) && (y < (cy + ch)) &&
if ((cx < x) && (x < (cx + cw)) &&
(cy < y) && (y < (cy + ch)) &&
gdk_window_is_visible (childWindow)) {
return get_inner_gdk_window (childWindow,
x - cx, y - cy,

View File

@ -229,7 +229,10 @@ public:
void GrabPointer (void);
void GrabKeyboard (void);
void ReleaseGrabs (void);
void SetPluginType(PRBool aIsXembed);
void SetNonXEmbedPluginFocus(void);
void LoseNonXEmbedPluginFocus(void);
Window mOldFocusWindow;