1 /* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 University of Szeged 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #if PLUGIN_ARCHITECTURE(X11) 29 30 #include "NetscapePlugin.h" 31 32 #include "WebEvent.h" 33 #include <WebCore/GraphicsContext.h> 34 #include <WebCore/NotImplemented.h> 35 36 #if PLATFORM(QT) 37 #include <QApplication> 38 #include <QDesktopWidget> 39 #include <QPixmap> 40 #include <QX11Info> 41 #elif PLATFORM(GTK) 42 #include <gdk/gdkx.h> 43 #include <WebCore/GtkVersioning.h> 44 #endif 45 46 using namespace WebCore; 47 48 namespace WebKit { 49 50 static Display *getPluginDisplay() 51 { 52 #if PLATFORM(QT) 53 // At the moment, we only support gdk based plugins (like Flash) that use a different X connection. 54 // The code below has the same effect as this one: 55 // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default()); 56 57 QLibrary library(QLatin1String("libgdk-x11-2.0"), 0); 58 if (!library.load()) 59 return 0; 60 61 typedef void *(*gdk_display_get_default_ptr)(); 62 gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default"); 63 if (!gdk_display_get_default) 64 return 0; 65 66 typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *); 67 gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay"); 68 if (!gdk_x11_display_get_xdisplay) 69 return 0; 70 71 return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); 72 #elif PLATFORM(GTK) 73 // Since we're a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based 74 // plugins, so we can return that. We might want to add other implementations here later. 75 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 76 #else 77 return 0; 78 #endif 79 } 80 81 static inline Display* x11Display() 82 { 83 #if PLATFORM(QT) 84 return QX11Info::display(); 85 #elif PLATFORM(GTK) 86 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 87 #else 88 return 0; 89 #endif 90 } 91 92 static inline int displayDepth() 93 { 94 #if PLATFORM(QT) 95 return QApplication::desktop()->x11Info().depth(); 96 #elif PLATFORM(GTK) 97 return gdk_visual_get_depth(gdk_screen_get_system_visual(gdk_screen_get_default())); 98 #else 99 return 0; 100 #endif 101 } 102 103 static inline unsigned long rootWindowID() 104 { 105 #if PLATFORM(QT) 106 return QX11Info::appRootWindow(); 107 #elif PLATFORM(GTK) 108 return GDK_ROOT_WINDOW(); 109 #else 110 return 0; 111 #endif 112 } 113 114 static inline int x11Screen() 115 { 116 #if PLATFORM(QT) 117 return QX11Info::appScreen(); 118 #elif PLATFORM(GTK) 119 return gdk_screen_get_number(gdk_screen_get_default()); 120 #else 121 return 0; 122 #endif 123 } 124 125 bool NetscapePlugin::platformPostInitialize() 126 { 127 if (m_isWindowed) 128 return false; 129 130 if (!(m_pluginDisplay = getPluginDisplay())) 131 return false; 132 133 NPSetWindowCallbackStruct* callbackStruct = new NPSetWindowCallbackStruct; 134 callbackStruct->type = 0; 135 Display* display = x11Display(); 136 int depth = displayDepth(); 137 callbackStruct->display = display; 138 callbackStruct->depth = depth; 139 140 XVisualInfo visualTemplate; 141 visualTemplate.screen = x11Screen(); 142 visualTemplate.depth = depth; 143 visualTemplate.c_class = TrueColor; 144 int numMatching; 145 XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask, 146 &visualTemplate, &numMatching); 147 ASSERT(visualInfo); 148 Visual* visual = visualInfo[0].visual; 149 ASSERT(visual); 150 XFree(visualInfo); 151 152 callbackStruct->visual = visual; 153 callbackStruct->colormap = XCreateColormap(display, rootWindowID(), visual, AllocNone); 154 155 m_npWindow.type = NPWindowTypeDrawable; 156 m_npWindow.window = 0; 157 m_npWindow.ws_info = callbackStruct; 158 159 callSetWindow(); 160 161 return true; 162 } 163 164 void NetscapePlugin::platformDestroy() 165 { 166 delete static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info); 167 168 if (m_drawable) { 169 XFreePixmap(x11Display(), m_drawable); 170 m_drawable = 0; 171 } 172 } 173 174 bool NetscapePlugin::platformInvalidate(const IntRect&) 175 { 176 notImplemented(); 177 return false; 178 } 179 180 void NetscapePlugin::platformGeometryDidChange() 181 { 182 if (m_isWindowed) { 183 notImplemented(); 184 return; 185 } 186 187 Display* display = x11Display(); 188 if (m_drawable) 189 XFreePixmap(display, m_drawable); 190 191 m_drawable = XCreatePixmap(display, rootWindowID(), m_frameRect.width(), m_frameRect.height(), displayDepth()); 192 193 XSync(display, false); // Make sure that the server knows about the Drawable. 194 } 195 196 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool /*isSnapshot*/) 197 { 198 if (m_isWindowed) { 199 notImplemented(); 200 return; 201 } 202 203 if (!m_isStarted) { 204 // FIXME: we should paint a missing plugin icon. 205 return; 206 } 207 208 if (context->paintingDisabled()) 209 return; 210 211 ASSERT(m_drawable); 212 213 #if PLATFORM(QT) 214 QPainter* painter = context->platformContext(); 215 painter->translate(m_frameRect.x(), m_frameRect.y()); 216 #else 217 notImplemented(); 218 return; 219 #endif 220 221 XEvent xevent; 222 memset(&xevent, 0, sizeof(XEvent)); 223 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; 224 exposeEvent.type = GraphicsExpose; 225 exposeEvent.display = x11Display(); 226 exposeEvent.drawable = m_drawable; 227 228 IntRect exposedRect(dirtyRect); 229 exposedRect.intersect(m_frameRect); 230 exposedRect.move(-m_frameRect.x(), -m_frameRect.y()); 231 exposeEvent.x = exposedRect.x(); 232 exposeEvent.y = exposedRect.y(); 233 234 // Note: in transparent mode Flash thinks width is the right and height is the bottom. 235 // We should take it into account if we want to support transparency. 236 exposeEvent.width = exposedRect.width(); 237 exposeEvent.height = exposedRect.height(); 238 239 NPP_HandleEvent(&xevent); 240 241 if (m_pluginDisplay != x11Display()) 242 XSync(m_pluginDisplay, false); 243 244 #if PLATFORM(QT) 245 QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared); 246 ASSERT(qtDrawable.depth() == static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)->depth); 247 painter->drawPixmap(QPoint(exposedRect.x(), exposedRect.y()), qtDrawable, exposedRect); 248 249 painter->translate(-m_frameRect.x(), -m_frameRect.y()); 250 #endif 251 } 252 253 static inline void initializeXEvent(XEvent& event) 254 { 255 memset(&event, 0, sizeof(XEvent)); 256 event.xany.serial = 0; 257 event.xany.send_event = false; 258 event.xany.display = x11Display(); 259 event.xany.window = 0; 260 } 261 262 static inline uint64_t xTimeStamp(double timestampInSeconds) 263 { 264 return timestampInSeconds * 1000; 265 } 266 267 static inline unsigned xKeyModifiers(const WebEvent& event) 268 { 269 unsigned xModifiers = 0; 270 if (event.controlKey()) 271 xModifiers |= ControlMask; 272 if (event.shiftKey()) 273 xModifiers |= ShiftMask; 274 if (event.altKey()) 275 xModifiers |= Mod1Mask; 276 if (event.metaKey()) 277 xModifiers |= Mod4Mask; 278 279 return xModifiers; 280 } 281 282 template <typename XEventType> 283 static inline void setCommonMouseEventFields(XEventType& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) 284 { 285 xEvent.root = rootWindowID(); 286 xEvent.subwindow = 0; 287 xEvent.time = xTimeStamp(webEvent.timestamp()); 288 xEvent.x = webEvent.position().x() - pluginLocation.x(); 289 xEvent.y = webEvent.position().y() - pluginLocation.y(); 290 xEvent.x_root = webEvent.globalPosition().x(); 291 xEvent.y_root = webEvent.globalPosition().y(); 292 xEvent.state = xKeyModifiers(webEvent); 293 xEvent.same_screen = true; 294 } 295 296 static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) 297 { 298 XMotionEvent& xMotion = xEvent.xmotion; 299 setCommonMouseEventFields(xMotion, webEvent, pluginLocation); 300 xMotion.type = MotionNotify; 301 } 302 303 static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) 304 { 305 XButtonEvent& xButton = xEvent.xbutton; 306 setCommonMouseEventFields(xButton, webEvent, pluginLocation); 307 308 xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease; 309 switch (webEvent.button()) { 310 case WebMouseEvent::LeftButton: 311 xButton.button = Button1; 312 break; 313 case WebMouseEvent::MiddleButton: 314 xButton.button = Button2; 315 break; 316 case WebMouseEvent::RightButton: 317 xButton.button = Button3; 318 break; 319 } 320 } 321 322 static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type) 323 { 324 XCrossingEvent& xCrossing = xEvent.xcrossing; 325 setCommonMouseEventFields(xCrossing, webEvent, pluginLocation); 326 327 xCrossing.type = type; 328 xCrossing.mode = NotifyNormal; 329 xCrossing.detail = NotifyDetailNone; 330 xCrossing.focus = false; 331 } 332 333 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event) 334 { 335 if (m_isWindowed) 336 return false; 337 338 XEvent xEvent; 339 initializeXEvent(xEvent); 340 341 switch (event.type()) { 342 case WebEvent::MouseDown: 343 case WebEvent::MouseUp: 344 setXButtonEventFields(xEvent, event, m_frameRect.location()); 345 break; 346 case WebEvent::MouseMove: 347 setXMotionEventFields(xEvent, event, m_frameRect.location()); 348 break; 349 } 350 351 return NPP_HandleEvent(&xEvent); 352 } 353 354 // We undefine these constants in npruntime_internal.h to avoid collision 355 // with WebKit and platform headers. Values are defined in X.h. 356 const int kKeyPressType = 2; 357 const int kKeyReleaseType = 3; 358 const int kFocusInType = 9; 359 const int kFocusOutType = 10; 360 361 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&) 362 { 363 notImplemented(); 364 return false; 365 } 366 367 void NetscapePlugin::platformSetFocus(bool) 368 { 369 notImplemented(); 370 } 371 372 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event) 373 { 374 if (m_isWindowed) 375 return false; 376 377 XEvent xEvent; 378 initializeXEvent(xEvent); 379 setXCrossingEventFields(xEvent, event, m_frameRect.location(), EnterNotify); 380 381 return NPP_HandleEvent(&xEvent); 382 } 383 384 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event) 385 { 386 if (m_isWindowed) 387 return false; 388 389 XEvent xEvent; 390 initializeXEvent(xEvent); 391 setXCrossingEventFields(xEvent, event, m_frameRect.location(), LeaveNotify); 392 393 return NPP_HandleEvent(&xEvent); 394 } 395 396 static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent) 397 { 398 xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType; 399 XKeyEvent& xKey = xEvent.xkey; 400 xKey.root = rootWindowID(); 401 xKey.subwindow = 0; 402 xKey.time = xTimeStamp(webEvent.timestamp()); 403 xKey.state = xKeyModifiers(webEvent); 404 xKey.keycode = webEvent.nativeVirtualKeyCode(); 405 406 xKey.same_screen = true; 407 408 // Key events propagated to the plugin does not need to have position. 409 // source: https://developer.mozilla.org/en/NPEvent 410 xKey.x = 0; 411 xKey.y = 0; 412 xKey.x_root = 0; 413 xKey.y_root = 0; 414 } 415 416 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event) 417 { 418 // We don't generate other types of keyboard events via WebEventFactory. 419 ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp); 420 421 XEvent xEvent; 422 initializeXEvent(xEvent); 423 setXKeyEventFields(xEvent, event); 424 425 return NPP_HandleEvent(&xEvent); 426 } 427 428 } // namespace WebKit 429 430 #endif // PLUGIN_ARCHITECTURE(X11) 431