summaryrefslogtreecommitdiffstats
path: root/o3d/plugin/linux/main_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/plugin/linux/main_linux.cc')
-rw-r--r--o3d/plugin/linux/main_linux.cc315
1 files changed, 289 insertions, 26 deletions
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);