From f6618de512037e7f9701b03877f53868c47e01dc Mon Sep 17 00:00:00 2001 From: "tschmelcher@google.com" Date: Thu, 1 Oct 2009 17:30:15 +0000 Subject: Implement fullscreen on Linux. No "Press Esc" message yet though. Also it's DISPLAY_MODE_DEFAULT only for now (mode switching is evil anyways ;) ). Small bit of code cleanup in renderer.h. TESTED=entered and exited fullscreen; tested leaving with Esc, Alt+F4, click on region, and right-click on taskbare -> Close; all with both Compiz on and off, on gHardy. Review URL: http://codereview.chromium.org/258004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27732 0039d316-1c4b-4281-b951-d872f2087c98 --- o3d/converter/cross/renderer_stub.cc | 20 ++++ o3d/converter/cross/renderer_stub.h | 7 ++ o3d/core/cross/gl/renderer_gl.cc | 52 ++++++++++ o3d/core/cross/gl/renderer_gl.h | 8 ++ o3d/core/cross/renderer.h | 23 ++--- o3d/plugin/cross/o3d_glue.cc | 5 + o3d/plugin/cross/o3d_glue.h | 20 +++- o3d/plugin/linux/main_linux.cc | 187 +++++++++++++++++++++++++++++++---- 8 files changed, 279 insertions(+), 43 deletions(-) (limited to 'o3d') diff --git a/o3d/converter/cross/renderer_stub.cc b/o3d/converter/cross/renderer_stub.cc index 2801470..0f11826 100644 --- a/o3d/converter/cross/renderer_stub.cc +++ b/o3d/converter/cross/renderer_stub.cc @@ -183,6 +183,26 @@ void RendererStub::SetViewportInPixels(int, int, int, int, float, float) { DCHECK(false); } +bool RendererStub::GoFullscreen(const DisplayWindow& display, + int mode_id) { + return false; +} + +bool RendererStub::CancelFullscreen(const DisplayWindow& display, + int width, int height) { + return false; +} + +bool RendererStub::fullscreen() const { return false; } + +void RendererStub::GetDisplayModes(std::vector *modes) { + modes->clear(); +} + +bool RendererStub::GetDisplayMode(int id, DisplayMode *mode) { + return false; +} + void RendererStub::PlatformSpecificPresent(void) { } diff --git a/o3d/converter/cross/renderer_stub.h b/o3d/converter/cross/renderer_stub.h index 3f0c413..514349d 100644 --- a/o3d/converter/cross/renderer_stub.h +++ b/o3d/converter/cross/renderer_stub.h @@ -71,6 +71,13 @@ class RendererStub : public Renderer { int height, float min_z, float max_z); + virtual bool GoFullscreen(const DisplayWindow& display, + int mode_id); + virtual bool CancelFullscreen(const DisplayWindow& display, + int width, int height); + virtual bool fullscreen() const; + virtual void GetDisplayModes(std::vector *modes); + virtual bool GetDisplayMode(int id, DisplayMode *mode); // Overridden from Renderer. virtual const int* GetRGBAUByteNSwizzleTable(); diff --git a/o3d/core/cross/gl/renderer_gl.cc b/o3d/core/cross/gl/renderer_gl.cc index 02a6942..0e2f683 100644 --- a/o3d/core/cross/gl/renderer_gl.cc +++ b/o3d/core/cross/gl/renderer_gl.cc @@ -1214,16 +1214,68 @@ void RendererGL::Resize(int width, int height) { bool RendererGL::GoFullscreen(const DisplayWindow& display, int mode_id) { +#ifdef OS_LINUX + // This actually just switches the GLX context to the new window. The real + // work is in main_linux.cc. + const DisplayWindowLinux &display_platform = + static_cast(display); + display_ = display_platform.display(); + window_ = display_platform.window(); + if (!MakeCurrent()) { + return false; + } +#endif fullscreen_ = true; return true; } bool RendererGL::CancelFullscreen(const DisplayWindow& display, int width, int height) { +#ifdef OS_LINUX + // This actually just switches the GLX context to the old window. The real + // work is in main_linux.cc. + const DisplayWindowLinux &display_platform = + static_cast(display); + display_ = display_platform.display(); + window_ = display_platform.window(); + if (!MakeCurrent()) { + return false; + } +#endif fullscreen_ = false; return true; } +void RendererGL::GetDisplayModes(std::vector *modes) { +#ifdef OS_MACOSX + // Mac is supposed to call a different function in plugin_mac.mm instead. + DLOG(FATAL) << "Not supposed to be called"; +#endif + // On all other platforms this is unimplemented. Linux only supports + // DISPLAY_MODE_DEFAULT for now. + modes->clear(); +} + +bool RendererGL::GetDisplayMode(int id, DisplayMode *mode) { +#ifdef OS_MACOSX + // Mac is supposed to call a different function in plugin_mac.mm instead. + DLOG(FATAL) << "Not supposed to be called"; + return false; +#elif defined(OS_LINUX) + if (id == DISPLAY_MODE_DEFAULT) { + // Don't need to know any of this on Linux. + mode->Set(0, 0, 0, id); + return true; + } else { + // There are no other valid ids until we implement GetDisplayModes() and + // mode switching. + return false; + } +#else + return false; +#endif +} + bool RendererGL::PlatformSpecificStartRendering() { DLOG_FIRST_N(INFO, 10) << "RendererGL StartRendering"; MakeCurrentLazy(); diff --git a/o3d/core/cross/gl/renderer_gl.h b/o3d/core/cross/gl/renderer_gl.h index 722e29e2..de2a8cb 100644 --- a/o3d/core/cross/gl/renderer_gl.h +++ b/o3d/core/cross/gl/renderer_gl.h @@ -78,6 +78,14 @@ class RendererGL : public Renderer { return fullscreen_; } + // Get a vector of the available fullscreen display modes. + // Clears *modes on error. + virtual void GetDisplayModes(std::vector *modes); + + // Get a single fullscreen display mode by id. + // Returns true on success, false on error. + virtual bool GetDisplayMode(int id, DisplayMode *mode); + // Resizes the viewport in OpenGL. virtual void Resize(int width, int height); diff --git a/o3d/core/cross/renderer.h b/o3d/core/cross/renderer.h index c372bca..f299886 100644 --- a/o3d/core/cross/renderer.h +++ b/o3d/core/cross/renderer.h @@ -218,11 +218,8 @@ class Renderer { // display: a platform-specific display identifier // mode_id: a mode returned by GetDisplayModes // Returns true on success, false on failure. - // TODO(o3d): Make this pure virtual once it's implemented everywhere. virtual bool GoFullscreen(const DisplayWindow& display, - int mode_id) { - return false; - } + int mode_id) = 0; // Cancels fullscreen display. Restores rendering to windowed mode // with the given width and height. @@ -231,27 +228,19 @@ class Renderer { // width: the width to which to restore windowed rendering // height: the height to which to restore windowed rendering // Returns true on success, false on failure. - // TODO(o3d): Make this pure virtual once it's implemented everywhere. virtual bool CancelFullscreen(const DisplayWindow& display, - int width, int height) { - return false; - } + int width, int height) = 0; // Tells whether we're currently displayed fullscreen or not. - virtual bool fullscreen() const { return false; } + virtual bool fullscreen() const = 0; // Get a vector of the available fullscreen display modes. // Clears *modes on error. - // TODO: Make this pure virtual once it's implemented everywhere. - virtual void GetDisplayModes(std::vector *modes) { - modes->clear(); - } + virtual void GetDisplayModes(std::vector *modes) = 0; + // Get a single fullscreen display mode by id. // Returns true on success, false on error. - // TODO: Make this pure virtual once it's implemented everywhere. - virtual bool GetDisplayMode(int id, DisplayMode *mode) { - return false; - } + virtual bool GetDisplayMode(int id, DisplayMode *mode) = 0; // Gets the viewport. void GetViewport(Float4* rectangle, Float2* depth_range); diff --git a/o3d/plugin/cross/o3d_glue.cc b/o3d/plugin/cross/o3d_glue.cc index 91a12f7..d041907 100644 --- a/o3d/plugin/cross/o3d_glue.cc +++ b/o3d/plugin/cross/o3d_glue.cc @@ -140,8 +140,13 @@ PluginObject::PluginObject(NPP npp) xt_app_context_(NULL), xt_interval_(0), last_click_time_(0), + drawable_(0), gtk_container_(NULL), + gtk_fullscreen_container_(NULL), + gtk_event_source_(NULL), + event_handler_id_(0), timeout_id_(0), + fullscreen_pending_(false), draw_(true), in_plugin_(false), #endif diff --git a/o3d/plugin/cross/o3d_glue.h b/o3d/plugin/cross/o3d_glue.h index bd02228..4d2a90c 100644 --- a/o3d/plugin/cross/o3d_glue.h +++ b/o3d/plugin/cross/o3d_glue.h @@ -218,8 +218,13 @@ class PluginObject: public NPObject { } bool got_dblclick() const { return got_dblclick_; } void set_got_dblclick(bool got_dblclick) { got_dblclick_ = got_dblclick; } -#endif -#ifdef OS_MACOSX +#elif defined(OS_LINUX) + void SetGtkEventSource(GtkWidget *widget); + gboolean OnGtkConfigure(GtkWidget *widget, + GdkEventConfigure *configure_event); + gboolean OnGtkDelete(GtkWidget *widget, + GdkEvent *configure); +#elif defined(OS_MACOSX) void SetFullscreenOverlayMacWindow(WindowRef window) { mac_fullscreen_overlay_window_ = window; } @@ -284,9 +289,14 @@ class PluginObject: public NPObject { Time last_click_time_; // XEmbed mode + Window drawable_; GtkWidget *gtk_container_; + GtkWidget *gtk_fullscreen_container_; + GtkWidget *gtk_event_source_; + gulong event_handler_id_; bool got_double_click_[3]; guint timeout_id_; + bool fullscreen_pending_; bool draw_; bool in_plugin_; @@ -348,9 +358,9 @@ class PluginObject: public NPObject { // Make a region of the plugin area that will invoke fullscreen mode if // clicked. The app developer is responsible for communicating this to the - // user, as this region has no visible marker. The user is also responsible - // for updating this region if the plugin gets resized, as we don't know - // whether or how to scale it. + // user, as this region has no visible marker. The developer is also + // responsible for updating this region if the plugin gets resized, as we + // don't know whether or how to scale it. // Fails if the mode_id supplied isn't valid. Returns true on success. bool SetFullscreenClickRegion(int x, int y, int width, int height, int mode_id); diff --git a/o3d/plugin/linux/main_linux.cc b/o3d/plugin/linux/main_linux.cc index fbb6495..45cc982 100644 --- a/o3d/plugin/linux/main_linux.cc +++ b/o3d/plugin/linux/main_linux.cc @@ -75,7 +75,6 @@ void LinuxTimer(XtPointer data, XtIntervalId* id) { obj->draw_ = true; if (obj->renderer()) { if (obj->client()->render_mode() == o3d::Client::RENDERMODE_CONTINUOUS || - obj->renderer()->need_to_render()) { // NOTE: this draws no matter what instead of just invalidating the @@ -467,6 +466,10 @@ static gboolean GtkHandleMouseButton(GtkWidget *widget, event.set_type(Event::TYPE_DBLCLICK); obj->client()->AddEventToQueue(event); } + if (event.in_plugin() && event.type() == Event::TYPE_MOUSEDOWN && + obj->HitFullscreenClickRegion(event.x(), event.y())) { + obj->RequestFullscreenDisplay(); + } return TRUE; } @@ -501,6 +504,12 @@ static gboolean GtkHandleKey(GtkWidget *widget, event.set_type(Event::TYPE_KEYPRESS); obj->client()->AddEventToQueue(event); } + // No need to check for Alt+F4 because Gtk (or the window manager?) handles + // that and delivers a destroy event to us already. + if (event.type() == Event::TYPE_KEYDOWN && + event.key_code() == 0x1B) { // escape + obj->CancelFullscreenDisplay(); + } return TRUE; } @@ -539,6 +548,7 @@ static gboolean GtkEventCallback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { PluginObject *obj = static_cast(user_data); + DLOG_ASSERT(widget == obj->gtk_event_source_); switch (event->type) { case GDK_EXPOSE: if (GTK_WIDGET_DRAWABLE(widget)) { @@ -568,16 +578,35 @@ static gboolean GtkEventCallback(GtkWidget *widget, } } +static gboolean GtkConfigureEventCallback(GtkWidget *widget, + GdkEventConfigure *configure_event, + gpointer user_data) { + PluginObject *obj = static_cast(user_data); + return obj->OnGtkConfigure(widget, configure_event); +} + +static gboolean GtkDeleteEventCallback(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) { + PluginObject *obj = static_cast(user_data); + return obj->OnGtkDelete(widget, event); +} + static gboolean GtkTimeoutCallback(gpointer user_data) { PluginObject *obj = static_cast(user_data); obj->draw_ = true; obj->client()->Tick(); if (obj->renderer()) { if (obj->client()->render_mode() == o3d::Client::RENDERMODE_CONTINUOUS || - obj->renderer()->need_to_render()) { - gtk_widget_queue_draw(obj->gtk_container_); + GtkWidget *widget; + if (obj->fullscreen()) { + widget = obj->gtk_fullscreen_container_; + } else { + widget = obj->gtk_container_; + } + gtk_widget_queue_draw(widget); } } return TRUE; @@ -692,7 +721,15 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) { gtk_widget_unref(obj->gtk_container_); obj->gtk_container_ = NULL; } + if (obj->gtk_fullscreen_container_) { + gtk_widget_destroy(obj->gtk_fullscreen_container_); + gtk_widget_unref(obj->gtk_fullscreen_container_); + obj->gtk_container_ = NULL; + } + obj->gtk_event_source_ = NULL; + obj->event_handler_id_ = 0; obj->window_ = 0; + obj->drawable_ = 0; obj->display_ = NULL; obj->TearDown(); @@ -703,7 +740,6 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) { return NPERR_NO_ERROR; } - NPError NPP_SetWindow(NPP instance, NPWindow *window) { HANDLE_CRASHES; PluginObject *obj = static_cast(instance->pdata); @@ -719,18 +755,9 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window) { // 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); + if (!obj->fullscreen()) { + obj->SetGtkEventSource(obj->gtk_container_); + } gtk_widget_show(obj->gtk_container_); drawable = GDK_WINDOW_XID(obj->gtk_container_->window); obj->timeout_id_ = g_timeout_add(10, GtkTimeoutCallback, obj); @@ -765,6 +792,7 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window) { obj->client()->Init(); obj->display_ = display; obj->window_ = xwindow; + obj->drawable_ = drawable; } obj->Resize(window->width, window->height); @@ -790,19 +818,136 @@ int16 NPP_HandleEvent(NPP instance, void *event) { namespace glue { namespace _o3d { +void PluginObject::SetGtkEventSource(GtkWidget *widget) { + if (gtk_event_source_) { + g_signal_handler_disconnect(G_OBJECT(gtk_event_source_), + event_handler_id_); + } + gtk_event_source_ = widget; + if (gtk_event_source_) { + gtk_widget_add_events(gtk_event_source_, + 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); + event_handler_id_ = g_signal_connect(G_OBJECT(gtk_event_source_), "event", + G_CALLBACK(GtkEventCallback), this); + } +} + +gboolean PluginObject::OnGtkConfigure(GtkWidget *widget, + GdkEventConfigure *configure_event) { + DLOG_ASSERT(widget == gtk_fullscreen_container_); + if (fullscreen_pending_) { + // Our fullscreen window has been placed and sized. Switch to it. + fullscreen_pending_ = false; + Window fullscreen_window = + GDK_WINDOW_XID(gtk_fullscreen_container_->window); + DisplayWindowLinux display; + display.set_display(display_); + display.set_window(fullscreen_window); + prev_width_ = renderer()->width(); + prev_height_ = renderer()->height(); + if (!renderer()->GoFullscreen(display, fullscreen_region_mode_id_)) { + gtk_widget_destroy(gtk_fullscreen_container_); + gtk_fullscreen_container_ = NULL; + // The return value is for whether we handled the event, not whether it + // was successful, so return TRUE event for error. + return TRUE; + } + SetGtkEventSource(gtk_fullscreen_container_); + } + renderer()->Resize(configure_event->width, configure_event->height); + client()->SendResizeEvent(renderer()->width(), renderer()->height(), + true); + fullscreen_ = true; + return TRUE; +} + +gboolean PluginObject::OnGtkDelete(GtkWidget *widget, + GdkEvent *event) { + DLOG_ASSERT(widget == gtk_fullscreen_container_); + CancelFullscreenDisplay(); + return TRUE; +} + bool PluginObject::GetDisplayMode(int id, o3d::DisplayMode *mode) { - return false; + return renderer()->GetDisplayMode(id, mode); } // TODO: Where should this really live? It's platform-specific, but in // PluginObject, which mainly lives in cross/o3d_glue.h+cc. -bool PluginObject::RequestFullscreenDisplay() { - // TODO: Unimplemented. - return false; +bool PluginObject::RequestFullscreenDisplay() { + if (fullscreen_ || fullscreen_pending_) { + return false; + } + if (!g_xembed_support) { + // I tested every Linux browser I could that worked with our plugin and not + // a single one lacked XEmbed/Gtk2 support, so I don't think that case is + // worth implementing. + DLOG(ERROR) << "Fullscreen not supported without XEmbed/Gtk2; please use a " + "modern web browser"; + return false; + } + GtkWidget *widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + // The returned object counts as both a widget and a window. + GtkWindow *window = GTK_WINDOW(widget); + // The window title shouldn't normally be visible, but the user will see it + // if they Alt+Tab to another app. + gtk_window_set_title(window, "O3D Application"); + // Suppresses the title bar, resize controls, etc. + gtk_window_set_decorated(window, FALSE); + // Stops Gtk from writing an off-screen buffer to the display, which conflicts + // with our GL rendering. + gtk_widget_set_double_buffered(widget, FALSE); + GdkScreen *screen = gtk_window_get_screen(window); + // In the case of Xinerama or TwinView, these will be the dimensions of the + // whole desktop, which is wrong, but the window manager is smart enough to + // restrict our size to that of the main screen. + gint width = gdk_screen_get_width(screen); + gint height = gdk_screen_get_height(screen); + gtk_window_set_default_size(window, width, height); + // This is probably superfluous since we have already set an appropriate + // size, but let's do it anyway. It could still be relevant for some window + // managers. + gtk_window_fullscreen(window); + g_signal_connect(G_OBJECT(window), "configure-event", + G_CALLBACK(GtkConfigureEventCallback), this); + g_signal_connect(G_OBJECT(window), "delete-event", + G_CALLBACK(GtkDeleteEventCallback), this); + gtk_fullscreen_container_ = widget; + gtk_widget_show(widget); + // We defer switching to the new window until it gets displayed and assigned + // it's final dimensions in the configure-event. + fullscreen_pending_ = true; + return true; } void PluginObject::CancelFullscreenDisplay() { - // TODO: Unimplemented. + if (!fullscreen_) { + return; + } + o3d::DisplayWindowLinux default_display; + default_display.set_display(display_); + default_display.set_window(drawable_); + if (!renderer()->CancelFullscreen(default_display, + prev_width_, + prev_height_)) { + return; + } + renderer()->Resize(prev_width_, prev_height_); + client()->SendResizeEvent(renderer()->width(), renderer()->height(), + false); + SetGtkEventSource(gtk_container_); + gtk_widget_destroy(gtk_fullscreen_container_); + gtk_widget_unref(gtk_fullscreen_container_); + gtk_fullscreen_container_ = NULL; + fullscreen_ = false; } } // namespace _o3d } // namespace glue -- cgit v1.1