OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "webkit/tools/test_shell/webwidget_host.h" |
| 6 |
| 7 #include <cairo/cairo.h> |
| 8 #include <gtk/gtk.h> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "base/gfx/platform_canvas_linux.h" |
| 12 #include "base/gfx/platform_device_linux.h" |
| 13 #include "base/gfx/bitmap_platform_device_linux.h" |
| 14 #include "webkit/glue/webinputevent.h" |
| 15 #include "webkit/glue/webwidget.h" |
| 16 |
| 17 namespace { |
| 18 |
| 19 // ----------------------------------------------------------------------------- |
| 20 // Callback functions to proxy to host... |
| 21 |
| 22 gboolean ConfigureEvent(GtkWidget* widget, GdkEventConfigure* config, |
| 23 WebWidgetHost* host) { |
| 24 host->Resize(gfx::Size(config->width, config->height)); |
| 25 return FALSE; |
| 26 } |
| 27 |
| 28 gboolean ExposeEvent(GtkWidget* widget, GdkEventExpose* expose, |
| 29 WebWidgetHost* host) { |
| 30 gfx::Rect rect(expose->area); |
| 31 host->UpdatePaintRect(rect); |
| 32 host->Paint(); |
| 33 return FALSE; |
| 34 } |
| 35 |
| 36 gboolean DestroyEvent(GtkWidget* widget, GdkEvent* event, |
| 37 WebWidgetHost* host) { |
| 38 host->WindowDestroyed(); |
| 39 return FALSE; |
| 40 } |
| 41 |
| 42 gboolean KeyPressReleaseEvent(GtkWidget* widget, GdkEventKey* event, |
| 43 WebWidgetHost* host) { |
| 44 WebKeyboardEvent wke(event); |
| 45 host->webwidget()->HandleInputEvent(&wke); |
| 46 |
| 47 // The WebKeyboardEvent model, when holding down a key, is: |
| 48 // KEY_DOWN, CHAR, (repeated CHAR as key repeats,) KEY_UP |
| 49 // The GDK model for the same sequence is just: |
| 50 // KEY_PRESS, (repeated KEY_PRESS as key repeats,) KEY_RELEASE |
| 51 // So we must simulate a CHAR event for every key press. |
| 52 if (event->type == GDK_KEY_PRESS) { |
| 53 wke.type = WebKeyboardEvent::CHAR; |
| 54 host->webwidget()->HandleInputEvent(&wke); |
| 55 } |
| 56 |
| 57 return FALSE; |
| 58 } |
| 59 |
| 60 gboolean FocusIn(GtkWidget* widget, GdkEventFocus* focus, |
| 61 WebWidgetHost* host) { |
| 62 host->webwidget()->SetFocus(true); |
| 63 return FALSE; |
| 64 } |
| 65 |
| 66 gboolean FocusOut(GtkWidget* widget, GdkEventFocus* focus, |
| 67 WebWidgetHost* host) { |
| 68 host->webwidget()->SetFocus(false); |
| 69 return FALSE; |
| 70 } |
| 71 |
| 72 gboolean ButtonPressReleaseEvent(GtkWidget* widget, GdkEventButton* event, |
| 73 WebWidgetHost* host) { |
| 74 WebMouseEvent wme(event); |
| 75 host->webwidget()->HandleInputEvent(&wme); |
| 76 return FALSE; |
| 77 } |
| 78 |
| 79 gboolean MouseMoveEvent(GtkWidget* widget, GdkEventMotion* event, |
| 80 WebWidgetHost* host) { |
| 81 WebMouseEvent wme(event); |
| 82 host->webwidget()->HandleInputEvent(&wme); |
| 83 return FALSE; |
| 84 } |
| 85 |
| 86 gboolean MouseScrollEvent(GtkWidget* widget, GdkEventScroll* event, |
| 87 WebWidgetHost* host) { |
| 88 WebMouseWheelEvent wmwe(event); |
| 89 host->webwidget()->HandleInputEvent(&wmwe); |
| 90 return FALSE; |
| 91 } |
| 92 |
| 93 } // anonymous namespace |
| 94 |
| 95 // ----------------------------------------------------------------------------- |
| 96 |
| 97 gfx::WindowHandle WebWidgetHost::CreateWindow(gfx::WindowHandle box, |
| 98 void* host) { |
| 99 GtkWidget* widget = gtk_drawing_area_new(); |
| 100 gtk_box_pack_start(GTK_BOX(box), widget, TRUE, TRUE, 0); |
| 101 |
| 102 gtk_widget_add_events(widget, GDK_EXPOSURE_MASK | |
| 103 GDK_POINTER_MOTION_MASK | |
| 104 GDK_BUTTON_PRESS_MASK | |
| 105 GDK_BUTTON_RELEASE_MASK | |
| 106 GDK_KEY_PRESS_MASK | |
| 107 GDK_KEY_RELEASE_MASK); |
| 108 GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS); |
| 109 g_signal_connect(widget, "configure-event", G_CALLBACK(ConfigureEvent), host); |
| 110 g_signal_connect(widget, "expose-event", G_CALLBACK(ExposeEvent), host); |
| 111 g_signal_connect(widget, "destroy-event", G_CALLBACK(DestroyEvent), host); |
| 112 g_signal_connect(widget, "key-press-event", G_CALLBACK(KeyPressReleaseEvent), |
| 113 host); |
| 114 g_signal_connect(widget, "key-release-event", |
| 115 G_CALLBACK(KeyPressReleaseEvent), host); |
| 116 g_signal_connect(widget, "focus-in-event", G_CALLBACK(FocusIn), host); |
| 117 g_signal_connect(widget, "focus-out-event", G_CALLBACK(FocusOut), host); |
| 118 g_signal_connect(widget, "button-press-event", |
| 119 G_CALLBACK(ButtonPressReleaseEvent), host); |
| 120 g_signal_connect(widget, "button-release-event", |
| 121 G_CALLBACK(ButtonPressReleaseEvent), host); |
| 122 g_signal_connect(widget, "motion-notify-event", G_CALLBACK(MouseMoveEvent), |
| 123 host); |
| 124 g_signal_connect(widget, "scroll-event", G_CALLBACK(MouseScrollEvent), |
| 125 host); |
| 126 |
| 127 return widget; |
| 128 } |
| 129 |
| 130 WebWidgetHost* WebWidgetHost::Create(gfx::WindowHandle box, |
| 131 WebWidgetDelegate* delegate) { |
| 132 WebWidgetHost* host = new WebWidgetHost(); |
| 133 host->view_ = CreateWindow(box, host); |
| 134 host->webwidget_ = WebWidget::Create(delegate); |
| 135 |
| 136 return host; |
| 137 } |
| 138 |
| 139 void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) { |
| 140 paint_rect_ = paint_rect_.Union(rect); |
| 141 } |
| 142 |
| 143 void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { |
| 144 DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting"; |
| 145 |
| 146 UpdatePaintRect(damaged_rect); |
| 147 |
| 148 gtk_widget_queue_draw_area(GTK_WIDGET(view_), damaged_rect.x(), |
| 149 damaged_rect.y(), damaged_rect.width(), damaged_rect.height()); |
| 150 } |
| 151 |
| 152 void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { |
| 153 // This is used for optimizing painting when the renderer is scrolled. We're |
| 154 // currently not doing any optimizations so just invalidate the region. |
| 155 DidInvalidateRect(clip_rect); |
| 156 } |
| 157 |
| 158 WebWidgetHost* FromWindow(gfx::WindowHandle view) { |
| 159 const gpointer p = g_object_get_data(G_OBJECT(view), "webwidgethost"); |
| 160 return (WebWidgetHost* ) p; |
| 161 } |
| 162 |
| 163 WebWidgetHost::WebWidgetHost() |
| 164 : view_(NULL), |
| 165 webwidget_(NULL), |
| 166 scroll_dx_(0), |
| 167 scroll_dy_(0), |
| 168 track_mouse_leave_(false) { |
| 169 set_painting(false); |
| 170 } |
| 171 |
| 172 WebWidgetHost::~WebWidgetHost() { |
| 173 webwidget_->Close(); |
| 174 webwidget_->Release(); |
| 175 } |
| 176 |
| 177 void WebWidgetHost::Resize(const gfx::Size &newsize) { |
| 178 // The pixel buffer backing us is now the wrong size |
| 179 canvas_.reset(); |
| 180 |
| 181 webwidget_->Resize(gfx::Size(newsize.width(), newsize.height())); |
| 182 } |
| 183 |
| 184 void WebWidgetHost::Paint() { |
| 185 int width = view_->allocation.width; |
| 186 int height = view_->allocation.height; |
| 187 gfx::Rect client_rect(width, height); |
| 188 |
| 189 // Allocate a canvas if necessary |
| 190 if (!canvas_.get()) { |
| 191 ResetScrollRect(); |
| 192 paint_rect_ = client_rect; |
| 193 canvas_.reset(new gfx::PlatformCanvas(width, height, true)); |
| 194 if (!canvas_.get()) { |
| 195 // memory allocation failed, we can't paint. |
| 196 LOG(ERROR) << "Failed to allocate memory for " << width << "x" << height; |
| 197 return; |
| 198 } |
| 199 } |
| 200 |
| 201 // This may result in more invalidation |
| 202 webwidget_->Layout(); |
| 203 |
| 204 // Paint the canvas if necessary. Allow painting to generate extra rects the |
| 205 // first time we call it. This is necessary because some WebCore rendering |
| 206 // objects update their layout only when painted. |
| 207 for (int i = 0; i < 2; ++i) { |
| 208 paint_rect_ = client_rect.Intersect(paint_rect_); |
| 209 if (!paint_rect_.IsEmpty()) { |
| 210 gfx::Rect rect(paint_rect_); |
| 211 paint_rect_ = gfx::Rect(); |
| 212 |
| 213 DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations"; |
| 214 PaintRect(rect); |
| 215 } |
| 216 } |
| 217 DCHECK(paint_rect_.IsEmpty()); |
| 218 |
| 219 // BitBlit to the X server |
| 220 gfx::PlatformDeviceLinux &platdev = canvas_->getTopPlatformDevice(); |
| 221 gfx::BitmapPlatformDeviceLinux* const bitdev = |
| 222 static_cast<gfx::BitmapPlatformDeviceLinux* >(&platdev); |
| 223 |
| 224 cairo_t* cairo_drawable = gdk_cairo_create(view_->window); |
| 225 cairo_set_source_surface(cairo_drawable, bitdev->surface(), 0, 0); |
| 226 cairo_paint(cairo_drawable); |
| 227 cairo_destroy(cairo_drawable); |
| 228 } |
| 229 |
| 230 void WebWidgetHost::ResetScrollRect() { |
| 231 // This method is only needed for optimized scroll painting, which we don't |
| 232 // care about in the test shell, yet. |
| 233 } |
| 234 |
| 235 void WebWidgetHost::PaintRect(const gfx::Rect& rect) { |
| 236 set_painting(true); |
| 237 webwidget_->Paint(canvas_.get(), rect); |
| 238 set_painting(false); |
| 239 } |
| 240 |
| 241 // ----------------------------------------------------------------------------- |
| 242 // This is called when the GTK window is destroyed. In the Windows code this |
| 243 // deletes this object. Since it's only test_shell it probably doesn't matter |
| 244 // that much. |
| 245 // ----------------------------------------------------------------------------- |
| 246 void WebWidgetHost::WindowDestroyed() { |
| 247 delete this; |
| 248 } |
OLD | NEW |