diff options
author | piman@google.com <piman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-29 18:42:06 +0000 |
---|---|---|
committer | piman@google.com <piman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-29 18:42:06 +0000 |
commit | 1c30273f852a1da694d769d38b411426e8a5ef69 (patch) | |
tree | 874757688444efef8d399c310d4932795f74282a | |
parent | 1a1f77936f887b936c29bd61f82397c8f5d987fa (diff) | |
download | chromium_src-1c30273f852a1da694d769d38b411426e8a5ef69.zip chromium_src-1c30273f852a1da694d769d38b411426e8a5ef69.tar.gz chromium_src-1c30273f852a1da694d769d38b411426e8a5ef69.tar.bz2 |
add XEmbed bindings
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19508 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | o3d/main.scons | 1 | ||||
-rw-r--r-- | o3d/plugin/cross/main.cc | 9 | ||||
-rw-r--r-- | o3d/plugin/cross/main.h | 2 | ||||
-rw-r--r-- | o3d/plugin/cross/o3d_glue.cc | 8 | ||||
-rw-r--r-- | o3d/plugin/cross/o3d_glue.h | 11 | ||||
-rw-r--r-- | o3d/plugin/linux/main_linux.cc | 315 | ||||
-rw-r--r-- | o3d/plugin/mac/main_mac.mm | 4 | ||||
-rw-r--r-- | o3d/plugin/win/main_win.cc | 4 |
8 files changed, 323 insertions, 31 deletions
diff --git a/o3d/main.scons b/o3d/main.scons index 4402f04..d734b41 100644 --- a/o3d/main.scons +++ b/o3d/main.scons @@ -662,6 +662,7 @@ linux_env = binaries_env.Clone( CG_DIR = '$CG_BASE_DIR/linux', ) linux_env.FilterOut(CPPDEFINES = ['OS_LINUX=OS_LINUX']) +linux_env.ParseConfig('pkg-config --cflags --libs gtk+-2.0') linux_env.Append( RENDERER_INCLUDE_PATH = [ diff --git a/o3d/plugin/cross/main.cc b/o3d/plugin/cross/main.cc index 0f8ab78..5f08407 100644 --- a/o3d/plugin/cross/main.cc +++ b/o3d/plugin/cross/main.cc @@ -146,9 +146,12 @@ extern "C" { *v = obj; break; } - default: - return NP_GetValue(instance, variable, value); - break; + default: { + NPError ret = PlatformNPPGetValue(instance, variable, value); + if (ret == NPERR_INVALID_PARAM) + ret = NP_GetValue(instance, variable, value); + return ret; + } } return NPERR_NO_ERROR; } diff --git a/o3d/plugin/cross/main.h b/o3d/plugin/cross/main.h index 79a657a..e72f458 100644 --- a/o3d/plugin/cross/main.h +++ b/o3d/plugin/cross/main.h @@ -97,6 +97,8 @@ namespace o3d { void WriteLogString(const char* text, int length); } // end namespace o3d +NPError PlatformNPPGetValue(NPP instance, NPPVariable variable, void *value); + // NPAPI declarations. Some of these are only implemented in the // platform-specific versions of "main.cc". diff --git a/o3d/plugin/cross/o3d_glue.cc b/o3d/plugin/cross/o3d_glue.cc index b14cd3b..8be6c18 100644 --- a/o3d/plugin/cross/o3d_glue.cc +++ b/o3d/plugin/cross/o3d_glue.cc @@ -138,9 +138,11 @@ PluginObject::PluginObject(NPP npp) xt_widget_(NULL), xt_app_context_(NULL), xt_interval_(0), + last_click_time_(0), + gtk_container_(NULL), + timeout_id_(0), draw_(true), in_plugin_(false), - last_click_time_(0), #endif np_v8_bridge_(&service_locator_, npp), stream_manager_(new StreamManager(npp)), @@ -155,6 +157,10 @@ PluginObject::PluginObject(NPP npp) memset(last_buffer_rect_, 0, sizeof(last_buffer_rect_)); #endif +#ifdef OS_LINUX + memset(got_double_click_, 0, sizeof(got_double_click_)); +#endif + // create an O3D object client_ = new Client(&service_locator_); diff --git a/o3d/plugin/cross/o3d_glue.h b/o3d/plugin/cross/o3d_glue.h index 625770e..133b09f 100644 --- a/o3d/plugin/cross/o3d_glue.h +++ b/o3d/plugin/cross/o3d_glue.h @@ -43,6 +43,7 @@ #ifdef OS_LINUX #include <GL/glx.h> #include <X11/Intrinsic.h> +#include <gtk/gtk.h> #endif @@ -250,12 +251,20 @@ class PluginObject: public NPObject { #ifdef OS_LINUX Display *display_; Window window_; + + // Xt mode Widget xt_widget_; XtAppContext xt_app_context_; XtIntervalId xt_interval_; + Time last_click_time_; + + // XEmbed mode + GtkWidget *gtk_container_; + bool got_double_click_[3]; + guint timeout_id_; + bool draw_; bool in_plugin_; - Time last_click_time_; #endif explicit PluginObject(NPP npp); ~PluginObject(); diff --git a/o3d/plugin/linux/main_linux.cc b/o3d/plugin/linux/main_linux.cc index dee3e1e..3dc781a 100644 --- a/o3d/plugin/linux/main_linux.cc +++ b/o3d/plugin/linux/main_linux.cc @@ -34,6 +34,8 @@ // the Linux platform. #include <X11/keysym.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> #include "base/at_exit.h" #include "base/command_line.h" #include "base/logging.h" @@ -51,6 +53,9 @@ namespace { // plugin, that's not possible, so we allocate it dynamically and // destroy it explicitly. scoped_ptr<base::AtExitManager> g_at_exit_manager; + +bool g_xembed_support = false; + } // end anonymous namespace static void DrawPlugin(PluginObject *obj) { @@ -68,6 +73,8 @@ void RenderOnDemandCallbackHandler::Run() { DrawPlugin(obj_); } +// Xt support functions + void LinuxTimer(XtPointer data, XtIntervalId* id) { PluginObject *obj = static_cast<PluginObject *>(data); DCHECK(obj->xt_interval_ == *id); @@ -219,7 +226,7 @@ static int KeySymToDOMKeyCode(KeySym key_sym) { } } -static int GetModifierState(int x_state) { +static int GetXModifierState(int x_state) { int modifier_state = 0; if (x_state & ControlMask) { modifier_state |= Event::MODIFIER_CTRL; @@ -259,7 +266,7 @@ void LinuxKeyHandler(Widget w, int result = XLookupString(key_event, &char_code, sizeof(char_code), &key_sym, NULL); event.set_key_code(KeySymToDOMKeyCode(key_sym)); - int modifier_state = GetModifierState(key_event->state); + int modifier_state = GetXModifierState(key_event->state); event.set_modifier_state(modifier_state); obj->client()->AddEventToQueue(event); if (xevent->type == KeyPress && result > 0) { @@ -270,8 +277,10 @@ void LinuxKeyHandler(Widget w, } } -// TODO: Any way to query the system for the correct value ? -const unsigned int kDoubleClickTime = 300; // in ms +// TODO: Any way to query the system for the correct value ? According to +// http://library.gnome.org/devel/gdk/stable/gdk-Event-Structures.html GTK uses +// 250ms. +const unsigned int kDoubleClickTime = 250; // in ms void LinuxMouseButtonHandler(Widget w, XtPointer user_data, @@ -313,18 +322,18 @@ void LinuxMouseButtonHandler(Widget w, default: return; } - int modifier_state = GetModifierState(button_event->state); + int modifier_state = GetXModifierState(button_event->state); event.set_modifier_state(modifier_state); event.set_position(button_event->x, button_event->y, button_event->x_root, button_event->y_root, obj->in_plugin()); obj->client()->AddEventToQueue(event); if (event.type() == Event::TYPE_MOUSEUP && obj->in_plugin()) { - event.set_type(Event::TYPE_CLICK); - obj->client()->AddEventToQueue(event); + // The event manager automatically generates CLICK from MOUSEDOWN, MOUSEUP. if (button_event->time < obj->last_click_time() + kDoubleClickTime) { obj->set_last_click_time(0); event.set_type(Event::TYPE_DBLCLICK); + obj->client()->AddEventToQueue(event); } else { obj->set_last_click_time(button_event->time); } @@ -340,7 +349,7 @@ void LinuxMouseMoveHandler(Widget w, return; XMotionEvent *motion_event = &xevent->xmotion; Event event(Event::TYPE_MOUSEMOVE); - int modifier_state = GetModifierState(motion_event->state); + int modifier_state = GetXModifierState(motion_event->state); event.set_modifier_state(modifier_state); event.set_position(motion_event->x, motion_event->y, motion_event->x_root, motion_event->y_root, @@ -365,6 +374,203 @@ void LinuxEnterLeaveHandler(Widget w, } } +// XEmbed / GTK support functions +static int GetGtkModifierState(int gtk_state) { + int modifier_state = 0; + if (gtk_state & GDK_CONTROL_MASK) { + modifier_state |= Event::MODIFIER_CTRL; + } + if (gtk_state & GDK_SHIFT_MASK) { + modifier_state |= Event::MODIFIER_SHIFT; + } + if (gtk_state & GDK_MOD1_MASK) { + modifier_state |= Event::MODIFIER_ALT; + } + if (gtk_state & GDK_META_MASK) { + modifier_state |= Event::MODIFIER_META; + } + return modifier_state; +} + +static gboolean GtkHandleMouseMove(GtkWidget *widget, + GdkEventMotion *motion_event, + PluginObject *obj) { + Event event(Event::TYPE_MOUSEMOVE); + int modifier_state = GetGtkModifierState(motion_event->state); + event.set_modifier_state(modifier_state); + event.set_position(static_cast<int>(motion_event->x), + static_cast<int>(motion_event->y), + static_cast<int>(motion_event->x_root), + static_cast<int>(motion_event->y_root), + obj->in_plugin()); + obj->client()->AddEventToQueue(event); + return TRUE; +} + +static gboolean GtkHandleMouseButton(GtkWidget *widget, + GdkEventButton *button_event, + PluginObject *obj) { + // On a double-click, Gtk produces: BUTTON_PRESS, BUTTON_RELEASE, + // BUTTON_PRESS, 2BUTTON_PRESS, BUTTON_RELEASE. + // JavaScript should receive: down, up, [optional move, ] click, down, + // up, click, dblclick. + // The EventManager turns (down, up) into click, since we need that on all + // platforms. + // So when a 2BUTTON_PRESS occurs, we keep track of this, so that we can + // issue a corresponding dblclick when BUTTON_RELEASE comes. + Event::Button button; + switch (button_event->button) { + case 1: + button = Event::BUTTON_LEFT; + break; + case 2: + button = Event::BUTTON_MIDDLE; + break; + case 3: + button = Event::BUTTON_RIGHT; + break; + default: + return FALSE; + } + Event::Type type; + switch (button_event->type) { + case GDK_BUTTON_PRESS: + type = Event::TYPE_MOUSEDOWN; + break; + case GDK_BUTTON_RELEASE: + type = Event::TYPE_MOUSEUP; + break; + default: + obj->got_double_click_[button_event->button - 1] = true; + return TRUE; + } + Event event(type); + int modifier_state = GetGtkModifierState(button_event->state); + event.set_modifier_state(modifier_state); + event.set_button(button); + event.set_position(static_cast<int>(button_event->x), + static_cast<int>(button_event->y), + static_cast<int>(button_event->x_root), + static_cast<int>(button_event->y_root), + obj->in_plugin()); + obj->client()->AddEventToQueue(event); + if (event.type() == Event::TYPE_MOUSEUP && obj->in_plugin() && + obj->got_double_click_[button_event->button - 1]) { + obj->got_double_click_[button_event->button - 1] = false; + event.set_type(Event::TYPE_DBLCLICK); + obj->client()->AddEventToQueue(event); + } +} + +static gboolean GtkHandleKey(GtkWidget *widget, + GdkEventKey *key_event, + PluginObject *obj) { + Event::Type type; + switch (key_event->type) { + case GDK_KEY_PRESS: + type = Event::TYPE_KEYDOWN; + break; + case GDK_KEY_RELEASE: + type = Event::TYPE_KEYUP; + break; + default: + return FALSE; + } + Event event(type); + // Logically, GTK events and X events use a different namespace for the + // various values, but in practice, all the keys we use have the same values, + // because one of the paths in GTK uses straight X to do the translation. So + // we can use the same function here. + int key_code = KeySymToDOMKeyCode(key_event->keyval); + event.set_key_code(key_code); + int modifier_state = GetGtkModifierState(key_event->state); + event.set_modifier_state(modifier_state); + obj->client()->AddEventToQueue(event); + int char_code = gdk_keyval_to_unicode(key_event->keyval); + if (key_event->type == GDK_KEY_PRESS && char_code != 0) { + event.clear_key_code(); + event.set_char_code(char_code); + event.set_type(Event::TYPE_KEYPRESS); + obj->client()->AddEventToQueue(event); + } + return TRUE; +} + +static gboolean GtkHandleScroll(GtkWidget *widget, + GdkEventScroll *scroll_event, + PluginObject *obj) { + Event event(Event::TYPE_WHEEL); + switch (scroll_event->direction) { + case GDK_SCROLL_UP: + event.set_delta(0, 1); + break; + case GDK_SCROLL_DOWN: + event.set_delta(0, -1); + break; + case GDK_SCROLL_LEFT: + event.set_delta(-1, 0); + break; + case GDK_SCROLL_RIGHT: + event.set_delta(1, 0); + break; + default: + return FALSE; + } + int modifier_state = GetGtkModifierState(scroll_event->state); + event.set_modifier_state(modifier_state); + event.set_position(static_cast<int>(scroll_event->x), + static_cast<int>(scroll_event->y), + static_cast<int>(scroll_event->x_root), + static_cast<int>(scroll_event->y_root), + obj->in_plugin()); + obj->client()->AddEventToQueue(event); + return TRUE; +} + +static gboolean GtkEventCallback(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + switch (event->type) { + case GDK_EXPOSE: + if (GTK_WIDGET_DRAWABLE(widget)) { + obj->draw_ = true; + DrawPlugin(obj); + } + return TRUE; + case GDK_ENTER_NOTIFY: + obj->set_in_plugin(true); + return TRUE; + case GDK_LEAVE_NOTIFY: + obj->set_in_plugin(false); + return TRUE; + case GDK_MOTION_NOTIFY: + return GtkHandleMouseMove(widget, &event->motion, obj); + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + return GtkHandleMouseButton(widget, &event->button, obj); + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + return GtkHandleKey(widget, &event->key, obj); + case GDK_SCROLL: + return GtkHandleScroll(widget, &event->scroll, obj); + default: + return FALSE; + } +} + +static gboolean GtkTimeoutCallback(gpointer user_data) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + obj->draw_ = true; + obj->client()->Tick(); + if (obj->client()->render_mode() == + o3d::Client::RENDERMODE_CONTINUOUS) { + gtk_widget_queue_draw(obj->gtk_container_); + } + return TRUE; +} + bool PluginObject::GetDisplayMode(int id, o3d::DisplayMode *mode) { return false; } @@ -380,6 +586,17 @@ void PluginObject::CancelFullscreenDisplay() { // TODO: Unimplemented. } +NPError PlatformNPPGetValue(NPP instance, NPPVariable variable, void *value) { + switch (variable) { + case NPPVpluginNeedsXEmbed: + *static_cast<NPBool *>(value) = g_xembed_support; + return NPERR_NO_ERROR; + default: + return NPERR_INVALID_PARAM; + } + return NPERR_NO_ERROR; +} + extern "C" { NPError InitializePlugin() { if (!o3d::SetupOutOfMemoryHandler()) @@ -397,6 +614,19 @@ extern "C" { DLOG(INFO) << "NP_Initialize"; + NPBool xembed_support = 0; + NPError err = NPN_GetValue(NULL, NPNVSupportsXEmbedBool, &xembed_support); + if (err != NPERR_NO_ERROR) + xembed_support = 0; + + if (xembed_support) { + NPNToolkitType toolkit = static_cast<NPNToolkitType>(0); + err = NPN_GetValue(NULL, NPNVToolkit, &toolkit); + if (err != NPERR_NO_ERROR || toolkit != NPNVGtk2) + xembed_support = 0; + } + g_xembed_support = xembed_support != 0; + return NPERR_NO_ERROR; } @@ -450,6 +680,15 @@ extern "C" { XtRemoveTimeOut(obj->xt_interval_); obj->xt_interval_ = 0; } + if (obj->timeout_id_) { + g_source_remove(obj->timeout_id_); + obj->timeout_id_ = 0; + } + if (obj->gtk_container_) { + gtk_widget_destroy(obj->gtk_container_); + gtk_widget_unref(obj->gtk_container_); + obj->gtk_container_ = NULL; + } obj->window_ = 0; obj->display_ = NULL; @@ -471,16 +710,53 @@ extern "C" { Window xwindow = reinterpret_cast<Window>(window->window); if (xwindow != obj->window_) { Display *display = cb_struct->display; - Widget widget = XtWindowToWidget(display, xwindow); - if (!widget) { - DLOG(ERROR) << "window is not a Widget"; - return NPERR_MODULE_LOAD_FAILED_ERROR; + Window drawable = xwindow; + if (g_xembed_support) { + // We asked for a XEmbed plugin, the xwindow is a GtkSocket, we create + // a GtkPlug to go into it. + obj->gtk_container_ = gtk_plug_new(xwindow); + gtk_widget_set_double_buffered(obj->gtk_container_, FALSE); + gtk_widget_add_events(obj->gtk_container_, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_EXPOSURE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK); + g_signal_connect(G_OBJECT(obj->gtk_container_), "event", + G_CALLBACK(GtkEventCallback), obj); + gtk_widget_show(obj->gtk_container_); + drawable = GDK_WINDOW_XID(obj->gtk_container_->window); + obj->timeout_id_ = g_timeout_add(10, GtkTimeoutCallback, obj); + } else { + // No XEmbed support, the xwindow is a Xt Widget. + Widget widget = XtWindowToWidget(display, xwindow); + if (!widget) { + DLOG(ERROR) << "window is not a Widget"; + return NPERR_MODULE_LOAD_FAILED_ERROR; + } + obj->xt_widget_ = widget; + XtAddEventHandler(widget, ExposureMask, 0, LinuxExposeHandler, obj); + XtAddEventHandler(widget, KeyPressMask|KeyReleaseMask, 0, + LinuxKeyHandler, obj); + XtAddEventHandler(widget, ButtonPressMask|ButtonReleaseMask, 0, + LinuxMouseButtonHandler, obj); + XtAddEventHandler(widget, PointerMotionMask, 0, + LinuxMouseMoveHandler, obj); + XtAddEventHandler(widget, EnterWindowMask|LeaveWindowMask, 0, + LinuxEnterLeaveHandler, obj); + obj->xt_app_context_ = XtWidgetToApplicationContext(widget); + obj->xt_interval_ = + XtAppAddTimeOut(obj->xt_app_context_, 10, LinuxTimer, obj); } // Create and assign the graphics context. o3d::DisplayWindowLinux default_display; default_display.set_display(display); - default_display.set_window(xwindow); + default_display.set_window(drawable); obj->CreateRenderer(default_display); obj->client()->Init(); @@ -488,19 +764,6 @@ extern "C" { new RenderOnDemandCallbackHandler(obj)); obj->display_ = display; obj->window_ = xwindow; - obj->xt_widget_ = widget; - XtAddEventHandler(widget, ExposureMask, 0, LinuxExposeHandler, obj); - XtAddEventHandler(widget, KeyPressMask|KeyReleaseMask, 0, - LinuxKeyHandler, obj); - XtAddEventHandler(widget, ButtonPressMask|ButtonReleaseMask, 0, - LinuxMouseButtonHandler, obj); - XtAddEventHandler(widget, PointerMotionMask, 0, - LinuxMouseMoveHandler, obj); - XtAddEventHandler(widget, EnterWindowMask|LeaveWindowMask, 0, - LinuxEnterLeaveHandler, obj); - obj->xt_app_context_ = XtWidgetToApplicationContext(widget); - obj->xt_interval_ = - XtAppAddTimeOut(obj->xt_app_context_, 10, LinuxTimer, obj); } obj->Resize(window->width, window->height); diff --git a/o3d/plugin/mac/main_mac.mm b/o3d/plugin/mac/main_mac.mm index c9dcec0..59c374a 100644 --- a/o3d/plugin/mac/main_mac.mm +++ b/o3d/plugin/mac/main_mac.mm @@ -592,6 +592,10 @@ bool HandleMacEvent(EventRecord* the_event, NPP instance) { return handled; } +NPError PlatformNPPGetValue(NPP instance, NPPVariable variable, void *value) { + return NPERR_INVALID_PARAM; +} + extern "C" { NPError InitializePlugin() { if (!o3d::SetupOutOfMemoryHandler()) diff --git a/o3d/plugin/win/main_win.cc b/o3d/plugin/win/main_win.cc index df70b0e..db11b17 100644 --- a/o3d/plugin/win/main_win.cc +++ b/o3d/plugin/win/main_win.cc @@ -640,6 +640,10 @@ WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return 0; } +NPError PlatformNPPGetValue(NPP instance, NPPVariable variable, void *value) { + return NPERR_INVALID_PARAM; +} + extern "C" { BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_DETACH) { |