Home | History | Annotate | Download | only in x11
      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