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 46using namespace WebCore; 47 48namespace WebKit { 49 50static 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 81static 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 92static 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 103static 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 114static 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 125bool 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 164void 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 174bool NetscapePlugin::platformInvalidate(const IntRect&) 175{ 176 notImplemented(); 177 return false; 178} 179 180void 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 196void 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 253static 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 262static inline uint64_t xTimeStamp(double timestampInSeconds) 263{ 264 return timestampInSeconds * 1000; 265} 266 267static 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 282template <typename XEventType> 283static 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 296static 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 303static 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 322static 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 333bool 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. 356const int kKeyPressType = 2; 357const int kKeyReleaseType = 3; 358const int kFocusInType = 9; 359const int kFocusOutType = 10; 360 361bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&) 362{ 363 notImplemented(); 364 return false; 365} 366 367void NetscapePlugin::platformSetFocus(bool) 368{ 369 notImplemented(); 370} 371 372bool 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 384bool 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 396static 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 416bool 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