diff options
Diffstat (limited to 'nsplugins/viewer')
-rw-r--r-- | nsplugins/viewer/Makefile.am | 11 | ||||
-rw-r--r-- | nsplugins/viewer/NSPluginClassIface.h | 83 | ||||
-rw-r--r-- | nsplugins/viewer/glibevents.cpp | 43 | ||||
-rw-r--r-- | nsplugins/viewer/glibevents.h | 41 | ||||
-rw-r--r-- | nsplugins/viewer/kxt.cpp | 629 | ||||
-rw-r--r-- | nsplugins/viewer/kxt.h | 99 | ||||
-rw-r--r-- | nsplugins/viewer/nsplugin.cpp | 1980 | ||||
-rw-r--r-- | nsplugins/viewer/nsplugin.h | 339 | ||||
-rw-r--r-- | nsplugins/viewer/qxteventloop.cpp | 472 | ||||
-rw-r--r-- | nsplugins/viewer/qxteventloop.h | 80 | ||||
-rw-r--r-- | nsplugins/viewer/resolve.h | 46 | ||||
-rw-r--r-- | nsplugins/viewer/viewer.cpp | 312 |
12 files changed, 4135 insertions, 0 deletions
diff --git a/nsplugins/viewer/Makefile.am b/nsplugins/viewer/Makefile.am new file mode 100644 index 000000000..12a0301b9 --- /dev/null +++ b/nsplugins/viewer/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/nsplugins -I$(top_builddir)/nsplugins $(all_includes) `pkg-config --cflags glib-2.0` +METASOURCES = AUTO + +bin_PROGRAMS = nspluginviewer + +nspluginviewer_SOURCES = NSPluginCallbackIface.stub NSPluginClassIface.skel \ + nsplugin.cpp viewer.cpp kxt.cpp qxteventloop.cpp glibevents.cpp +nspluginviewer_LDFLAGS = $(all_libraries) $(KDE_RPATH) -export-dynamic `pkg-config --libs glib-2.0` +nspluginviewer_LDADD = $(LIB_KIO) $(LIB_KPARTS) -lXt + +NSPluginCallbackIface_DIR = $(srcdir)/.. diff --git a/nsplugins/viewer/NSPluginClassIface.h b/nsplugins/viewer/NSPluginClassIface.h new file mode 100644 index 000000000..20fd6ed03 --- /dev/null +++ b/nsplugins/viewer/NSPluginClassIface.h @@ -0,0 +1,83 @@ +/* + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <mhk@caldera.de> + Stefan Schimanski <1Stein@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + + +#ifndef __NSPluginClassIface_h__ +#define __NSPluginClassIface_h__ + + +#include <qstringlist.h> +#include <qcstring.h> +#include <dcopobject.h> +#include <dcopref.h> + + +class NSPluginViewerIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + virtual void shutdown() = 0; + virtual DCOPRef newClass(QString plugin) = 0; +}; + + +class NSPluginClassIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + + virtual DCOPRef newInstance(QString url, QString mimeType, Q_INT8 embed, + QStringList argn, QStringList argv, + QString appId, QString callbackId, Q_INT8 reload, + Q_INT8 doPost, QByteArray postData, Q_UINT32 xembed) = 0; + virtual QString getMIMEDescription() = 0; + +}; + + +class NSPluginInstanceIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + + virtual void shutdown() = 0; + + virtual int winId() = 0; + + virtual int setWindow(Q_INT8 remove=0) = 0; + + virtual void resizePlugin(Q_INT32 w, Q_INT32 h) = 0; + + virtual void javascriptResult(Q_INT32 id, QString result) = 0; + + virtual void displayPlugin() = 0; + + virtual void gotFocusIn() = 0; + virtual void gotFocusOut() = 0; +}; + + +#endif + diff --git a/nsplugins/viewer/glibevents.cpp b/nsplugins/viewer/glibevents.cpp new file mode 100644 index 000000000..b1012675b --- /dev/null +++ b/nsplugins/viewer/glibevents.cpp @@ -0,0 +1,43 @@ +/* + Copyright (c) 2007 Lubos Lunak <l.lunak@suse.cz> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "glibevents.h" + +#include <qapplication.h> + +GlibEvents::GlibEvents() + { + g_main_context_ref( g_main_context_default()); + connect( &timer, SIGNAL( timeout()), SLOT( process())); + // TODO Poll for now + timer.start( 10 ); + } + +GlibEvents::~GlibEvents() + { + g_main_context_unref( g_main_context_default()); + } + +void GlibEvents::process() + { + while( g_main_context_pending( g_main_context_default())) + g_main_context_iteration( g_main_context_default(), false ); + } + +#include "glibevents.moc" diff --git a/nsplugins/viewer/glibevents.h b/nsplugins/viewer/glibevents.h new file mode 100644 index 000000000..8a890807b --- /dev/null +++ b/nsplugins/viewer/glibevents.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2007 Lubos Lunak <l.lunak@suse.cz> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef GLIBEVENTS_H +#define GLIBEVENTS_H + +#include <qwidget.h> +#include <qtimer.h> + +#include <glib.h> + +class GlibEvents + : public QWidget + { + Q_OBJECT + public: + GlibEvents(); + virtual ~GlibEvents(); + private slots: + void process(); + private: + QTimer timer; + }; + +#endif diff --git a/nsplugins/viewer/kxt.cpp b/nsplugins/viewer/kxt.cpp new file mode 100644 index 000000000..7f2e04b54 --- /dev/null +++ b/nsplugins/viewer/kxt.cpp @@ -0,0 +1,629 @@ +/* + + kxt.cpp - Xt enabled Qt classed (derived from Qt Extension QXt) + + Copyright (c) 2000,2001 Stefan Schimanski <1Stein@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +/**************************************************************************** +** +** Implementation of Qt extension classes for Xt/Motif support. +** +** Created : 980107 +** +** Copyright (C) 1992-2000 Troll Tech AS. All rights reserved. +** +** This file is part of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Troll Tech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** Licensees holding valid Qt Professional Edition licenses may use this +** file in accordance with the Qt Professional Edition License Agreement +** provided with the Qt Professional Edition. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about the Professional Edition licensing, or see +** http://www.trolltech.com/qpl/ for QPL licensing information. +** +*****************************************************************************/ + +#include <qglobal.h> +#if QT_VERSION < 0x030100 + +#include <kapplication.h> +#include <qwidget.h> +#include <qobjectlist.h> +#include <qwidgetlist.h> +#include <kdebug.h> +#include <qtimer.h> + +#include "kxt.h" + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <limits.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/IntrinsicP.h> // for XtCreateWindow +#include <X11/Shell.h> +#include <X11/StringDefs.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> + +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyPress +#undef KeyRelease + +extern Atom qt_wm_state; + +//#define HAVE_MOTIF +#ifdef HAVE_MOTIF +#include <Xm/Xm.h> +#endif + +typedef void (*SameAsXtTimerCallbackProc)(void*,void*); +typedef void (*IntervalSetter)(int); +typedef void (*ForeignEventProc)(XEvent*); + +extern XtEventDispatchProc + qt_np_cascade_event_handler[LASTEvent]; // defined in qnpsupport.cpp +void qt_reset_color_avail(); // defined in qcolor_x11.cpp +int qt_activate_timers(); // defined in qapplication_x11.cpp +timeval *qt_wait_timer(); // defined in qapplication_x11.cpp +void qt_x11SendPostedEvents(); // defined in qapplication_x11.cpp +int qt_event_handler( XEvent* event ); // defined in qnpsupport.cpp +extern int qt_np_count; // defined in qnpsupport.cpp +void qt_np_timeout( void* p, void* id ); // defined in qnpsupport.cpp +void qt_np_add_timeoutcb( + SameAsXtTimerCallbackProc cb ); // defined in qnpsupport.cpp +void qt_np_remove_timeoutcb( + SameAsXtTimerCallbackProc cb ); // defined in qnpsupport.cpp +void qt_np_add_timer_setter( + IntervalSetter is ); // defined in qnpsupport.cpp +void qt_np_remove_timer_setter( + IntervalSetter is ); // defined in qnpsupport.cpp +extern XtIntervalId qt_np_timerid; // defined in qnpsupport.cpp +extern void (*qt_np_leave_cb) + (XLeaveWindowEvent*); // defined in qnpsupport.cpp +void qt_np_add_event_proc( + ForeignEventProc fep ); // defined in qnpsupport.cpp +void qt_np_remove_event_proc( + ForeignEventProc fep ); // defined in qnpsupport.cpp + + +typedef struct { + int empty; +} QWidgetClassPart; + +typedef struct _QWidgetClassRec { + CoreClassPart core_class; + QWidgetClassPart qwidget_class; +} QWidgetClassRec; + +//static QWidgetClassRec qwidgetClassRec; + +typedef struct { + /* resources */ + /* (none) */ + /* private state */ + KXtWidget* qxtwidget; +} QWidgetPart; + +typedef struct _QWidgetRec { + CorePart core; + QWidgetPart qwidget; +} QWidgetRec; + + +static +void reparentChildrenOf(QWidget* parent) +{ + + if ( !parent->children() ) + return; // nothing to do + + for ( QObjectListIt it( *parent->children() ); it.current(); ++it ) { + if ( it.current()->isWidgetType() ) { + QWidget* widget = (QWidget*)it.current(); + XReparentWindow( qt_xdisplay(), + widget->winId(), + parent->winId(), + widget->x(), + widget->y() ); + if ( widget->isVisible() ) + XMapWindow( qt_xdisplay(), widget->winId() ); + } + } + +} + +void qwidget_realize( + Widget widget, + XtValueMask* mask, + XSetWindowAttributes* attributes + ) +{ + widgetClassRec.core_class.realize(widget, mask, attributes); + KXtWidget* qxtw = ((QWidgetRec*)widget)->qwidget.qxtwidget; + if (XtWindow(widget) != qxtw->winId()) { + qxtw->create(XtWindow(widget), FALSE, FALSE); + reparentChildrenOf(qxtw); + } + qxtw->show(); + XMapWindow( qt_xdisplay(), qxtw->winId() ); +} + +static +QWidgetClassRec qwidgetClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &widgetClassRec, + /* class_name */ (char*)"QWidget", + /* widget_size */ sizeof(QWidgetRec), + /* class_initialize */ 0, + /* class_part_initialize */ 0, + /* class_inited */ FALSE, + /* initialize */ 0, + /* initialize_hook */ 0, + /* realize */ qwidget_realize, + /* actions */ 0, + /* num_actions */ 0, + /* resources */ 0, + /* num_resources */ 0, + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ 0, + /* resize */ XtInheritResize, + /* expose */ XtInheritExpose, + /* set_values */ 0, + /* set_values_hook */ 0, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ 0, + /* accept_focus */ XtInheritAcceptFocus, + /* version */ XtVersion, + /* callback_private */ 0, + /* tm_table */ XtInheritTranslations, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ 0 + }, + { /* qwidget fields */ + /* empty */ 0 + } +}; +static WidgetClass qWidgetClass = (WidgetClass)&qwidgetClassRec; + +static bool filters_installed = FALSE; +static KXtApplication* qxtapp = 0; +static XtAppContext appcon; + +static +Boolean qt_event_handler_wrapper( XEvent* event ) +{ + return (Boolean)qt_event_handler( event ); +} + +static +void installXtEventFilters() +{ + if (filters_installed) return; + // Get Xt out of our face - install filter on every event type + for (int et=2; et < LASTEvent; et++) { + qt_np_cascade_event_handler[et] = XtSetEventDispatcher( + qt_xdisplay(), et, qt_event_handler_wrapper ); + } + filters_installed = TRUE; +} + +static +void removeXtEventFilters() +{ + if (!filters_installed) return; + // We aren't needed any more... slink back into the shadows. + for (int et=2; et < LASTEvent; et++) { + XtSetEventDispatcher( + qt_xdisplay(), et, qt_np_cascade_event_handler[et] ); + } + filters_installed = FALSE; +} + +// When we are in an event loop of QApplication rather than the browser's +// event loop (eg. for a modal dialog), we still send events to Xt. +static +void np_event_proc( XEvent* e ) +{ + Widget xtw = XtWindowToWidget( e->xany.display, e->xany.window ); + if ( xtw && qApp->loopLevel() > 0 ) { + // Allow Xt to process the event + qt_np_cascade_event_handler[e->type]( e ); + } +} + +static void np_set_timer( int interval ) +{ + // Ensure we only have one timeout in progress - QApplication is + // computing the one amount of time we need to wait. + if ( qt_np_timerid ) { + XtRemoveTimeOut( qt_np_timerid ); + } + qt_np_timerid = XtAppAddTimeOut(appcon, interval, + (XtTimerCallbackProc)qt_np_timeout, 0); +} + +static void np_do_timers( void*, void* ) +{ + qt_np_timerid = 0; // It's us, and we just expired, that's why we are here. + + qt_activate_timers(); + + timeval *tm = qt_wait_timer(); + + if (tm) { + int interval = QMIN(tm->tv_sec,INT_MAX/1000)*1000 + tm->tv_usec/1000; + np_set_timer( interval ); + } + qxtapp->sendPostedEvents(); +} + +/*! + \class KXtApplication qxt.h + \brief Allows mixing of Xt/Motif and Qt widgets. + + \extension Xt/Motif + + The KXtApplication and KXtWidget classes allow old Xt or Motif widgets + to be used in new Qt applications. They also allow Qt widgets to + be used in primarily Xt/Motif applications. The facility is intended + to aid migration from Xt/Motif to the more comfortable Qt system. +*/ + +static bool my_xt; + +/*! + Constructs a QApplication and initializes the Xt toolkit. + The \a appclass, \a options, \a num_options, and \a resources + arguments are passed on to XtAppSetFallbackResources and + XtDisplayInitialize. + + Use this constructor when writing a new Qt application which + needs to use some existing Xt/Motif widgets. +*/ +KXtApplication::KXtApplication(int& argc, char** argv, + const QCString& rAppName, bool allowStyles, bool GUIenabled, + XrmOptionDescRec *options, int num_options, + char** resources) + : KApplication(argc, argv, rAppName, allowStyles, GUIenabled) +{ + my_xt = TRUE; + + XtToolkitInitialize(); + appcon = XtCreateApplicationContext(); + if (resources) XtAppSetFallbackResources(appcon, (char**)resources); + XtDisplayInitialize(appcon, qt_xdisplay(), name(), rAppName, options, + num_options, &argc, argv); + init(); +} + +/*! + Constructs a QApplication from the \a display of an already-initialized + Xt application. + + Use this constructor when introducing Qt widgets into an existing + Xt/Motif application. +*/ +KXtApplication::KXtApplication(Display* dpy, int& argc, char** argv, + const QCString& rAppName, bool allowStyles, bool GUIenabled) + : KApplication(dpy, argc, argv, rAppName, allowStyles, GUIenabled) +{ + my_xt = FALSE; + init(); + appcon = XtDisplayToApplicationContext(dpy); +} + +/*! + Destructs the application. Does not close the Xt toolkit. +*/ +KXtApplication::~KXtApplication() +{ + Q_ASSERT(qxtapp==this); + removeXtEventFilters(); + qxtapp = 0; + + // the manpage says: "or just exit", that's what we do to avoid + // double closing of the display +// if (my_xt) { +// XtDestroyApplicationContext(appcon); +// } +} + +void KXtApplication::init() +{ + Q_ASSERT(qxtapp==0); + qxtapp = this; + installXtEventFilters(); + qt_np_add_timeoutcb(np_do_timers); + qt_np_add_timer_setter(np_set_timer); + qt_np_add_event_proc(np_event_proc); + qt_np_count++; +/* QTimer *timer = new QTimer( this ); + timer->start(500);*/ +} + +/*! + \class KXtWidget qxt.h + \brief Allows mixing of Xt/Motif and Qt widgets. + + \extension Xt/Motif + + KXtWidget acts as a bridge between Xt and Qt. For utilizing old + Xt widgets, it can be a QWidget + based on a Xt widget class. For including Qt widgets in an existing + Xt/Motif application, it can be a special Xt widget class that is + a QWidget. See the constructors for the different behaviors. +*/ + +void KXtWidget::init(const char* name, WidgetClass widget_class, + Widget parent, QWidget* qparent, + ArgList args, Cardinal num_args, + bool managed) +{ + need_reroot=FALSE; + xtparent = 0; + if (parent ) { + Q_ASSERT(!qparent); + xtw = XtCreateWidget(name, widget_class, parent, args, num_args); + if ( widget_class == qWidgetClass ) + ((QWidgetRec*)xtw)->qwidget.qxtwidget = this; + xtparent = parent; + if (managed) + XtManageChild(xtw); + } else { + Q_ASSERT(!managed); + + String n, c; + XtGetApplicationNameAndClass(qt_xdisplay(), &n, &c); + xtw = XtAppCreateShell(n, c, widget_class, qt_xdisplay(), + args, num_args); + if ( widget_class == qWidgetClass ) + ((QWidgetRec*)xtw)->qwidget.qxtwidget = this; + } + + if ( qparent ) { + XtResizeWidget( xtw, 100, 100, 0 ); + XtSetMappedWhenManaged(xtw, False); + XtRealizeWidget(xtw); + XSync(qt_xdisplay(), False); // I want all windows to be created now + XReparentWindow(qt_xdisplay(), XtWindow(xtw), qparent->winId(), x(), y()); + XtSetMappedWhenManaged(xtw, True); + need_reroot=TRUE; + } + + Arg reqargs[20]; + Cardinal nargs=0; + XtSetArg(reqargs[nargs], XtNx, x()); nargs++; + XtSetArg(reqargs[nargs], XtNy, y()); nargs++; + XtSetArg(reqargs[nargs], XtNwidth, width()); nargs++; + XtSetArg(reqargs[nargs], XtNheight, height()); nargs++; + //XtSetArg(reqargs[nargs], "mappedWhenManaged", False); nargs++; + XtSetValues(xtw, reqargs, nargs); + + //#### destroy(); MLK + + if (!parent || XtIsRealized(parent)) + XtRealizeWidget(xtw); +} + + +/*! + Constructs a KXtWidget of the special Xt widget class known as + "QWidget" to the resource manager. + + Use this constructor to utilize Qt widgets in an Xt/Motif + application. The KXtWidget is a QWidget, so you can create + subwidgets, layouts, etc. using Qt functionality. +*/ +KXtWidget::KXtWidget(const char* name, Widget parent, bool managed) : + QWidget( 0, name, WResizeNoErase ) +{ + init(name, qWidgetClass, parent, 0, 0, 0, managed); + Arg reqargs[20]; + Cardinal nargs=0; + XtSetArg(reqargs[nargs], XtNborderWidth, 0); + nargs++; + XtSetValues(xtw, reqargs, nargs); +} + +/*! + Constructs a KXtWidget of the given \a widget_class. + + Use this constructor to utilize Xt or Motif widgets in a Qt + application. The KXtWidget looks and behaves + like the Xt class, but can be used like any QWidget. + + Note that Xt requires that the most toplevel Xt widget is a shell. + That means, if \a parent is a KXtWidget, the \a widget_class can be + of any kind. If there isn't a parent or the parent is just a normal + QWidget, \a widget_class should be something like \c + topLevelShellWidgetClass. + + If the \a managed parameter is TRUE and \a parent in not NULL, + XtManageChild it used to manage the child. +*/ +KXtWidget::KXtWidget(const char* name, WidgetClass widget_class, + QWidget *parent, ArgList args, Cardinal num_args, + bool managed) : + QWidget( parent, name, WResizeNoErase ) +{ + if ( !parent ) + init(name, widget_class, 0, 0, args, num_args, managed); + else if ( parent->inherits("KXtWidget") ) + init(name, widget_class, ( (KXtWidget*)parent)->xtw , 0, args, num_args, managed); + else + init(name, widget_class, 0, parent, args, num_args, managed); + create(XtWindow(xtw), FALSE, FALSE); +} + +/*! + Destructs the KXtWidget. +*/ +KXtWidget::~KXtWidget() +{ + // Delete children first, as Xt will destroy their windows + // + QObjectList* list = queryList("QWidget", 0, FALSE, FALSE); + if ( list ) { + QWidget* c; + QObjectListIt it( *list ); + while ( (c = (QWidget*)it.current()) ) { + delete c; + ++it; + } + delete list; + } + + if ( need_reroot ) { + hide(); + XReparentWindow(qt_xdisplay(), winId(), qApp->desktop()->winId(), + x(), y()); + } + + XtDestroyWidget(xtw); + destroy( FALSE, FALSE ); +} + +/*! + \fn Widget KXtWidget::xtWidget() const + Returns the Xt widget equivalent for the Qt widget. +*/ + + + +/*! + Reimplemented to produce the Xt effect of getting focus when the + mouse enters the widget. <em>This may be changed.</em> +*/ +bool KXtWidget::x11Event( XEvent * e ) +{ + if ( e->type == EnterNotify ) { + if ( xtparent ) + setActiveWindow(); + } + return QWidget::x11Event( e ); +} + + +/*! + Implement a degree of focus handling for Xt widgets. +*/ +void KXtWidget::setActiveWindow() +{ + if ( xtparent ) { + if ( !QWidget::isActiveWindow() && isActiveWindow() ) { + XFocusChangeEvent e; + e.type = FocusIn; + e.window = winId(); + e.mode = NotifyNormal; + e.detail = NotifyInferior; + XSendEvent( qt_xdisplay(), e.window, TRUE, NoEventMask, (XEvent*)&e ); + } + } else { + QWidget::setActiveWindow(); + } +} + +/*! + Different from QWidget::isActiveWindow() + */ +bool KXtWidget::isActiveWindow() const +{ + Window win; + int revert; + XGetInputFocus( qt_xdisplay(), &win, &revert ); + + if ( win == None) return FALSE; + + QWidget *w = find( (WId)win ); + if ( w ) { + // We know that window + return w->topLevelWidget() == topLevelWidget(); + } else { + // Window still may be a parent (if top-level is foreign window) + Window root, parent; + Window cursor = winId(); + Window *ch; + unsigned int nch; + while ( XQueryTree(qt_xdisplay(), cursor, &root, &parent, &ch, &nch) ) { + if (ch) XFree( (char*)ch); + if ( parent == win ) return TRUE; + if ( parent == root ) return FALSE; + cursor = parent; + } + return FALSE; + } +} + +/*!\reimp + */ +void KXtWidget::moveEvent( QMoveEvent* ) +{ + if ( xtparent ) + return; + XConfigureEvent c; + c.type = ConfigureNotify; + c.event = winId(); + c.window = winId(); + c.x = x(); + c.y = y(); + c.width = width(); + c.height = height(); + c.border_width = 0; + XSendEvent( qt_xdisplay(), c.event, TRUE, NoEventMask, (XEvent*)&c ); + XtMoveWidget( xtw, x(), y() ); +} + +/*!\reimp + */ +void KXtWidget::resizeEvent( QResizeEvent* ) +{ + if ( xtparent ) + return; + XtWidgetGeometry preferred; + (void ) XtQueryGeometry( xtw, 0, &preferred ); + XConfigureEvent c; + c.type = ConfigureNotify; + c.event = winId(); + c.window = winId(); + c.x = x(); + c.y = y(); + c.width = width(); + c.height = height(); + c.border_width = 0; + XSendEvent( qt_xdisplay(), c.event, TRUE, NoEventMask, (XEvent*)&c ); + XtResizeWidget( xtw, width(), height(), preferred.border_width ); +} + +#include "kxt.moc" + +#endif diff --git a/nsplugins/viewer/kxt.h b/nsplugins/viewer/kxt.h new file mode 100644 index 000000000..1d594b328 --- /dev/null +++ b/nsplugins/viewer/kxt.h @@ -0,0 +1,99 @@ +/* + + kxt.h - Xt enabled Qt classed (derived from Qt Extension QXt) + + Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +/**************************************************************************** +** +** Definition of Qt extension classes for Xt/Motif support. +** +** Created : 980107 +** +** Copyright (C) 1992-2000 Troll Tech AS. All rights reserved. +** +** This file is part of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Troll Tech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** Licensees holding valid Qt Professional Edition licenses may use this +** file in accordance with the Qt Professional Edition License Agreement +** provided with the Qt Professional Edition. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about the Professional Edition licensing, or see +** http://www.trolltech.com/qpl/ for QPL licensing information. +** +*****************************************************************************/ +#ifndef KXT_H +#define KXT_H + +#include <qglobal.h> +#if QT_VERSION < 0x030100 + +#include <kapplication.h> +#include <qwidget.h> +#include <X11/Intrinsic.h> + +class KXtApplication : public KApplication { + Q_OBJECT + void init(); + +public: + KXtApplication(int& argc, char** argv, + const QCString& rAppName, bool allowStyles=true, bool GUIenabled=true, + XrmOptionDescRec *options=0, int num_options=0, char** resources=0); + KXtApplication(Display*, int& argc, char** argv, const QCString& rAppName, + bool allowStyles=true, bool GUIenabled=true); + ~KXtApplication(); +}; + +class KXtWidget : public QWidget { + Q_OBJECT + Widget xtw; + Widget xtparent; + bool need_reroot; + void init(const char* name, WidgetClass widget_class, + Widget parent, QWidget* qparent, + ArgList args, Cardinal num_args, + bool managed); + friend void qwidget_realize( Widget widget, XtValueMask* mask, + XSetWindowAttributes* attributes ); + +public: + KXtWidget(const char* name, Widget parent, bool managed=FALSE); + KXtWidget(const char* name, WidgetClass widget_class, + QWidget *parent=0, ArgList args=0, Cardinal num_args=0, + bool managed=FALSE); + ~KXtWidget(); + + Widget xtWidget() const { return xtw; } + bool isActiveWindow() const; + void setActiveWindow(); + +protected: + void moveEvent( QMoveEvent* ); + void resizeEvent( QResizeEvent* ); + bool x11Event( XEvent * ); +}; + +#endif +#endif diff --git a/nsplugins/viewer/nsplugin.cpp b/nsplugins/viewer/nsplugin.cpp new file mode 100644 index 000000000..cb39aa107 --- /dev/null +++ b/nsplugins/viewer/nsplugin.cpp @@ -0,0 +1,1980 @@ +/* + + This is an encapsulation of the Netscape plugin API. + + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Stefan Schimanski <1Stein@gmx.de> + 2003-2005 George Staikos <staikos@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#include "NSPluginCallbackIface_stub.h" + + +#include <stdlib.h> +#include <unistd.h> + +#include <qdict.h> +#include <qdir.h> +#include <qfile.h> +#include <qtimer.h> + +#include "kxt.h" +#include "nsplugin.h" +#include "resolve.h" + +#ifdef Bool +#undef Bool +#endif + +#include <dcopclient.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kio/netaccess.h> +#include <klibloader.h> +#include <klocale.h> +#include <kprocess.h> +#include <kprotocolmanager.h> +#include <kstandarddirs.h> +#include <ktempfile.h> +#include <kurl.h> + +#include <X11/Intrinsic.h> +#include <X11/Composite.h> +#include <X11/Constraint.h> +#include <X11/Shell.h> +#include <X11/StringDefs.h> + +// provide these symbols when compiling with gcc 3.x + +#if defined __GNUC__ && defined __GNUC_MINOR__ +# define KDE_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define KDE_GNUC_PREREQ(maj, min) 0 +#endif + + +#if defined(__GNUC__) && KDE_GNUC_PREREQ(3,0) +extern "C" void* __builtin_new(size_t s) +{ + return operator new(s); +} + +extern "C" void __builtin_delete(void* p) +{ + operator delete(p); +} + +extern "C" void* __builtin_vec_new(size_t s) +{ + return operator new[](s); +} + +extern "C" void __builtin_vec_delete(void* p) +{ + operator delete[](p); +} + +extern "C" void __pure_virtual() +{ + abort(); +} +#endif + +// server side functions ----------------------------------------------------- + +// allocate memory +void *g_NPN_MemAlloc(uint32 size) +{ + void *mem = ::malloc(size); + + //kdDebug(1431) << "g_NPN_MemAlloc(), size=" << size << " allocated at " << mem << endl; + + return mem; +} + + +// free memory +void g_NPN_MemFree(void *ptr) +{ + //kdDebug(1431) << "g_NPN_MemFree() at " << ptr << endl; + if (ptr) + ::free(ptr); +} + +uint32 g_NPN_MemFlush(uint32 size) +{ + Q_UNUSED(size); + //kdDebug(1431) << "g_NPN_MemFlush()" << endl; + // MAC OS only.. we don't use this + return 0; +} + + +// redraw +void g_NPN_ForceRedraw(NPP /*instance*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api3.html#999401 + // FIXME + kdDebug(1431) << "g_NPN_ForceRedraw() [unimplemented]" << endl; +} + + +// invalidate rect +void g_NPN_InvalidateRect(NPP /*instance*/, NPRect* /*invalidRect*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api7.html#999503 + // FIXME + kdDebug(1431) << "g_NPN_InvalidateRect() [unimplemented]" << endl; +} + + +// invalidate region +void g_NPN_InvalidateRegion(NPP /*instance*/, NPRegion /*invalidRegion*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api8.html#999528 + // FIXME + kdDebug(1431) << "g_NPN_InvalidateRegion() [unimplemented]" << endl; +} + + +// get value +NPError g_NPN_GetValue(NPP /*instance*/, NPNVariable variable, void *value) +{ + kdDebug(1431) << "g_NPN_GetValue(), variable=" << static_cast<int>(variable) << endl; + + switch (variable) + { + case NPNVxDisplay: + *(void**)value = qt_xdisplay(); + return NPERR_NO_ERROR; + case NPNVxtAppContext: + *(void**)value = XtDisplayToApplicationContext(qt_xdisplay()); + return NPERR_NO_ERROR; + case NPNVjavascriptEnabledBool: + *(bool*)value = true; + return NPERR_NO_ERROR; + case NPNVasdEnabledBool: + // SmartUpdate - we don't do this + *(bool*)value = false; + return NPERR_NO_ERROR; + case NPNVisOfflineBool: + // Offline browsing - no thanks + *(bool*)value = false; + return NPERR_NO_ERROR; + case NPNVToolkit: + *(NPNToolkitType*)value = NPNVGtk2; + return NPERR_NO_ERROR; + case NPNVSupportsXEmbedBool: + *(bool*)value = true; + return NPERR_NO_ERROR; + default: + return NPERR_INVALID_PARAM; + } +} + + +NPError g_NPN_DestroyStream(NPP instance, NPStream* stream, + NPReason reason) +{ + // FIXME: is this correct? I imagine it is not. (GS) + kdDebug(1431) << "g_NPN_DestroyStream()" << endl; + + NSPluginInstance *inst = (NSPluginInstance*) instance->ndata; + inst->streamFinished( (NSPluginStream *)stream->ndata ); + + switch (reason) { + case NPRES_DONE: + return NPERR_NO_ERROR; + case NPRES_USER_BREAK: + // FIXME: notify the user + case NPRES_NETWORK_ERR: + // FIXME: notify the user + default: + return NPERR_GENERIC_ERROR; + } +} + + +NPError g_NPN_RequestRead(NPStream* /*stream*/, NPByteRange* /*rangeList*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api16.html#999734 + kdDebug(1431) << "g_NPN_RequestRead() [unimplemented]" << endl; + + // FIXME + return NPERR_GENERIC_ERROR; +} + +NPError g_NPN_NewStream(NPP /*instance*/, NPMIMEType /*type*/, + const char* /*target*/, NPStream** /*stream*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api12.html#999628 + kdDebug(1431) << "g_NPN_NewStream() [unimplemented]" << endl; + + // FIXME + // This creates a stream from the plugin to the browser of type "type" to + // display in "target" + return NPERR_GENERIC_ERROR; +} + +int32 g_NPN_Write(NPP /*instance*/, NPStream* /*stream*/, int32 /*len*/, void* /*buf*/) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api21.html#999859 + kdDebug(1431) << "g_NPN_Write() [unimplemented]" << endl; + + // FIXME + return 0; +} + + +// URL functions +NPError g_NPN_GetURL(NPP instance, const char *url, const char *target) +{ + kdDebug(1431) << "g_NPN_GetURL: url=" << url << " target=" << target << endl; + + NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); + if (inst) { + inst->requestURL( QString::fromLatin1(url), QString::null, + QString::fromLatin1(target), 0 ); + } + + return NPERR_NO_ERROR; +} + + +NPError g_NPN_GetURLNotify(NPP instance, const char *url, const char *target, + void* notifyData) +{ + kdDebug(1431) << "g_NPN_GetURLNotify: url=" << url << " target=" << target << " inst=" << (void*)instance << endl; + NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); + if (inst) { + kdDebug(1431) << "g_NPN_GetURLNotify: ndata=" << (void*)inst << endl; + inst->requestURL( QString::fromLatin1(url), QString::null, + QString::fromLatin1(target), notifyData, true ); + } + + return NPERR_NO_ERROR; +} + + +NPError g_NPN_PostURLNotify(NPP instance, const char* url, const char* target, + uint32 len, const char* buf, NPBool file, void* notifyData) +{ +// http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api14.html + kdDebug(1431) << "g_NPN_PostURLNotify() [incomplete]" << endl; + kdDebug(1431) << "url=[" << url << "] target=[" << target << "]" << endl; + QByteArray postdata; + KParts::URLArgs args; + + if (len == 0) { + return NPERR_NO_DATA; + } + + if (file) { // buf is a filename + QFile f(buf); + if (!f.open(IO_ReadOnly)) { + return NPERR_FILE_NOT_FOUND; + } + + // FIXME: this will not work because we need to strip the header out! + postdata = f.readAll(); + f.close(); + } else { // buf is raw data + // First strip out the header + const char *previousStart = buf; + uint32 l; + bool previousCR = true; + + for (l = 1;; l++) { + if (l == len) { + break; + } + + if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) { + if (previousCR) { // header is done! + if ((buf[l-1] == '\r' && buf[l] == '\n') || + (buf[l-1] == '\n' && buf[l] == '\r')) + l++; + l++; + previousStart = &buf[l-1]; + break; + } + + QString thisLine = QString::fromLatin1(previousStart, &buf[l-1] - previousStart).stripWhiteSpace(); + + previousStart = &buf[l]; + previousCR = true; + + kdDebug(1431) << "Found header line: [" << thisLine << "]" << endl; + if (thisLine.startsWith("Content-Type: ")) { + args.setContentType(thisLine); + } + } else { + previousCR = false; + } + } + + postdata.duplicate(previousStart, len - l + 1); + } + + kdDebug(1431) << "Post data: " << postdata.size() << " bytes" << endl; +#if 0 + QFile f("/tmp/nspostdata"); + f.open(IO_WriteOnly); + f.writeBlock(postdata); + f.close(); +#endif + + if (!target || !*target) { + // Send the results of the post to the plugin + // (works by default) + } else if (!strcmp(target, "_current") || !strcmp(target, "_self") || + !strcmp(target, "_top")) { + // Unload the plugin, put the results in the frame/window that the + // plugin was loaded in + // FIXME + } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){ + // Open a new browser window and write the results there + // FIXME + } else { + // Write the results to the specified frame + // FIXME + } + + NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); + if (inst && !inst->normalizedURL(QString::fromLatin1(url)).isNull()) { + inst->postURL( QString::fromLatin1(url), postdata, args.contentType(), + QString::fromLatin1(target), notifyData, args, true ); + } else { + // Unsupported / insecure + return NPERR_INVALID_URL; + } + + return NPERR_NO_ERROR; +} + + +NPError g_NPN_PostURL(NPP instance, const char* url, const char* target, + uint32 len, const char* buf, NPBool file) +{ +// http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api13.html + kdDebug(1431) << "g_NPN_PostURL()" << endl; + kdDebug(1431) << "url=[" << url << "] target=[" << target << "]" << endl; + QByteArray postdata; + KParts::URLArgs args; + + if (len == 0) { + return NPERR_NO_DATA; + } + + if (file) { // buf is a filename + QFile f(buf); + if (!f.open(IO_ReadOnly)) { + return NPERR_FILE_NOT_FOUND; + } + + // FIXME: this will not work because we need to strip the header out! + postdata = f.readAll(); + f.close(); + } else { // buf is raw data + // First strip out the header + const char *previousStart = buf; + uint32 l; + bool previousCR = true; + + for (l = 1;; l++) { + if (l == len) { + break; + } + + if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) { + if (previousCR) { // header is done! + if ((buf[l-1] == '\r' && buf[l] == '\n') || + (buf[l-1] == '\n' && buf[l] == '\r')) + l++; + l++; + previousStart = &buf[l-1]; + break; + } + + QString thisLine = QString::fromLatin1(previousStart, &buf[l-1] - previousStart).stripWhiteSpace(); + + previousStart = &buf[l]; + previousCR = true; + + kdDebug(1431) << "Found header line: [" << thisLine << "]" << endl; + if (thisLine.startsWith("Content-Type: ")) { + args.setContentType(thisLine); + } + } else { + previousCR = false; + } + } + + postdata.duplicate(previousStart, len - l + 1); + } + + kdDebug(1431) << "Post data: " << postdata.size() << " bytes" << endl; +#if 0 + QFile f("/tmp/nspostdata"); + f.open(IO_WriteOnly); + f.writeBlock(postdata); + f.close(); +#endif + + if (!target || !*target) { + // Send the results of the post to the plugin + // (works by default) + } else if (!strcmp(target, "_current") || !strcmp(target, "_self") || + !strcmp(target, "_top")) { + // Unload the plugin, put the results in the frame/window that the + // plugin was loaded in + // FIXME + } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){ + // Open a new browser window and write the results there + // FIXME + } else { + // Write the results to the specified frame + // FIXME + } + + NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); + if (inst && !inst->normalizedURL(QString::fromLatin1(url)).isNull()) { + inst->postURL( QString::fromLatin1(url), postdata, args.contentType(), + QString::fromLatin1(target), 0L, args, false ); + } else { + // Unsupported / insecure + return NPERR_INVALID_URL; + } + + return NPERR_NO_ERROR; +} + + +// display status message +void g_NPN_Status(NPP instance, const char *message) +{ + kdDebug(1431) << "g_NPN_Status(): " << message << endl; + + if (!instance) + return; + + // turn into an instance signal + NSPluginInstance *inst = (NSPluginInstance*) instance->ndata; + + inst->emitStatus(message); +} + + +// inquire user agent +const char *g_NPN_UserAgent(NPP /*instance*/) +{ + KProtocolManager kpm; + QString agent = kpm.userAgentForHost("nspluginviewer"); + kdDebug(1431) << "g_NPN_UserAgent() = " << agent << endl; + // flash crashes without Firefox UA + agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/2007101500 Firefox/2.0.0.10"; + return agent.latin1(); +} + + +// inquire version information +void g_NPN_Version(int *plugin_major, int *plugin_minor, int *browser_major, int *browser_minor) +{ + kdDebug(1431) << "g_NPN_Version()" << endl; + + // FIXME: Use the sensible values + *browser_major = NP_VERSION_MAJOR; + *browser_minor = NP_VERSION_MINOR; + + *plugin_major = NP_VERSION_MAJOR; + *plugin_minor = NP_VERSION_MINOR; +} + + +void g_NPN_ReloadPlugins(NPBool reloadPages) +{ + // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api15.html#999713 + kdDebug(1431) << "g_NPN_ReloadPlugins()" << endl; + KProcess p; + p << KGlobal::dirs()->findExe("nspluginscan"); + + if (reloadPages) { + // This is the proper way, but it cannot be done because we have no + // handle to the caller! How stupid! We cannot force all konqi windows + // to reload - that would be evil. + //p.start(KProcess::Block); + // Let's only allow the caller to be reloaded, not everything. + //if (_callback) + // _callback->reloadPage(); + p.start(KProcess::DontCare); + } else { + p.start(KProcess::DontCare); + } +} + + +// JAVA functions +JRIEnv *g_NPN_GetJavaEnv() +{ + kdDebug(1431) << "g_NPN_GetJavaEnv() [unimplemented]" << endl; + // FIXME - what do these do? I can't find docs, and even Mozilla doesn't + // implement them + return 0; +} + + +jref g_NPN_GetJavaPeer(NPP /*instance*/) +{ + kdDebug(1431) << "g_NPN_GetJavaPeer() [unimplemented]" << endl; + // FIXME - what do these do? I can't find docs, and even Mozilla doesn't + // implement them + return 0; +} + + +NPError g_NPN_SetValue(NPP /*instance*/, NPPVariable variable, void* /*value*/) +{ + kdDebug(1431) << "g_NPN_SetValue() [unimplemented]" << endl; + switch (variable) { + case NPPVpluginWindowBool: + // FIXME + // If true, the plugin is windowless. If false, it is in a window. + case NPPVpluginTransparentBool: + // FIXME + // If true, the plugin is displayed transparent + default: + return NPERR_GENERIC_ERROR; + } +} + + + + + +/******************************************************************/ + +void +NSPluginInstance::forwarder(Widget w, XtPointer cl_data, XEvent * event, Boolean * cont) +{ + Q_UNUSED(w); + NSPluginInstance *inst = (NSPluginInstance*)cl_data; + *cont = True; + if (inst->_form == 0 || event->xkey.window == XtWindow(inst->_form)) + return; + *cont = False; + event->xkey.window = XtWindow(inst->_form); + event->xkey.subwindow = None; + XtDispatchEvent(event); +} + + +NSPluginInstance::NSPluginInstance(NPP privateData, NPPluginFuncs *pluginFuncs, + KLibrary *handle, int width, int height, + QString src, QString /*mime*/, + QString appId, QString callbackId, + bool embed, WId xembed, + QObject *parent, const char* name ) + : DCOPObject(), QObject( parent, name ) +{ + Q_UNUSED(embed); + _visible = false; + _npp = privateData; + _npp->ndata = this; + _destroyed = false; + _handle = handle; + _width = width; + _height = height; + _tempFiles.setAutoDelete( true ); + _streams.setAutoDelete( true ); + _waitingRequests.setAutoDelete( true ); + _callback = new NSPluginCallbackIface_stub( appId.latin1(), callbackId.latin1() ); + _xembed_window = xembed; + _toplevel = _form = 0; + + KURL base(src); + base.setFileName( QString::null ); + _baseURL = base.url(); + + memcpy(&_pluginFuncs, pluginFuncs, sizeof(_pluginFuncs)); + + _timer = new QTimer( this ); + connect( _timer, SIGNAL(timeout()), SLOT(timer()) ); + + kdDebug(1431) << "NSPluginInstance::NSPluginInstance" << endl; + kdDebug(1431) << "pdata = " << _npp->pdata << endl; + kdDebug(1431) << "ndata = " << _npp->ndata << endl; + + if (width == 0) + width = 1600; + + if (height == 0) + height = 1200; + + if( _xembed_window == 0 ) { + // create drawing area + Arg args[7]; + Cardinal nargs = 0; + XtSetArg(args[nargs], XtNwidth, width); nargs++; + XtSetArg(args[nargs], XtNheight, height); nargs++; + XtSetArg(args[nargs], XtNborderWidth, 0); nargs++; + + String n, c; + XtGetApplicationNameAndClass(qt_xdisplay(), &n, &c); + + _toplevel = XtAppCreateShell("drawingArea", c, applicationShellWidgetClass, + qt_xdisplay(), args, nargs); + + // What exactly does widget mapping mean? Without this call the widget isn't + // embedded correctly. With it the viewer doesn't show anything in standalone mode. + //if (embed) + XtSetMappedWhenManaged(_toplevel, False); + XtRealizeWidget(_toplevel); + + // Create form window that is searched for by flash plugin + _form = XtVaCreateWidget("form", compositeWidgetClass, _toplevel, NULL); + XtSetArg(args[nargs], XtNvisual, QPaintDevice::x11AppVisual()); nargs++; + XtSetArg(args[nargs], XtNdepth, QPaintDevice::x11AppDepth()); nargs++; + XtSetArg(args[nargs], XtNcolormap, QPaintDevice::x11AppColormap()); nargs++; + XtSetValues(_form, args, nargs); + XSync(qt_xdisplay(), false); + + // From mozilla - not sure if it's needed yet, nor what to use for embedder +#if 0 + /* this little trick seems to finish initializing the widget */ +#if XlibSpecificationRelease >= 6 + XtRegisterDrawable(qt_xdisplay(), embedderid, _toplevel); +#else + _XtRegisterWindow(embedderid, _toplevel); +#endif +#endif + XtRealizeWidget(_form); + XtManageChild(_form); + + // Register forwarder + XtAddEventHandler(_toplevel, (KeyPressMask|KeyReleaseMask), + False, forwarder, (XtPointer)this ); + XtAddEventHandler(_form, (KeyPressMask|KeyReleaseMask), + False, forwarder, (XtPointer)this ); + XSync(qt_xdisplay(), false); + } +} + +NSPluginInstance::~NSPluginInstance() +{ + kdDebug(1431) << "-> ~NSPluginInstance" << endl; + destroy(); + kdDebug(1431) << "<- ~NSPluginInstance" << endl; +} + + +void NSPluginInstance::destroy() +{ + if ( !_destroyed ) { + + kdDebug(1431) << "delete streams" << endl; + _waitingRequests.clear(); + + shutdown(); + + for( NSPluginStreamBase *s=_streams.first(); s!=0; ) { + NSPluginStreamBase *next = _streams.next(); + s->stop(); + s = next; + } + + _streams.clear(); + + kdDebug(1431) << "delete callbacks" << endl; + delete _callback; + _callback = 0; + + kdDebug(1431) << "destroy plugin" << endl; + NPSavedData *saved = 0; + + // As of 7/31/01, nsplugin crashes when used with Qt + // linked with libGL if the destroy function is called. + // A patch on that date hacked out the following call. + // On 11/17/01, Jeremy White has reenabled this destroy + // in a an attempt to better understand why this crash + // occurs so that the real problem can be found and solved. + // It's possible that a flaw in the SetWindow call + // caused the crash and it is now fixed. + if ( _pluginFuncs.destroy ) + _pluginFuncs.destroy( _npp, &saved ); + + if (saved && saved->len && saved->buf) + g_NPN_MemFree(saved->buf); + if (saved) + g_NPN_MemFree(saved); + + if( _form != 0 ) { + XtRemoveEventHandler(_form, (KeyPressMask|KeyReleaseMask), + False, forwarder, (XtPointer)this); + XtRemoveEventHandler(_toplevel, (KeyPressMask|KeyReleaseMask), + False, forwarder, (XtPointer)this); + XtDestroyWidget(_form); + _form = 0; + XtDestroyWidget(_toplevel); + _toplevel = 0; + } + + if (_npp) { + ::free(_npp); // matched with malloc() in newInstance + } + + _destroyed = true; + } +} + + +void NSPluginInstance::shutdown() +{ + NSPluginClass *cls = dynamic_cast<NSPluginClass*>(parent()); + //destroy(); + if (cls) { + cls->destroyInstance( this ); + } +} + + +void NSPluginInstance::timer() +{ + if (!_visible) { + _timer->start( 100, true ); + return; + } + + //_streams.clear(); + + // start queued requests + kdDebug(1431) << "looking for waiting requests" << endl; + while ( _waitingRequests.head() ) { + kdDebug(1431) << "request found" << endl; + Request req( *_waitingRequests.head() ); + _waitingRequests.remove(); + + QString url; + + // make absolute url + if ( req.url.left(11).lower()=="javascript:" ) + url = req.url; + else if ( KURL::isRelativeURL(req.url) ) { + KURL bu( _baseURL ); + KURL absUrl( bu, req.url ); + url = absUrl.url(); + } else if ( req.url[0]=='/' && KURL(_baseURL).hasHost() ) { + KURL absUrl( _baseURL ); + absUrl.setPath( req.url ); + url = absUrl.url(); + } else + url = req.url; + + // non empty target = frame target + if ( !req.target.isEmpty()) + { + if (_callback) + { + if ( req.post ) { + _callback->postURL( url, req.target, req.data, req.mime ); + } else { + _callback->requestURL( url, req.target ); + } + if ( req.notify ) { + NPURLNotify( req.url, NPRES_DONE, req.notify ); + } + } + } else { + if (!url.isEmpty()) + { + kdDebug(1431) << "Starting new stream " << req.url << endl; + + if (req.post) { + // create stream + NSPluginStream *s = new NSPluginStream( this ); + connect( s, SIGNAL(finished(NSPluginStreamBase*)), + SLOT(streamFinished(NSPluginStreamBase*)) ); + _streams.append( s ); + + kdDebug() << "posting to " << url << endl; + + emitStatus( i18n("Submitting data to %1").arg(url) ); + s->post( url, req.data, req.mime, req.notify, req.args ); + } else if (url.lower().startsWith("javascript:")){ + if (_callback) { + static Q_INT32 _jsrequestid = 0; + _jsrequests.insert(_jsrequestid, new Request(req)); + _callback->evalJavaScript(_jsrequestid++, url.mid(11)); + } else { + kdDebug() << "No callback for javascript: url!" << endl; + } + } else { + // create stream + NSPluginStream *s = new NSPluginStream( this ); + connect( s, SIGNAL(finished(NSPluginStreamBase*)), + SLOT(streamFinished(NSPluginStreamBase*)) ); + _streams.append( s ); + + kdDebug() << "getting " << url << endl; + + emitStatus( i18n("Requesting %1").arg(url) ); + s->get( url, req.mime, req.notify, req.reload ); + } + + //break; + } + } + } +} + + +QString NSPluginInstance::normalizedURL(const QString& url) const { + KURL bu( _baseURL ); + KURL inURL(bu, url); + KConfig cfg("kcmnspluginrc", true); + cfg.setGroup("Misc"); + + if (!cfg.readBoolEntry("HTTP URLs Only", false) || + inURL.protocol() == "http" || + inURL.protocol() == "https" || + inURL.protocol() == "javascript") { + return inURL.url(); + } + + // Allow: javascript:, http, https, or no protocol (match loading) + kdDebug(1431) << "NSPluginInstance::normalizedURL - I don't think so. http or https only!" << endl; + return QString::null; +} + + +void NSPluginInstance::requestURL( const QString &url, const QString &mime, + const QString &target, void *notify, bool forceNotify, bool reload ) +{ + // Generally this should already be done, but let's be safe for now. + QString nurl = normalizedURL(url); + if (nurl.isNull()) { + return; + } + + kdDebug(1431) << "NSPluginInstance::requestURL url=" << nurl << " target=" << target << " notify=" << notify << endl; + _waitingRequests.enqueue( new Request( nurl, mime, target, notify, forceNotify, reload ) ); + _timer->start( 100, true ); +} + + +void NSPluginInstance::postURL( const QString &url, const QByteArray& data, + const QString &mime, + const QString &target, void *notify, + const KParts::URLArgs& args, bool forceNotify ) +{ + // Generally this should already be done, but let's be safe for now. + QString nurl = normalizedURL(url); + if (nurl.isNull()) { + return; + } + + kdDebug(1431) << "NSPluginInstance::postURL url=" << nurl << " target=" << target << " notify=" << notify << endl; + _waitingRequests.enqueue( new Request( nurl, data, mime, target, notify, args, forceNotify) ); + _timer->start( 100, true ); +} + + +void NSPluginInstance::emitStatus(const QString &message) +{ + if( _callback ) + _callback->statusMessage( message ); +} + + +void NSPluginInstance::streamFinished( NSPluginStreamBase* strm ) +{ + kdDebug(1431) << "-> NSPluginInstance::streamFinished" << endl; + emitStatus( QString::null ); + _streams.setAutoDelete(false); // Don't delete it yet!! we get called from + // its slot! + _streams.remove(strm); + _streams.setAutoDelete(true); + strm->deleteLater(); + _timer->start( 100, true ); +} + +int NSPluginInstance::setWindow(Q_INT8 remove) +{ + if (remove) + { + NPSetWindow(0); + return NPERR_NO_ERROR; + } + + kdDebug(1431) << "-> NSPluginInstance::setWindow" << endl; + + _win.x = 0; + _win.y = 0; + _win.height = _height; + _win.width = _width; + _win.type = NPWindowTypeWindow; + + // Well, the docu says sometimes, this is only used on the + // MAC, but sometimes it says it's always. Who knows... + _win.clipRect.top = 0; + _win.clipRect.left = 0; + _win.clipRect.bottom = _height; + _win.clipRect.right = _width; + + if( _xembed_window ) { + _win.window = (void*) _xembed_window; + _win_info.type = NP_SETWINDOW; + _win_info.display = qt_xdisplay(); + _win_info.visual = DefaultVisualOfScreen(DefaultScreenOfDisplay(qt_xdisplay())); + _win_info.colormap = DefaultColormapOfScreen(DefaultScreenOfDisplay(qt_xdisplay())); + _win_info.depth = DefaultDepthOfScreen(DefaultScreenOfDisplay(qt_xdisplay())); + } else { + _win.window = (void*) XtWindow(_form); + + _win_info.type = NP_SETWINDOW; + _win_info.display = XtDisplay(_form); + _win_info.visual = DefaultVisualOfScreen(XtScreen(_form)); + _win_info.colormap = DefaultColormapOfScreen(XtScreen(_form)); + _win_info.depth = DefaultDepthOfScreen(XtScreen(_form)); + } + + kdDebug(1431) << "Window ID = " << _win.window << endl; + + _win.ws_info = &_win_info; + + NPError error = NPSetWindow( &_win ); + + kdDebug(1431) << "<- NSPluginInstance::setWindow = " << error << endl; + return error; +} + + +static void resizeWidgets(Window w, int width, int height) { + Window rroot, parent, *children; + unsigned int nchildren = 0; + + if (XQueryTree(qt_xdisplay(), w, &rroot, &parent, &children, &nchildren)) { + for (unsigned int i = 0; i < nchildren; i++) { + XResizeWindow(qt_xdisplay(), children[i], width, height); + } + XFree(children); + } +} + + +void NSPluginInstance::resizePlugin(Q_INT32 w, Q_INT32 h) +{ + if (w == _width && h == _height) + return; + + kdDebug(1431) << "-> NSPluginInstance::resizePlugin( w=" << w << ", h=" << h << " ) " << endl; + + _width = w; + _height = h; + + if( _form != 0 ) { + XResizeWindow(qt_xdisplay(), XtWindow(_form), w, h); + XResizeWindow(qt_xdisplay(), XtWindow(_toplevel), w, h); + + Arg args[7]; + Cardinal nargs = 0; + XtSetArg(args[nargs], XtNwidth, _width); nargs++; + XtSetArg(args[nargs], XtNheight, _height); nargs++; + XtSetArg(args[nargs], XtNvisual, QPaintDevice::x11AppVisual()); nargs++; + XtSetArg(args[nargs], XtNdepth, QPaintDevice::x11AppDepth()); nargs++; + XtSetArg(args[nargs], XtNcolormap, QPaintDevice::x11AppColormap()); nargs++; + XtSetArg(args[nargs], XtNborderWidth, 0); nargs++; + + XtSetValues(_toplevel, args, nargs); + XtSetValues(_form, args, nargs); + + resizeWidgets(XtWindow(_form), _width, _height); + } + + // If not visible yet, displayWindow() will call setWindow() again anyway, so avoid this. + // This also handled plugins that are broken and cannot handle repeated setWindow() calls + // very well. + if (!_visible) + return; + + setWindow(); + + kdDebug(1431) << "<- NSPluginInstance::resizePlugin" << endl; +} + + +void NSPluginInstance::javascriptResult(Q_INT32 id, QString result) { + QMap<int, Request*>::iterator i = _jsrequests.find( id ); + if (i != _jsrequests.end()) { + Request *req = i.data(); + _jsrequests.remove( i ); + NSPluginStream *s = new NSPluginStream( this ); + connect( s, SIGNAL(finished(NSPluginStreamBase*)), + SLOT(streamFinished(NSPluginStreamBase*)) ); + _streams.append( s ); + + int len = result.length(); + s->create( req->url, QString("text/plain"), req->notify, req->forceNotify ); + kdDebug(1431) << "javascriptResult has been called with: "<<result<<endl; + if (len > 0) { + QByteArray data(len + 1); + memcpy(data.data(), result.latin1(), len); + data[len] = 0; + s->process(data, 0); + } else { + len = 7; // "unknown" + QByteArray data(len + 1); + memcpy(data.data(), "unknown", len); + data[len] = 0; + s->process(data, 0); + } + s->finish(false); + + delete req; + } +} + + +NPError NSPluginInstance::NPGetValue(NPPVariable variable, void *value) +{ + if( value==0 ) { + kdDebug() << "FIXME: value==0 in NSPluginInstance::NPGetValue" << endl; + return NPERR_GENERIC_ERROR; + } + + if (!_pluginFuncs.getvalue) + return NPERR_GENERIC_ERROR; + + NPError error = _pluginFuncs.getvalue(_npp, variable, value); + + CHECK(GetValue,error); +} + + +NPError NSPluginInstance::NPSetValue(NPNVariable variable, void *value) +{ + if( value==0 ) { + kdDebug() << "FIXME: value==0 in NSPluginInstance::NPSetValue" << endl; + return NPERR_GENERIC_ERROR; + } + + if (!_pluginFuncs.setvalue) + return NPERR_GENERIC_ERROR; + + NPError error = _pluginFuncs.setvalue(_npp, variable, value); + + CHECK(SetValue,error); +} + + +NPError NSPluginInstance::NPSetWindow(NPWindow *window) +{ + if( window==0 ) { + kdDebug() << "FIXME: window==0 in NSPluginInstance::NPSetWindow" << endl; + return NPERR_GENERIC_ERROR; + } + + if (!_pluginFuncs.setwindow) + return NPERR_GENERIC_ERROR; + + NPError error = _pluginFuncs.setwindow(_npp, window); + + CHECK(SetWindow,error); +} + + +NPError NSPluginInstance::NPDestroyStream(NPStream *stream, NPReason reason) +{ + if( stream==0 ) { + kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPDestroyStream" << endl; + return NPERR_GENERIC_ERROR; + } + + if (!_pluginFuncs.destroystream) + return NPERR_GENERIC_ERROR; + + NPError error = _pluginFuncs.destroystream(_npp, stream, reason); + + CHECK(DestroyStream,error); +} + + +NPError NSPluginInstance::NPNewStream(NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype) +{ + if( stream==0 ) { + kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPNewStream" << endl; + return NPERR_GENERIC_ERROR; + } + + if( stype==0 ) { + kdDebug() << "FIXME: stype==0 in NSPluginInstance::NPNewStream" << endl; + return NPERR_GENERIC_ERROR; + } + + if (!_pluginFuncs.newstream) + return NPERR_GENERIC_ERROR; + + NPError error = _pluginFuncs.newstream(_npp, type, stream, seekable, stype); + + CHECK(NewStream,error); +} + + +void NSPluginInstance::NPStreamAsFile(NPStream *stream, const char *fname) +{ + if( stream==0 ) { + kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPStreamAsFile" << endl; + return; + } + + if( fname==0 ) { + kdDebug() << "FIXME: fname==0 in NSPluginInstance::NPStreamAsFile" << endl; + return; + } + + if (!_pluginFuncs.asfile) + return; + + _pluginFuncs.asfile(_npp, stream, fname); +} + + +int32 NSPluginInstance::NPWrite(NPStream *stream, int32 offset, int32 len, void *buf) +{ + if( stream==0 ) { + kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPWrite" << endl; + return 0; + } + + if( buf==0 ) { + kdDebug() << "FIXME: buf==0 in NSPluginInstance::NPWrite" << endl; + return 0; + } + + if (!_pluginFuncs.write) + return 0; + + return _pluginFuncs.write(_npp, stream, offset, len, buf); +} + + +int32 NSPluginInstance::NPWriteReady(NPStream *stream) +{ + if( stream==0 ) { + kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPWriteReady" << endl; + return 0; + } + + if (!_pluginFuncs.writeready) + return 0; + + return _pluginFuncs.writeready(_npp, stream); +} + + +void NSPluginInstance::NPURLNotify(QString url, NPReason reason, void *notifyData) +{ + if (!_pluginFuncs.urlnotify) + return; + + _pluginFuncs.urlnotify(_npp, url.ascii(), reason, notifyData); +} + + +void NSPluginInstance::addTempFile(KTempFile *tmpFile) +{ + _tempFiles.append(tmpFile); +} + +/* + * We have to call this after we reparent the widget otherwise some plugins + * like the ones based on WINE get very confused. (their coordinates are not + * adjusted for the mouse at best) + */ +void NSPluginInstance::displayPlugin() +{ + // display plugin + setWindow(); + + _visible = true; + kdDebug(1431) << "<- NSPluginInstance::displayPlugin = " << (void*)this << endl; +} + +static bool has_focus = false; + +void NSPluginInstance::gotFocusIn() +{ + has_focus = true; +} + +void NSPluginInstance::gotFocusOut() +{ + has_focus = false; +} + +#include <dlfcn.h> +// Prevent plugins from polling the keyboard regardless of focus. +static int (*real_xquerykeymap)( Display*, char[32] ) = NULL; + +extern "C" KDE_EXPORT +int XQueryKeymap( Display* dpy, char k[32] ) +{ + if( real_xquerykeymap == NULL ) + real_xquerykeymap = (int (*)( Display*, char[32] )) dlsym( RTLD_NEXT, "XQueryKeymap" ); + if( has_focus ) + return real_xquerykeymap( dpy, k ); + memset( k, 0, 32 ); + return 1; +} + + + +/***************************************************************************/ + +NSPluginViewer::NSPluginViewer( QCString dcopId, + QObject *parent, const char *name ) + : DCOPObject(dcopId), QObject( parent, name ) +{ + _classes.setAutoDelete( true ); + connect(KApplication::dcopClient(), + SIGNAL(applicationRemoved(const QCString&)), + this, + SLOT(appUnregistered(const QCString&))); +} + + +NSPluginViewer::~NSPluginViewer() +{ + kdDebug(1431) << "NSPluginViewer::~NSPluginViewer" << endl; +} + + +void NSPluginViewer::appUnregistered(const QCString& id) { + if (id.isEmpty()) { + return; + } + + QDictIterator<NSPluginClass> it(_classes); + NSPluginClass *c; + while ( (c = it.current()) ) { + QString key = it.currentKey(); + ++it; + if (c->app() == id) { + _classes.remove(key); + } + } + + if (_classes.isEmpty()) { + shutdown(); + } +} + + +void NSPluginViewer::shutdown() +{ + kdDebug(1431) << "NSPluginViewer::shutdown" << endl; + _classes.clear(); +#if QT_VERSION < 0x030100 + quitXt(); +#else + qApp->quit(); +#endif +} + + +DCOPRef NSPluginViewer::newClass( QString plugin ) +{ + kdDebug(1431) << "NSPluginViewer::NewClass( " << plugin << ")" << endl; + + // search existing class + NSPluginClass *cls = _classes[ plugin ]; + if ( !cls ) { + // create new class + cls = new NSPluginClass( plugin, this ); + QCString id = ""; + DCOPClient *dc = callingDcopClient(); + if (dc) { + id = dc->senderId(); + } + cls->setApp(id); + if ( cls->error() ) { + kdError(1431) << "Can't create plugin class" << endl; + delete cls; + return DCOPRef(); + } + + _classes.insert( plugin, cls ); + } + + return DCOPRef( kapp->dcopClient()->appId(), cls->objId() ); +} + + +/****************************************************************************/ + + +NSPluginClass::NSPluginClass( const QString &library, + QObject *parent, const char *name ) + : DCOPObject(), QObject( parent, name ) +{ + // initialize members + _handle = KLibLoader::self()->library(QFile::encodeName(library)); + _libname = library; + _constructed = false; + _error = true; + _instances.setAutoDelete( true ); + _NP_GetMIMEDescription = 0; + _NP_Initialize = 0; + _NP_Shutdown = 0; + + _timer = new QTimer( this ); + connect( _timer, SIGNAL(timeout()), SLOT(timer()) ); + + // check lib handle + if (!_handle) { + kdDebug(1431) << "Could not dlopen " << library << endl; + return; + } + + // get exported lib functions + _NP_GetMIMEDescription = (NP_GetMIMEDescriptionUPP *)_handle->symbol("NP_GetMIMEDescription"); + _NP_Initialize = (NP_InitializeUPP *)_handle->symbol("NP_Initialize"); + _NP_Shutdown = (NP_ShutdownUPP *)_handle->symbol("NP_Shutdown"); + + // check for valid returned ptrs + if (!_NP_GetMIMEDescription) { + kdDebug(1431) << "Could not get symbol NP_GetMIMEDescription" << endl; + return; + } + + if (!_NP_Initialize) { + kdDebug(1431) << "Could not get symbol NP_Initialize" << endl; + return; + } + + if (!_NP_Shutdown) { + kdDebug(1431) << "Could not get symbol NP_Shutdown" << endl; + return; + } + + // initialize plugin + kdDebug(1431) << "Plugin library " << library << " loaded!" << endl; + _constructed = true; + _error = initialize()!=NPERR_NO_ERROR; +} + + +NSPluginClass::~NSPluginClass() +{ + _instances.clear(); + _trash.clear(); + shutdown(); + if (_handle) + _handle->unload(); +} + + +void NSPluginClass::timer() +{ + // delete instances + for ( NSPluginInstance *it=_trash.first(); it!=0; it=_trash.next() ) + _instances.remove(it); + + _trash.clear(); +} + + +int NSPluginClass::initialize() +{ + kdDebug(1431) << "NSPluginClass::Initialize()" << endl; + + if ( !_constructed ) + return NPERR_GENERIC_ERROR; + + // initialize nescape exported functions + memset(&_pluginFuncs, 0, sizeof(_pluginFuncs)); + memset(&_nsFuncs, 0, sizeof(_nsFuncs)); + + _pluginFuncs.size = sizeof(_pluginFuncs); + _nsFuncs.size = sizeof(_nsFuncs); + _nsFuncs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + _nsFuncs.geturl = g_NPN_GetURL; + _nsFuncs.posturl = g_NPN_PostURL; + _nsFuncs.requestread = g_NPN_RequestRead; + _nsFuncs.newstream = g_NPN_NewStream; + _nsFuncs.write = g_NPN_Write; + _nsFuncs.destroystream = g_NPN_DestroyStream; + _nsFuncs.status = g_NPN_Status; + _nsFuncs.uagent = g_NPN_UserAgent; + _nsFuncs.memalloc = g_NPN_MemAlloc; + _nsFuncs.memfree = g_NPN_MemFree; + _nsFuncs.memflush = g_NPN_MemFlush; + _nsFuncs.reloadplugins = g_NPN_ReloadPlugins; + _nsFuncs.getJavaEnv = g_NPN_GetJavaEnv; + _nsFuncs.getJavaPeer = g_NPN_GetJavaPeer; + _nsFuncs.geturlnotify = g_NPN_GetURLNotify; + _nsFuncs.posturlnotify = g_NPN_PostURLNotify; + _nsFuncs.getvalue = g_NPN_GetValue; + _nsFuncs.setvalue = g_NPN_SetValue; + _nsFuncs.invalidaterect = g_NPN_InvalidateRect; + _nsFuncs.invalidateregion = g_NPN_InvalidateRegion; + _nsFuncs.forceredraw = g_NPN_ForceRedraw; + + // initialize plugin + NPError error = _NP_Initialize(&_nsFuncs, &_pluginFuncs); + CHECK(Initialize,error); +} + + +QString NSPluginClass::getMIMEDescription() +{ + return _NP_GetMIMEDescription(); +} + + +void NSPluginClass::shutdown() +{ + kdDebug(1431) << "NSPluginClass::shutdown error=" << _error << endl; + if( _NP_Shutdown && !_error ) + _NP_Shutdown(); +} + + +DCOPRef NSPluginClass::newInstance( QString url, QString mimeType, Q_INT8 embed, + QStringList argn, QStringList argv, + QString appId, QString callbackId, + Q_INT8 reload, Q_INT8 doPost, QByteArray postData, Q_UINT32 xembed ) +{ + kdDebug(1431) << "-> NSPluginClass::NewInstance" << endl; + + if ( !_constructed ) + return DCOPRef(); + + // copy parameters over + unsigned int argc = argn.count(); + char **_argn = new char*[argc]; + char **_argv = new char*[argc]; + QString src = url; + int width = 0; + int height = 0; + QString baseURL = url; + + for (unsigned int i=0; i<argc; i++) + { + QCString encN = argn[i].utf8(); + QCString encV = argv[i].utf8(); + + const char *n = encN; + const char *v = encV; + + _argn[i] = strdup(n); + _argv[i] = strdup(v); + + if (!strcasecmp(_argn[i], "WIDTH")) width = argv[i].toInt(); + if (!strcasecmp(_argn[i], "HEIGHT")) height = argv[i].toInt(); + if (!strcasecmp(_argn[i], "__KHTML__PLUGINBASEURL")) baseURL = _argv[i]; + kdDebug(1431) << "argn=" << _argn[i] << " argv=" << _argv[i] << endl; + } + + // create plugin instance + char mime[256]; + strncpy(mime, mimeType.ascii(), 255); + mime[255] = 0; + NPP npp = (NPP)malloc(sizeof(NPP_t)); // I think we should be using + // malloc here, just to be safe, + // since the nsplugin plays with + // this thing + memset(npp, 0, sizeof(NPP_t)); + npp->ndata = NULL; + + // create plugin instance + NPError error = _pluginFuncs.newp(mime, npp, embed ? NP_EMBED : NP_FULL, + argc, _argn, _argv, 0); + kdDebug(1431) << "NPP_New = " << (int)error << endl; + + // don't use bool here, it can be 1 byte, but some plugins write it as int, and I can't find what the spec says + int wants_xembed = false; + if (_pluginFuncs.getvalue) { + NPError error = _pluginFuncs.getvalue(npp, (NPPVariable)14/*NPPVpluginNeedsXEmbed*/, &wants_xembed ); + if( error != NPERR_NO_ERROR ) + wants_xembed = false; + } + kdDebug(1431) << "Plugin requires XEmbed:" << (bool)wants_xembed << endl; + + // Create plugin instance object + NSPluginInstance *inst = new NSPluginInstance( npp, &_pluginFuncs, _handle, + width, height, baseURL, mimeType, + appId, callbackId, embed, wants_xembed ? xembed : 0, this ); + + // free arrays with arguments + delete [] _argn; + delete [] _argv; + + // check for error + if ( error!=NPERR_NO_ERROR) + { + delete inst; + //delete npp; double delete! + kdDebug(1431) << "<- PluginClass::NewInstance = 0" << endl; + return DCOPRef(); + } + + // create source stream + if ( !src.isEmpty() ) { + if (doPost) { + inst->postURL(src, postData, mimeType, QString::null, 0, KParts::URLArgs(), false); + } else { + inst->requestURL( src, mimeType, QString::null, 0, false, reload ); + } + } + + _instances.append( inst ); + return DCOPRef(kapp->dcopClient()->appId(), inst->objId()); +} + + +void NSPluginClass::destroyInstance( NSPluginInstance* inst ) +{ + // mark for destruction + _trash.append( inst ); + timer(); //_timer->start( 0, TRUE ); +} + +/****************************************************************************/ + +NSPluginStreamBase::NSPluginStreamBase( NSPluginInstance *instance ) + : QObject( instance ), _instance(instance), _stream(0), _tempFile(0L), + _pos(0), _queue(0), _queuePos(0), _error(false) +{ + _informed = false; +} + + +NSPluginStreamBase::~NSPluginStreamBase() +{ + if (_stream) { + _instance->NPDestroyStream( _stream, NPRES_USER_BREAK ); + if (_stream && _stream->url) + free(const_cast<char*>(_stream->url)); + delete _stream; + _stream = 0; + } + + delete _tempFile; + _tempFile = 0; +} + + +void NSPluginStreamBase::stop() +{ + finish( true ); +} + +void NSPluginStreamBase::inform() +{ + + if (! _informed) + { + KURL src(_url); + + _informed = true; + + // inform the plugin + _instance->NPNewStream( _mimeType.isEmpty() ? (char *) "text/plain" : (char*)_mimeType.ascii(), + _stream, false, &_streamType ); + kdDebug(1431) << "NewStream stype=" << _streamType << " url=" << _url << " mime=" << _mimeType << endl; + + // prepare data transfer + _tempFile = 0L; + + if ( _streamType==NP_ASFILE || _streamType==NP_ASFILEONLY ) { + _onlyAsFile = _streamType==NP_ASFILEONLY; + if ( KURL(_url).isLocalFile() ) { + kdDebug(1431) << "local file" << endl; + // local file can be passed directly + _fileURL = KURL(_url).path(); + + // without streaming stream is finished already + if ( _onlyAsFile ) { + kdDebug() << "local file AS_FILE_ONLY" << endl; + finish( false ); + } + } else { + kdDebug() << "remote file" << endl; + + // stream into temporary file (use lower() in case the + // filename as an upper case X in it) + _tempFile = new KTempFile; + _tempFile->setAutoDelete( TRUE ); + _fileURL = _tempFile->name(); + kdDebug() << "saving into " << _fileURL << endl; + } + } + } + +} + +bool NSPluginStreamBase::create( const QString& url, const QString& mimeType, void *notify, bool forceNotify) +{ + if ( _stream ) + return false; + + _url = url; + _notifyData = notify; + _pos = 0; + _tries = 0; + _onlyAsFile = false; + _streamType = NP_NORMAL; + _informed = false; + _forceNotify = forceNotify; + + // create new stream + _stream = new NPStream; + _stream->ndata = this; + _stream->url = strdup(url.ascii()); + _stream->end = 0; + _stream->pdata = 0; + _stream->lastmodified = 0; + _stream->notifyData = _notifyData; + + _mimeType = mimeType; + + return true; +} + +void NSPluginStreamBase::updateURL( const KURL& newURL ) +{ + _url = newURL; + free(const_cast<char*>(_stream->url)); + _stream->url = strdup(_url.url().ascii()); +} + +int NSPluginStreamBase::process( const QByteArray &data, int start ) +{ + int32 max, sent, to_sent, len; + char *d = data.data() + start; + + to_sent = data.size() - start; + while (to_sent > 0) + { + inform(); + + max = _instance->NPWriteReady(_stream); + //kdDebug(1431) << "to_sent == " << to_sent << " and max = " << max << endl; + len = QMIN(max, to_sent); + + //kdDebug(1431) << "-> Feeding stream to plugin: offset=" << _pos << ", len=" << len << endl; + sent = _instance->NPWrite( _stream, _pos, len, d ); + //kdDebug(1431) << "<- Feeding stream: sent = " << sent << endl; + + if (sent == 0) // interrupt the stream for a few ms + break; + + if (sent < 0) { + // stream data rejected/error + kdDebug(1431) << "stream data rejected/error" << endl; + _error = true; + break; + } + + if (_tempFile) { + _tempFile->dataStream()->writeRawBytes(d, sent); + } + + to_sent -= sent; + _pos += sent; + d += sent; + } + + return data.size() - to_sent; +} + + +bool NSPluginStreamBase::pump() +{ + //kdDebug(1431) << "queue pos " << _queuePos << ", size " << _queue.size() << endl; + + inform(); + + if ( _queuePos<_queue.size() ) { + unsigned newPos; + + // handle AS_FILE_ONLY streams + if ( _onlyAsFile ) { + if (_tempFile) { + _tempFile->dataStream()->writeRawBytes( _queue, _queue.size() ); + } + newPos = _queuePos+_queue.size(); + } else { + // normal streams + newPos = process( _queue, _queuePos ); + } + + // count tries + if ( newPos==_queuePos ) + _tries++; + else + _tries = 0; + + _queuePos = newPos; + } + + // return true if queue finished + return _queuePos>=_queue.size(); +} + + +void NSPluginStreamBase::queue( const QByteArray &data ) +{ + _queue = data; + _queue.detach(); + _queuePos = 0; + _tries = 0; + +/* + kdDebug(1431) << "new queue size=" << data.size() + << " data=" << (void*)data.data() + << " queue=" << (void*)_queue.data() << " qsize=" + << _queue.size() << endl; +*/ +} + + +void NSPluginStreamBase::finish( bool err ) +{ + kdDebug(1431) << "finish error=" << err << endl; + + _queue.resize( 0 ); + _pos = 0; + _queuePos = 0; + + inform(); + + if ( !err ) { + if ( _tempFile ) { + _tempFile->close(); + _instance->addTempFile( _tempFile ); + _tempFile = 0; + } + + if ( !_fileURL.isEmpty() ) { + kdDebug() << "stream as file " << _fileURL << endl; + _instance->NPStreamAsFile( _stream, _fileURL.ascii() ); + } + + _instance->NPDestroyStream( _stream, NPRES_DONE ); + if (_notifyData || _forceNotify) + _instance->NPURLNotify( _url.url(), NPRES_DONE, _notifyData ); + } else { + // close temp file + if ( _tempFile ) { + _tempFile->close(); + } + + // destroy stream + _instance->NPDestroyStream( _stream, NPRES_NETWORK_ERR ); + if (_notifyData || _forceNotify) + _instance->NPURLNotify( _url.url(), NPRES_NETWORK_ERR, _notifyData ); + } + + // delete stream + if (_stream && _stream->url) + free(const_cast<char *>(_stream->url)); + delete _stream; + _stream = 0; + + // destroy NSPluginStream object + emit finished( this ); +} + + +/****************************************************************************/ + +NSPluginBufStream::NSPluginBufStream( class NSPluginInstance *instance ) + : NSPluginStreamBase( instance ) +{ + _timer = new QTimer( this ); + connect( _timer, SIGNAL(timeout()), this, SLOT(timer()) ); +} + + +NSPluginBufStream::~NSPluginBufStream() +{ + +} + + +bool NSPluginBufStream::get( const QString& url, const QString& mimeType, + const QByteArray &buf, void *notifyData, + bool singleShot ) +{ + _singleShot = singleShot; + if ( create( url, mimeType, notifyData ) ) { + queue( buf ); + _timer->start( 100, true ); + } + + return false; +} + + +void NSPluginBufStream::timer() +{ + bool finished = pump(); + if ( _singleShot ) + finish( false ); + else { + + if ( !finished && tries()<=8 ) + _timer->start( 100, true ); + else + finish( error() || tries()>8 ); + } +} + + + +/****************************************************************************/ + +NSPluginStream::NSPluginStream( NSPluginInstance *instance ) + : NSPluginStreamBase( instance ), _job(0) +{ + _resumeTimer = new QTimer( this ); + connect(_resumeTimer, SIGNAL(timeout()), this, SLOT(resume())); +} + + +NSPluginStream::~NSPluginStream() +{ + if ( _job ) + _job->kill( true ); +} + + +bool NSPluginStream::get( const QString& url, const QString& mimeType, + void *notify, bool reload ) +{ + // create new stream + if ( create( url, mimeType, notify ) ) { + // start the kio job + _job = KIO::get(KURL( url ), false, false); + _job->addMetaData("errorPage", "false"); + _job->addMetaData("AllowCompressedPage", "false"); + if (reload) { + _job->addMetaData("cache", "reload"); + } + connect(_job, SIGNAL(data(KIO::Job *, const QByteArray &)), + SLOT(data(KIO::Job *, const QByteArray &))); + connect(_job, SIGNAL(result(KIO::Job *)), SLOT(result(KIO::Job *))); + connect(_job, SIGNAL(totalSize(KIO::Job *, KIO::filesize_t )), + SLOT(totalSize(KIO::Job *, KIO::filesize_t))); + connect(_job, SIGNAL(mimetype(KIO::Job *, const QString &)), + SLOT(mimetype(KIO::Job *, const QString &))); + connect(_job, SIGNAL(redirection(KIO::Job *, const KURL&)), + SLOT(redirection(KIO::Job *, const KURL&))); + } + + return false; +} + + +bool NSPluginStream::post( const QString& url, const QByteArray& data, + const QString& mimeType, void *notify, const KParts::URLArgs& args ) +{ + // create new stream + if ( create( url, mimeType, notify ) ) { + // start the kio job + _job = KIO::http_post(KURL( url ), data, false); + _job->addMetaData("content-type", args.contentType()); + _job->addMetaData("errorPage", "false"); + _job->addMetaData("AllowCompressedPage", "false"); + connect(_job, SIGNAL(data(KIO::Job *, const QByteArray &)), + SLOT(data(KIO::Job *, const QByteArray &))); + connect(_job, SIGNAL(result(KIO::Job *)), SLOT(result(KIO::Job *))); + connect(_job, SIGNAL(totalSize(KIO::Job *, KIO::filesize_t )), + SLOT(totalSize(KIO::Job *, KIO::filesize_t))); + connect(_job, SIGNAL(mimetype(KIO::Job *, const QString &)), + SLOT(mimetype(KIO::Job *, const QString &))); + connect(_job, SIGNAL(redirection(KIO::Job *, const KURL&)), + SLOT(redirection(KIO::Job *, const KURL&))); + } + + return false; +} + + +void NSPluginStream::data(KIO::Job * job, const QByteArray &data) +{ + //kdDebug(1431) << "NSPluginStream::data - job=" << (void*)job << " data size=" << data.size() << endl; + queue( data ); + if ( !pump() ) { + _job->suspend(); + _resumeTimer->start( 100, TRUE ); + } +} + +void NSPluginStream::redirection(KIO::Job * /*job*/, const KURL& url) +{ + updateURL( url ); +} + +void NSPluginStream::totalSize(KIO::Job * job, KIO::filesize_t size) +{ + kdDebug(1431) << "NSPluginStream::totalSize - job=" << (void*)job << " size=" << KIO::number(size) << endl; + _stream->end = size; +} + +void NSPluginStream::mimetype(KIO::Job * job, const QString &mimeType) +{ + kdDebug(1431) << "NSPluginStream::QByteArray - job=" << (void*)job << " mimeType=" << mimeType << endl; + _mimeType = mimeType; +} + + + + +void NSPluginStream::resume() +{ + if ( error() || tries()>8 ) { + _job->kill( true ); + finish( true ); + return; + } + + if ( pump() ) { + kdDebug(1431) << "resume job" << endl; + _job->resume(); + } else { + kdDebug(1431) << "restart timer" << endl; + _resumeTimer->start( 100, TRUE ); + } +} + + +void NSPluginStream::result(KIO::Job *job) +{ + int err = job->error(); + _job = 0; + finish( err!=0 || error() ); +} + +#include "nsplugin.moc" +// vim: ts=4 sw=4 et diff --git a/nsplugins/viewer/nsplugin.h b/nsplugins/viewer/nsplugin.h new file mode 100644 index 000000000..76d2e659b --- /dev/null +++ b/nsplugins/viewer/nsplugin.h @@ -0,0 +1,339 @@ +/* + + This is an encapsulation of the Netscape plugin API. + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Stefan Schimanski <1Stein@gmx.de> + Copyright (c) 2003-2005 George Staikos <staikos@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef __NS_PLUGIN_H__ +#define __NS_PLUGIN_H__ + + +#include <dcopobject.h> +#include "NSPluginClassIface.h" +#include "NSPluginCallbackIface_stub.h" + + +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qptrqueue.h> +#include <qdict.h> +#include <qmap.h> +#include <qintdict.h> +#include <qguardedptr.h> + +#include <kparts/browserextension.h> // for URLArgs +#include <kio/job.h> + + +#define XP_UNIX +#define MOZ_X11 +#include "sdk/npupp.h" + +typedef char* NP_GetMIMEDescriptionUPP(void); +typedef NPError NP_InitializeUPP(NPNetscapeFuncs*, NPPluginFuncs*); +typedef NPError NP_ShutdownUPP(void); + + +#include <X11/Intrinsic.h> + + +void quitXt(); + +class KLibrary; +class QTimer; + + +class NSPluginStreamBase : public QObject +{ +Q_OBJECT +friend class NSPluginInstance; +public: + NSPluginStreamBase( class NSPluginInstance *instance ); + ~NSPluginStreamBase(); + + KURL url() { return _url; } + int pos() { return _pos; } + void stop(); + +signals: + void finished( NSPluginStreamBase *strm ); + +protected: + void finish( bool err ); + bool pump(); + bool error() { return _error; } + void queue( const QByteArray &data ); + bool create( const QString& url, const QString& mimeType, void *notify, bool forceNotify = false ); + int tries() { return _tries; } + void inform( ); + void updateURL( const KURL& newURL ); + + class NSPluginInstance *_instance; + uint16 _streamType; + NPStream *_stream; + void *_notifyData; + KURL _url; + QString _fileURL; + QString _mimeType; + QByteArray _data; + class KTempFile *_tempFile; + +private: + int process( const QByteArray &data, int start ); + + unsigned int _pos; + QByteArray _queue; + unsigned int _queuePos; + int _tries; + bool _onlyAsFile; + bool _error; + bool _informed; + bool _forceNotify; +}; + + +class NSPluginStream : public NSPluginStreamBase +{ + Q_OBJECT + +public: + NSPluginStream( class NSPluginInstance *instance ); + ~NSPluginStream(); + + bool get(const QString& url, const QString& mimeType, void *notifyData, bool reload = false); + bool post(const QString& url, const QByteArray& data, const QString& mimeType, void *notifyData, const KParts::URLArgs& args); + +protected slots: + void data(KIO::Job *job, const QByteArray &data); + void totalSize(KIO::Job *job, KIO::filesize_t size); + void mimetype(KIO::Job * job, const QString &mimeType); + void result(KIO::Job *job); + void redirection(KIO::Job *job, const KURL& url); + void resume(); + +protected: + QGuardedPtr<KIO::TransferJob> _job; + QTimer *_resumeTimer; +}; + + +class NSPluginBufStream : public NSPluginStreamBase +{ + Q_OBJECT + +public: + NSPluginBufStream( class NSPluginInstance *instance ); + ~NSPluginBufStream(); + + bool get( const QString& url, const QString& mimeType, const QByteArray &buf, void *notifyData, bool singleShot=false ); + +protected slots: + void timer(); + +protected: + QTimer *_timer; + bool _singleShot; +}; + + +class NSPluginInstance : public QObject, public virtual NSPluginInstanceIface +{ + Q_OBJECT + +public: + + // constructor, destructor + NSPluginInstance( NPP privateData, NPPluginFuncs *pluginFuncs, KLibrary *handle, + int width, int height, QString src, QString mime, + QString appId, QString callbackId, bool embed, WId xembed, + QObject *parent, const char* name=0 ); + ~NSPluginInstance(); + + // DCOP functions + void shutdown(); + int winId() { return _form != 0 ? XtWindow(_form) : 0; } + int setWindow(Q_INT8 remove=0); + void resizePlugin(Q_INT32 w, Q_INT32 h); + void javascriptResult(Q_INT32 id, QString result); + void displayPlugin(); + void gotFocusIn(); + void gotFocusOut(); + + // value handling + NPError NPGetValue(NPPVariable variable, void *value); + NPError NPSetValue(NPNVariable variable, void *value); + + // window handling + NPError NPSetWindow(NPWindow *window); + + // stream functions + NPError NPDestroyStream(NPStream *stream, NPReason reason); + NPError NPNewStream(NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype); + void NPStreamAsFile(NPStream *stream, const char *fname); + int32 NPWrite(NPStream *stream, int32 offset, int32 len, void *buf); + int32 NPWriteReady(NPStream *stream); + + // URL functions + void NPURLNotify(QString url, NPReason reason, void *notifyData); + + // Event handling + uint16 HandleEvent(void *event); + + // signal emitters + void emitStatus( const QString &message); + void requestURL( const QString &url, const QString &mime, + const QString &target, void *notify, bool forceNotify = false, bool reload = false ); + void postURL( const QString &url, const QByteArray& data, const QString &mime, + const QString &target, void *notify, const KParts::URLArgs& args, bool forceNotify = false ); + + QString normalizedURL(const QString& url) const; + +public slots: + void streamFinished( NSPluginStreamBase *strm ); + +private slots: + void timer(); + +private: + friend class NSPluginStreamBase; + + static void forwarder(Widget, XtPointer, XEvent *, Boolean*); + + void destroy(); + + bool _destroyed; + bool _visible; + void addTempFile(KTempFile *tmpFile); + QPtrList<KTempFile> _tempFiles; + NSPluginCallbackIface_stub *_callback; + QPtrList<NSPluginStreamBase> _streams; + KLibrary *_handle; + QTimer *_timer; + + NPP _npp; + NPPluginFuncs _pluginFuncs; + + Widget _area, _form, _toplevel; + WId _xembed_window; + QString _baseURL; + int _width, _height; + + struct Request + { + // A GET request + Request( const QString &_url, const QString &_mime, + const QString &_target, void *_notify, bool _forceNotify = false, + bool _reload = false) + { url=_url; mime=_mime; target=_target; notify=_notify; post=false; forceNotify = _forceNotify; reload = _reload; } + + // A POST request + Request( const QString &_url, const QByteArray& _data, + const QString &_mime, const QString &_target, void *_notify, + const KParts::URLArgs& _args, bool _forceNotify = false) + { url=_url; mime=_mime; target=_target; + notify=_notify; post=true; data=_data; args=_args; + forceNotify = _forceNotify; } + + QString url; + QString mime; + QString target; + QByteArray data; + bool post; + bool forceNotify; + bool reload; + void *notify; + KParts::URLArgs args; + }; + + NPWindow _win; + NPSetWindowCallbackStruct _win_info; + QPtrQueue<Request> _waitingRequests; + QMap<int, Request*> _jsrequests; +}; + + +class NSPluginClass : public QObject, virtual public NSPluginClassIface +{ + Q_OBJECT +public: + + NSPluginClass( const QString &library, QObject *parent, const char *name=0 ); + ~NSPluginClass(); + + QString getMIMEDescription(); + DCOPRef newInstance(QString url, QString mimeType, Q_INT8 embed, + QStringList argn, QStringList argv, + QString appId, QString callbackId, Q_INT8 reload, Q_INT8 post, + QByteArray postData, Q_UINT32 xembed ); + void destroyInstance( NSPluginInstance* inst ); + bool error() { return _error; } + + void setApp(const QCString& app) { _app = app; } + const QCString& app() const { return _app; } + +protected slots: + void timer(); + +private: + int initialize(); + void shutdown(); + + KLibrary *_handle; + QString _libname; + bool _constructed; + bool _error; + QTimer *_timer; + + NP_GetMIMEDescriptionUPP *_NP_GetMIMEDescription; + NP_InitializeUPP *_NP_Initialize; + NP_ShutdownUPP *_NP_Shutdown; + + NPPluginFuncs _pluginFuncs; + NPNetscapeFuncs _nsFuncs; + + QPtrList<NSPluginInstance> _instances; + QPtrList<NSPluginInstance> _trash; + + QCString _app; +}; + + +class NSPluginViewer : public QObject, virtual public NSPluginViewerIface +{ + Q_OBJECT +public: + NSPluginViewer( QCString dcopId, QObject *parent, const char *name=0 ); + virtual ~NSPluginViewer(); + + void shutdown(); + DCOPRef newClass( QString plugin ); + +private slots: + void appUnregistered(const QCString& id); + +private: + QDict<NSPluginClass> _classes; +}; + + +#endif diff --git a/nsplugins/viewer/qxteventloop.cpp b/nsplugins/viewer/qxteventloop.cpp new file mode 100644 index 000000000..856cfe67c --- /dev/null +++ b/nsplugins/viewer/qxteventloop.cpp @@ -0,0 +1,472 @@ +/**************************************************************************** +** Implementation of QWidget class +** +** Created : 931031 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the Xt extension of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include <config.h> + +#include "qxteventloop.h" + +#if QT_VERSION >= 0x030100 + +#include <qapplication.h> +#include <qwidgetintdict.h> +#include <kglobal.h> + +// resolve the conflict between X11's FocusIn and QEvent::FocusIn +const int XFocusOut = FocusOut; +const int XFocusIn = FocusIn; +#undef FocusOut +#undef FocusIn + +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyPress +#undef KeyRelease + +Boolean qmotif_event_dispatcher( XEvent *event ); + +class QXtEventLoopPrivate +{ +public: + QXtEventLoopPrivate(); + + void hookMeUp(); + void unhook(); + + XtAppContext appContext, ownContext; + QMemArray<XtEventDispatchProc> dispatchers; + QWidgetIntDict mapper; + + QIntDict<QSocketNotifier> socknotDict; + bool activate_timers; + XtIntervalId timerid; + + // arguments for Xt display initialization + const char* applicationClass; + XrmOptionDescRec* options; + int numOptions; +}; +static QXtEventLoopPrivate *static_d = 0; +static XEvent* last_xevent = 0; + + +/*! \internal + Redeliver the given XEvent to Xt. + + Rationale: An XEvent handled by Qt does not go through the Xt event + handlers, and the internal state of Xt/Motif widgets will not be + updated. This function should only be used if an event delivered by + Qt to a QWidget needs to be sent to an Xt/Motif widget. +*/ +bool QXtEventLoop::redeliverEvent( XEvent *event ) +{ + // redeliver the event to Xt, NOT through Qt + if ( static_d->dispatchers[ event->type ]( event ) ) + return TRUE; + return FALSE; +} + + +/*!\internal + */ +XEvent* QXtEventLoop::lastEvent() +{ + return last_xevent; +} + + +QXtEventLoopPrivate::QXtEventLoopPrivate() + : appContext(NULL), ownContext(NULL), + activate_timers(FALSE), timerid(0) +{ +} + +void QXtEventLoopPrivate::hookMeUp() +{ + // worker to plug Qt into Xt (event dispatchers) + // and Xt into Qt (QXtEventLoopEventLoop) + + // ### TODO extensions? + dispatchers.resize( LASTEvent ); + dispatchers.fill( 0 ); + int et; + for ( et = 2; et < LASTEvent; et++ ) + dispatchers[ et ] = + XtSetEventDispatcher( QPaintDevice::x11AppDisplay(), + et, ::qmotif_event_dispatcher ); +} + +void QXtEventLoopPrivate::unhook() +{ + // unhook Qt from Xt (event dispatchers) + // unhook Xt from Qt? (QXtEventLoopEventLoop) + + // ### TODO extensions? + int et; + for ( et = 2; et < LASTEvent; et++ ) + (void) XtSetEventDispatcher( QPaintDevice::x11AppDisplay(), + et, dispatchers[ et ] ); + dispatchers.resize( 0 ); + + /* + We cannot destroy the app context here because it closes the X + display, something QApplication does as well a bit later. + if ( ownContext ) + XtDestroyApplicationContext( ownContext ); + */ + appContext = ownContext = 0; +} + +extern bool qt_try_modal( QWidget *, XEvent * ); // defined in qapplication_x11.cpp +Boolean qmotif_event_dispatcher( XEvent *event ) +{ + QApplication::sendPostedEvents(); + + QWidgetIntDict *mapper = &static_d->mapper; + QWidget* qMotif = mapper->find( event->xany.window ); + if ( !qMotif && QWidget::find( event->xany.window) == 0 ) { + // event is not for Qt, try Xt + Display* dpy = QPaintDevice::x11AppDisplay(); + Widget w = XtWindowToWidget( dpy, event->xany.window ); + while ( w && ! ( qMotif = mapper->find( XtWindow( w ) ) ) ) { + if ( XtIsShell( w ) ) { + break; + } + w = XtParent( w ); + } + + if ( qMotif && + ( event->type == XKeyPress || event->type == XKeyRelease ) ) { + // remap key events + event->xany.window = qMotif->winId(); + } + } + + last_xevent = event; + bool delivered = ( qApp->x11ProcessEvent( event ) != -1 ); + last_xevent = 0; + if ( qMotif ) { + switch ( event->type ) { + case EnterNotify: + case LeaveNotify: + event->xcrossing.focus = False; + delivered = FALSE; + break; + case XKeyPress: + case XKeyRelease: + delivered = TRUE; + break; + case XFocusIn: + case XFocusOut: + delivered = FALSE; + break; + default: + delivered = FALSE; + break; + } + } + + if ( delivered ) + return True; + + + if ( QApplication::activePopupWidget() ) + // we get all events through the popup grabs. discard the event + return True; + + if ( qMotif && QApplication::activeModalWidget() ) { + if ( !qt_try_modal(qMotif, event) ) + return True; + + } + + if ( static_d->dispatchers[ event->type ]( event ) ) + // Xt handled the event. + return True; + + return False; +} + + + +/*! + \class QXtEventLoop + \brief The QXtEventLoop class is the core behind the Motif Extension. + + \extension Motif + + QXtEventLoop only provides a few public functions, but is the brains + behind the integration. QXtEventLoop is responsible for initializing + the Xt toolkit and the Xt application context. It does not open a + connection to the X server, this is done by using QApplication. + + The only member function in QXtEventLoop that depends on an X server + connection is QXtEventLoop::initialize(). QXtEventLoop must be created before + QApplication. + + Example usage of QXtEventLoop and QApplication: + + \code + static char *resources[] = { + ... + }; + + int main(int argc, char **argv) + { + QXtEventLoop integrator( "AppClass" ); + XtAppSetFallbackResources( integrator.applicationContext(), + resources ); + QApplication app( argc, argv ); + + ... + + return app.exec(); + } + \endcode +*/ + +/*! + Creates QXtEventLoop, which allows Qt and Xt/Motif integration. + + If \a context is NULL, QXtEventLoop creates a default application context + itself. The context is accessible through applicationContext(). + + All arguments passed to this function (\a applicationClass, \a + options and \a numOptions) are used to call XtDisplayInitialize() + after QApplication has been constructed. +*/ + + + +QXtEventLoop::QXtEventLoop( const char *applicationClass, XtAppContext context, XrmOptionDescRec *options , int numOptions) +{ +#if defined(QT_CHECK_STATE) + if ( static_d ) + qWarning( "QXtEventLoop: should only have one QXtEventLoop instance!" ); +#endif + + d = static_d = new QXtEventLoopPrivate; + XtToolkitInitialize(); + if ( context ) + d->appContext = context; + else + d->ownContext = d->appContext = XtCreateApplicationContext(); + + d->applicationClass = applicationClass; + d->options = options; + d->numOptions = numOptions; +} + + +/*! + Destroys QXtEventLoop. +*/ +QXtEventLoop::~QXtEventLoop() +{ + // d->unhook(); + delete d; +} + +/*! + Returns the application context. +*/ +XtAppContext QXtEventLoop::applicationContext() const +{ + return d->appContext; +} + + +void QXtEventLoop::appStartingUp() +{ + int argc = qApp->argc(); + XtDisplayInitialize( d->appContext, + QPaintDevice::x11AppDisplay(), + qApp->name(), + d->applicationClass, + d->options, + d->numOptions, + &argc, + qApp->argv() ); + d->hookMeUp(); +} + +void QXtEventLoop::appClosingDown() +{ + d->unhook(); +} + + +/*!\internal + */ +void QXtEventLoop::registerWidget( QWidget* w ) +{ + if ( !static_d ) + return; + static_d->mapper.insert( w->winId(), w ); +} + + +/*!\internal + */ +void QXtEventLoop::unregisterWidget( QWidget* w ) +{ + if ( !static_d ) + return; + static_d->mapper.remove( w->winId() ); +} + + +/*! \internal + */ +void qmotif_socknot_handler( XtPointer pointer, int *, XtInputId *id ) +{ + QXtEventLoop *eventloop = (QXtEventLoop *) pointer; + QSocketNotifier *socknot = static_d->socknotDict.find( *id ); + if ( ! socknot ) // this shouldn't happen + return; + eventloop->setSocketNotifierPending( socknot ); +} + +/*! \reimp + */ +void QXtEventLoop::registerSocketNotifier( QSocketNotifier *notifier ) +{ + XtInputMask mask; + switch ( notifier->type() ) { + case QSocketNotifier::Read: + mask = XtInputReadMask; + break; + + case QSocketNotifier::Write: + mask = XtInputWriteMask; + break; + + case QSocketNotifier::Exception: + mask = XtInputExceptMask; + break; + + default: + qWarning( "QXtEventLoopEventLoop: socket notifier has invalid type" ); + return; + } + + XtInputId id = XtAppAddInput( d->appContext, + notifier->socket(), (XtPointer) mask, + qmotif_socknot_handler, this ); + d->socknotDict.insert( id, notifier ); + + QEventLoop::registerSocketNotifier( notifier ); +} + +/*! \reimp + */ +void QXtEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier ) +{ + QIntDictIterator<QSocketNotifier> it( d->socknotDict ); + while ( it.current() && notifier != it.current() ) + ++it; + if ( ! it.current() ) { + // this shouldn't happen + qWarning( "QXtEventLoopEventLoop: failed to unregister socket notifier" ); + return; + } + + XtRemoveInput( it.currentKey() ); + d->socknotDict.remove( it.currentKey() ); + + QEventLoop::unregisterSocketNotifier( notifier ); +} + +/*! \internal + */ +void qmotif_timeout_handler( XtPointer, XtIntervalId * ) +{ + static_d->activate_timers = TRUE; + static_d->timerid = 0; +} + +/*! \reimp + */ +bool QXtEventLoop::processEvents( ProcessEventsFlags flags ) +{ + // Qt uses posted events to do lots of delayed operations, like repaints... these + // need to be delivered before we go to sleep + QApplication::sendPostedEvents(); + + // make sure we fire off Qt's timers + int ttw = timeToWait(); + if ( d->timerid != 0 ) { + XtRemoveTimeOut( d->timerid ); + } + d->timerid = 0; + if ( ttw != -1 ) { + d->timerid = + XtAppAddTimeOut( d->appContext, ttw, + qmotif_timeout_handler, 0 ); + } + + // get the pending event mask from Xt and process the next event + XtInputMask pendingmask = XtAppPending( d->appContext ); + XtInputMask mask = pendingmask; + if ( pendingmask & XtIMTimer ) { + mask &= ~XtIMTimer; + // zero timers will starve the Xt X event dispatcher... so process + // something *instead* of a timer first... + if ( mask != 0 ) + XtAppProcessEvent( d->appContext, mask ); + // and process a timer afterwards + mask = pendingmask & XtIMTimer; + } + + if ( ( flags & WaitForMore ) ) + XtAppProcessEvent( d->appContext, XtIMAll ); + else + XtAppProcessEvent( d->appContext, mask ); + + int nevents = 0; + if ( ! ( flags & ExcludeSocketNotifiers ) ) + nevents += activateSocketNotifiers(); + + if ( d->activate_timers ) { + nevents += activateTimers(); + } + d->activate_timers = FALSE; + + return ( (flags & WaitForMore) || ( pendingmask != 0 ) || nevents > 0 ); +} + +#include "qxteventloop.moc" + +#endif + diff --git a/nsplugins/viewer/qxteventloop.h b/nsplugins/viewer/qxteventloop.h new file mode 100644 index 000000000..0bed590b2 --- /dev/null +++ b/nsplugins/viewer/qxteventloop.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Implementation of QWidget class +** +** Created : 931031 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the xt extension of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#ifndef QXTEVENTLOOP_H +#define QXTEVENTLOOP_H + +#include <qglobal.h> + +#if QT_VERSION >= 0x030100 +#include <qeventloop.h> + + + +#include <X11/Intrinsic.h> + +class QXtEventLoopPrivate; + +class QXtEventLoop : public QEventLoop +{ + Q_OBJECT + +public: + QXtEventLoop( const char *applicationClass, XtAppContext context = NULL, XrmOptionDescRec *options = 0, int numOptions = 0); + ~QXtEventLoop(); + + XtAppContext applicationContext() const; + + void registerSocketNotifier( QSocketNotifier * ); + void unregisterSocketNotifier( QSocketNotifier * ); + + static void registerWidget( QWidget* ); + static void unregisterWidget( QWidget* ); + static bool redeliverEvent( XEvent *event ); + static XEvent* lastEvent(); + +protected: + bool processEvents( ProcessEventsFlags flags ); + +private: + void appStartingUp(); + void appClosingDown(); + QXtEventLoopPrivate *d; + +}; + +#endif + +#endif // QMOTIF_H diff --git a/nsplugins/viewer/resolve.h b/nsplugins/viewer/resolve.h new file mode 100644 index 000000000..d3c747ed2 --- /dev/null +++ b/nsplugins/viewer/resolve.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#define RESOLVE_RETVAL(fname,error) \ + kdDebug() << "NSPluginInstance::" << endl; \ + \ + if (!_handle) \ + return error; \ + \ + if (!func_ ## fname) \ + func_ ## fname = _handle->symbol("NPP_"#fname); \ + \ + if (!func_ ## fname) \ + { \ + kdDebug() << "Failed: NPP_" << endl; \ + return error; \ + } \ + kdDebug() << "Resolved NPP_" << endl; + + +#define RESOLVE(fname) RESOLVE_RETVAL(fname, NPERR_GENERIC_ERROR) +#define RESOLVE_VOID(fname) RESOLVE_RETVAL(fname, ;) + + +#define CHECK(fname,error) \ + kdDebug() << "Result of " << #fname << ":" << error << endl; \ + return error; + + diff --git a/nsplugins/viewer/viewer.cpp b/nsplugins/viewer/viewer.cpp new file mode 100644 index 000000000..3e23a944a --- /dev/null +++ b/nsplugins/viewer/viewer.cpp @@ -0,0 +1,312 @@ +/* + + This is a standalone application that executes Netscape plugins. + + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <mhk@caldera.de> + Stefan Schimanski <1Stein@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#include <config.h> + +#include "nsplugin.h" + +#include <dcopclient.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <qptrlist.h> +#include <qsocketnotifier.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <unistd.h> + +#ifdef Bool +#undef Bool +#endif +#include <kconfig.h> + +#if QT_VERSION < 0x030100 +#include "kxt.h" +#include <X11/Intrinsic.h> +#include <X11/Shell.h> +#else +#include "qxteventloop.h" +#include "glibevents.h" +#endif + +/** + * Use RLIMIT_DATA on systems that don't define RLIMIT_AS, + * such as FreeBSD 4. + */ + +#ifndef RLIMIT_AS +#define RLIMIT_AS RLIMIT_DATA +#endif + +/** + * The error handler catches all X errors, writes the error + * message to the debug log and continues. + * + * This is done to prevent abortion of the plugin viewer + * in case the plugin does some invalid X operation. + * + */ +static int x_errhandler(Display *dpy, XErrorEvent *error) +{ + char errstr[256]; + XGetErrorText(dpy, error->error_code, errstr, 256); + kdDebug(1430) << "Detected X Error: " << errstr << endl; + return 1; +} + +/* + * As the plugin viewer needs to be a motif application, I give in to + * the "old style" and keep lot's of global vars. :-) + */ + +static QCString g_dcopId; + +/** + * parseCommandLine - get command line parameters + * + */ +void parseCommandLine(int argc, char *argv[]) +{ + for (int i=0; i<argc; i++) + { + if (!strcmp(argv[i], "-dcopid") && (i+1 < argc)) + { + g_dcopId = argv[i+1]; + i++; + } + } +} + +#if QT_VERSION < 0x030100 + +static XtAppContext g_appcon; +static bool g_quit = false; + +void quitXt() +{ + g_quit = true; +} + + +/** + * socket notifier handling + * + */ + +struct SocketNot +{ + int fd; + QObject *obj; + XtInputId id; +}; + +QPtrList<SocketNot> _notifiers[3]; + +/** + * socketCallback - send event to the socket notifier + * + */ +void socketCallback(void *client_data, int* /*source*/, XtInputId* /*id*/) +{ + kdDebug(1430) << "-> socketCallback( client_data=" << client_data << " )" << endl; + + QEvent event( QEvent::SockAct ); + SocketNot *socknot = (SocketNot *)client_data; + kdDebug(1430) << "obj=" << (void*)socknot->obj << endl; + QApplication::sendEvent( socknot->obj, &event ); + + kdDebug(1430) << "<- socketCallback" << endl; +} + + +/** + * qt_set_socket_handler - redefined internal qt function to register sockets + * The linker looks in the main binary first and finds this implementation before + * the original one in Qt. I hope this works with every dynamic library loader on any OS. + * + */ +extern bool qt_set_socket_handler( int, int, QObject *, bool ); +bool qt_set_socket_handler( int sockfd, int type, QObject *obj, bool enable ) +{ + if ( sockfd < 0 || type < 0 || type > 2 || obj == 0 ) { +#if defined(CHECK_RANGE) + qWarning( "QSocketNotifier: Internal error" ); +#endif + return FALSE; + } + + XtPointer inpMask = 0; + + switch (type) { + case QSocketNotifier::Read: inpMask = (XtPointer)XtInputReadMask; break; + case QSocketNotifier::Write: inpMask = (XtPointer)XtInputWriteMask; break; + case QSocketNotifier::Exception: inpMask = (XtPointer)XtInputExceptMask; break; + default: return FALSE; + } + + if (enable) { + SocketNot *sn = new SocketNot; + sn->obj = obj; + sn->fd = sockfd; + + if( _notifiers[type].isEmpty() ) { + _notifiers[type].insert( 0, sn ); + } else { + SocketNot *p = _notifiers[type].first(); + while ( p && p->fd > sockfd ) + p = _notifiers[type].next(); + +#if defined(CHECK_STATE) + if ( p && p->fd==sockfd ) { + static const char *t[] = { "read", "write", "exception" }; + qWarning( "QSocketNotifier: Multiple socket notifiers for " + "same socket %d and type %s", sockfd, t[type] ); + } +#endif + if ( p ) + _notifiers[type].insert( _notifiers[type].at(), sn ); + else + _notifiers[type].append( sn ); + } + + sn->id = XtAppAddInput( g_appcon, sockfd, inpMask, socketCallback, sn ); + + } else { + + SocketNot *sn = _notifiers[type].first(); + while ( sn && !(sn->obj == obj && sn->fd == sockfd) ) + sn = _notifiers[type].next(); + if ( !sn ) // not found + return FALSE; + + XtRemoveInput( sn->id ); + _notifiers[type].remove(); + } + + return TRUE; +} +#endif + + +int main(int argc, char** argv) +{ + // nspluginviewer is a helper app, it shouldn't do session management at all + setenv( "SESSION_MANAGER", "", 1 ); + + // trap X errors + kdDebug(1430) << "1 - XSetErrorHandler" << endl; + XSetErrorHandler(x_errhandler); + setvbuf( stderr, NULL, _IONBF, 0 ); + + kdDebug(1430) << "2 - parseCommandLine" << endl; + parseCommandLine(argc, argv); + +#if QT_VERSION < 0x030100 + // Create application + kdDebug(1430) << "3 - XtToolkitInitialize" << endl; + XtToolkitInitialize(); + g_appcon = XtCreateApplicationContext(); + Display *dpy = XtOpenDisplay(g_appcon, NULL, "nspluginviewer", "nspluginviewer", + 0, 0, &argc, argv); + + _notifiers[0].setAutoDelete( TRUE ); + _notifiers[1].setAutoDelete( TRUE ); + _notifiers[2].setAutoDelete( TRUE ); + + kdDebug(1430) << "4 - KXtApplication app" << endl; + KLocale::setMainCatalogue("nsplugin"); + KXtApplication app(dpy, argc, argv, "nspluginviewer"); +#else + kdDebug(1430) << "3 - create QXtEventLoop" << endl; + QXtEventLoop integrator( "nspluginviewer" ); + parseCommandLine(argc, argv); + KLocale::setMainCatalogue("nsplugin"); + + kdDebug(1430) << "4 - create KApplication" << endl; + KApplication app( argc, argv, "nspluginviewer" ); + GlibEvents glibevents; +#endif + + { + KConfig cfg("kcmnspluginrc", true); + cfg.setGroup("Misc"); + int v = KCLAMP(cfg.readNumEntry("Nice Level", 0), 0, 19); + if (v > 0) { + nice(v); + } + v = cfg.readNumEntry("Max Memory", 0); + if (v > 0) { + rlimit rl; + memset(&rl, 0, sizeof(rl)); + if (0 == getrlimit(RLIMIT_AS, &rl)) { + rl.rlim_cur = kMin(v, int(rl.rlim_max)); + setrlimit(RLIMIT_AS, &rl); + } + } + } + + // initialize the dcop client + kdDebug(1430) << "5 - app.dcopClient" << endl; + DCOPClient *dcop = app.dcopClient(); + if (!dcop->attach()) + { + KMessageBox::error(NULL, + i18n("There was an error connecting to the Desktop " + "communications server. Please make sure that " + "the 'dcopserver' process has been started, and " + "then try again."), + i18n("Error Connecting to DCOP Server")); + exit(1); + } + + kdDebug(1430) << "6 - dcop->registerAs" << endl; + if (g_dcopId) + g_dcopId = dcop->registerAs( g_dcopId, false ); + else + g_dcopId = dcop->registerAs("nspluginviewer"); + + dcop->setNotifications(true); + + // create dcop interface + kdDebug(1430) << "7 - new NSPluginViewer" << endl; + NSPluginViewer *viewer = new NSPluginViewer( "viewer", 0 ); + + // start main loop +#if QT_VERSION < 0x030100 + kdDebug(1430) << "8 - XtAppProcessEvent" << endl; + while (!g_quit) + XtAppProcessEvent( g_appcon, XtIMAll); +#else + kdDebug(1430) << "8 - app.exec()" << endl; + app.exec(); +#endif + + // delete viewer + delete viewer; +} |