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