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 |