diff --git a/mozilla/modules/plugin/SanePlugin/Makefile.in b/mozilla/modules/plugin/SanePlugin/Makefile.in new file mode 100644 index 00000000000..8c9678de5ac --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/Makefile.in @@ -0,0 +1,57 @@ +############################################################################ +## Makefile.in (Generic SANE Plugin Tree) +## +## The contents of this file are subject to the Mozilla Public +## License Version 1.1 (the "License"); you may not use this file +## except in compliance with the License. You may obtain a copy of +## the License at http://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS +## IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +## implied. See the License for the specific language governing +## rights and limitations under the License. +## Contributor(s): +## +## Rusty Lynch +############################################################################ + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = SanePlugin +LIBRARY_NAME = Sane +IS_COMPONENT = 1 + +REQUIRES = raptor xpcom + +CPPSRCS = \ + nsSanePluginFactory.cpp \ + nsSanePlugin.cpp \ + $(NULL) + +LOCAL_INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../../public \ + -I/usr/lib/glib/include + +EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lgtksuperwin \ + $(TK_LIBS) -lsane -ljpeg + +XPIDLSRCS = nsSanePluginControl.idl + +include $(topsrcdir)/config/rules.mk + +install:: $(TARGETS) + $(INSTALL) $(srcdir)/test/camera.html $(DIST)/bin/res/samples + $(INSTALL) $(srcdir)/test/scanner.html $(DIST)/bin/res/samples + $(INSTALL) $(srcdir)/test/camera.html /home/httpd/html/test + $(INSTALL) $(srcdir)/test/scanner.html /home/httpd/html/test + +superclean: clean + rm -f *~ test/*~ + + + + diff --git a/mozilla/modules/plugin/SanePlugin/README b/mozilla/modules/plugin/SanePlugin/README new file mode 100644 index 00000000000..68e649eb637 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/README @@ -0,0 +1,45 @@ +What is this plug-in? +--------------------- + +This is a Linux/Mozilla plug-in that enables a scanner/digital +camera application to be created with JavaScript. To this end +the SANE plug-in provides two things: + + 1. I have attempted to export all of the SANE API's + from the SANE native libraries to JavaScript. + (I have provided two brain-dead samples for a + CPia USB digital camera and HP 6300 scanner.) + + 2. A drawing surface for displaying the scanned image. + +===================================================================== + +Why did you bother? +------------------ + +Originally, I used this plug-in to cut my teeth on the new Mozilla +XPCom plug-in model. Now I am hoping to provide a Linux/Mozilla +plug-in sample that exercises most of the functionality that the +Linux community would want to use (like connecting JavaScript +to native code.) + +===================================================================== + +You know, Linux isn't the only UN*X around! +------------------------------------------- + +I say Linux because that is the only platform I have built/tested on. +SANE is supported by a long list of UN*X OS's so there is no reason +that other platforms shouldn't work with this plug-in. + +(I have tried to stick to NSPR functions for this reason, but it's easy +to over look a couple of Linux specific functions.) + +====================================================================== + +How do I build this beast? +------------------------- + +In order to build this plug-in, you need the SANE (Scanner Access +Now Easy) libraries/headers installed on your system. For more +info on SANE, go to http://www.mostang.com/sane. diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePlugin.cpp b/mozilla/modules/plugin/SanePlugin/nsSanePlugin.cpp new file mode 100644 index 00000000000..7ad44b58db4 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePlugin.cpp @@ -0,0 +1,2643 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Implements the nsSanePluginInterface for the SANE plugin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nsIIOService.h" +#include "nsSanePlugin.h" +#include "plstr.h" +#include "prmem.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" + +extern "C" +{ +#include "jpeglib.h" +#include "sane/sane.h" +#include "sane/sanei.h" +#include "sane/saneopts.h" +} + +#define MAX_OPT_SIZE (1024 * 2) + +struct my_JPEG_error_mgr +{ + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; +}; +typedef struct my_JPEG_error_mgr *emptr; + +static void PR_CALLBACK ThreadedDestroyEvent(PLEvent* aEvent); +static void PR_CALLBACK ThreadedHandleEvent(PLEvent* aEvent); + +//static void PR_CALLBACK scanimage_thread_routine( void * arg ); + +static void store_filename(GtkFileSelection *selector, gpointer user_data); +static nsresult optioncat( char ** dest, const char * src, + const char * ending ); +static void my_jpeg_error_exit (j_common_ptr cinfo); +static unsigned char* jpeg_file_to_rgb (FILE * f, int *w, int *h); +static gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data); +static unsigned char * crop_image(unsigned char *rgb_data, int *rgb_width, + int *rgb_height, gint x, gint y, + gint w, gint h); +static unsigned char * scale_image(unsigned char *rgb_data, int rgb_width, + int rgb_height, int w, int h); + +static PRInt32 gPluginObjectCount = 0; + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID ); +static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID ); +static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID ); +static NS_DEFINE_IID(knsSanePluginControlIID, NS_ISANEPLUGININSTANCE_IID ); +static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID ); +static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID ); +static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID ); +static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID ); + + +/////////////////////////////////////////////////////////////////////////////// +// Plugin Instance API Methods + +nsSanePluginInstance::nsSanePluginInstance( void ) + : fPeer( NULL ), fWindow( NULL ), fMode( nsPluginMode_Embedded ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::nsSanePluginInstance()\n"); +#endif + + NS_INIT_REFCNT(); + PR_AtomicIncrement(&gPluginObjectCount); + + // set default jpeg compression attributes + mCompQuality = 10; + mCompMethod = JDCT_FASTEST; + + // Initialize sane interface + mSaneDevice = nsnull; + mSaneOpen = SANE_FALSE; + mRGBData = nsnull; + mBottomRightXChange = mBottomRightYChange = + mTopLeftXChange = mTopLeftYChange = 0; + + // initialize JavaScript callback members + mOnScanCompleteScript = nsnull; + mOnInitCompleteScript = nsnull; + mScanThread = nsnull; + mUIThread = PR_CurrentThread(); + + mState = 0; // idle + mSuccess = 1; // success + mFileSelection = nsnull; + + // initialize gtk widgets + fPlatform.widget = nsnull; + mEvent_box = nsnull; + mDrawing_area = nsnull; + + if(nsComponentManager::CreateInstance(kCPluginManagerCID, + NULL, kIPluginManagerIID, + (void**)&mPluginManager) != NS_OK) { + NS_ERROR("Error trying to create plugin manager!"); + return; + } +} + +nsSanePluginInstance::~nsSanePluginInstance(void) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::~nsSanePluginInstance(%i)\n", + gPluginObjectCount ); +#endif + + PR_AtomicDecrement(&gPluginObjectCount); + PlatformDestroy(); // Perform platform specific cleanup + + if (mPluginManager) { + mPluginManager->Release(); + mPluginManager = nsnull; + } + + sane_exit(); + + // cleanup old widgets + if (fPlatform.widget) + gtk_widget_destroy(fPlatform.widget); + if (mEvent_box) + gtk_widget_destroy(mEvent_box); + if (mDrawing_area) + gtk_widget_destroy(mDrawing_area); + + NS_IF_RELEASE(fPeer); + PR_FREEIF(mSaneDevice); + PR_FREEIF(mOnScanCompleteScript); + PR_FREEIF(mOnInitCompleteScript); + +} + +// This macro produces simple versions of QueryInterface, AddRef and Release +// See the nsISupports.h header file for details. +NS_IMPL_ISUPPORTS2(nsSanePluginInstance, + nsIPluginInstance, + nsISanePluginInstance) + +NS_METHOD +nsSanePluginInstance::Initialize( nsIPluginInstancePeer* peer ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Initialize()\n"); +#endif + + NS_ASSERTION(peer != NULL, "null peer"); + + nsIPluginTagInfo * taginfo; + const char * const * names = nsnull; + const char * const * values = nsnull; + PRUint16 count = 0; + nsresult result; + + gdk_rgb_init(); + + fPeer = peer; + peer->AddRef(); + result = peer->GetMode( &fMode ); + + if ( NS_FAILED( result ) ) + return result; + + result = peer->QueryInterface( nsIPluginTagInfo::GetIID(), + ( void ** )&taginfo ); + + if ( !NS_FAILED( result ) ) + { + taginfo->GetAttributes( count, names, values ); + NS_IF_RELEASE( taginfo ); + } + + // Initialize default line attribute info + mLineWidth = 5; + mLineStyle = GDK_LINE_ON_OFF_DASH; + mCapStyle = GDK_CAP_BUTT; + mJoinStyle = GDK_JOIN_MITER; + + // Get user requested attribute info + for (int i=0; i < count; i++) { + + if (PL_strcasecmp(names[i], "line_width") == 0) { + ////////////// + // Line width + sscanf(values[i], "%i", &mLineWidth); + + } else if (PL_strcasecmp(names[i], "line_style") == 0) { + ////////////// + // Line style + if (PL_strcasecmp(values[i], "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(values[i], "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(values[i], "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + } else if (PL_strcasecmp(names[i], "cap_style") == 0) { + ///////////// + // Cap style + if (PL_strcasecmp(values[i], "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(values[i], "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(values[i], "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(values[i], "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + } else if (PL_strcasecmp(names[i], "join_style") == 0) { + ////////////// + // Join style + if (PL_strcasecmp(values[i], "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(values[i], "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(values[i], "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + } else if (PL_strcasecmp(names[i], "device") == 0) { + ////////// + // Device + PR_FREEIF(mSaneDevice); + mSaneDevice = PL_strdup(values[i]); + } else if (PL_strcasecmp(names[i], "onScanComplete") == 0) { + ////////////////////////// + // onScanComplete Callback + PR_FREEIF(mOnScanCompleteScript); + mOnScanCompleteScript = PL_strdup(values[i]); + } else if(PL_strcasecmp(names[i], "onInitComplete") == 0) { + ////////////////////////// + // onInitComplete Callback + PR_FREEIF(mOnInitCompleteScript); + mOnInitCompleteScript = PL_strdup(values[i]); + } + } + + // the mImageFilename is used to store data directly + // from the sane device and also rendering to the screen + sprintf( mImageFilename, "/tmp/nsSanePlugin.%p.jpg", (void *)this ); + + PlatformNew(); /* Call Platform-specific initializations */ + return NS_OK; + +} + +NS_METHOD +nsSanePluginInstance::SaveImage() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::SaveImage()\n"); +#endif + + if (!mImageFilename) { + NS_ERROR("Attempt to save image before initial scan!"); + return NS_ERROR_FAILURE; + } + + mFileSelection = gtk_file_selection_new("Choose file to save image to."); + + // Set a default filename + gtk_file_selection_set_filename(GTK_FILE_SELECTION(mFileSelection), + "saved.jpg"); + + // Callback for getting the filename + gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->ok_button), + "clicked", GTK_SIGNAL_FUNC (store_filename), + (gpointer) this); + + // Ensure the file selection dialog goes away after + // 'OK' or 'CANCEL' button is selected + gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->ok_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT(mFileSelection)); + gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT(mFileSelection)); + + // ok, now show the file selection dialog + gtk_widget_show(mFileSelection); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::DoScanCompleteCallback() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::DoScanCompleteCallback()\n"); +#endif + + char *url; + + if (mOnScanCompleteScript && mPluginManager) { + url = (char *)PR_Malloc(PL_strlen(mOnScanCompleteScript) + 13); + if (!url) + return NS_ERROR_OUT_OF_MEMORY; + + sprintf(url, "javascript:%s", mOnScanCompleteScript); + mPluginManager->GetURL((nsIPluginInstance *)this, url, "_self"); + + PR_FREEIF(url); + } + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::DoInitCompleteCallback() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::DoInitCompleteCallback()\n"); +#endif + + char *url; + + if (mOnInitCompleteScript && mPluginManager) { + url = (char *)PR_Malloc(PL_strlen(mOnInitCompleteScript) + 13); + if (!url) + return NS_ERROR_OUT_OF_MEMORY; + + sprintf(url, "javascript:%s", mOnInitCompleteScript); + mPluginManager->GetURL((nsIPluginInstance *)this, url, "_self"); + + PR_FREEIF(url); + } + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::GetPeer( nsIPluginInstancePeer* *result ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::GetPeer()\n"); +#endif + + fPeer->AddRef(); + *result = fPeer; + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Start( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Start()\n"); +#endif + + PR_FREEIF(mRGBData); + mRGBData = nsnull; + + sane_init(0,0); + mSaneOpen = SANE_FALSE; + nsresult rv = OpenSaneDeviceIF(); + if (rv != NS_OK) { + NS_ERROR("Failed to open device in Start()\n"); + return rv; + } + + PlatformNew(); + + // call onInitComplete callback + DoInitCompleteCallback(); + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Stop( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Stop()\n"); +#endif + + // Wait for scanner operation to complete. + // TODO: Warn user that they are still scanning + // and send abort command to scanner if + // the user wishes. + if (mScanThread) + PR_JoinThread(mScanThread); + + PR_FREEIF(mRGBData); + + // close sane + if (mSaneOpen) { + sane_close(mSaneHandle); + sane_exit(); + mSaneOpen = SANE_FALSE; + } + + if (remove ( mImageFilename ) == -1) + perror(mImageFilename); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Destroy( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Destroy()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::SetWindow(nsPluginWindow* window) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::SetWindow()\n"); +#endif + + nsresult result; + + // The real work is handled in PlatformSetWindow + // (as if this plug-in was multiplatform) + result = PlatformSetWindow( window ); + fWindow = window; + + return result; +} + +NS_METHOD +nsSanePluginInstance::NewStream( nsIPluginStreamListener** listener ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::NewStream()\n"); +#endif + + if ( listener != NULL ) + *listener = new nsSanePluginStreamListener( this ); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Print( nsPluginPrint* printInfo ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Print()\n"); +#endif + + if ( printInfo == NULL ) + return NS_ERROR_FAILURE; + + // needs to be implemented + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD +nsSanePluginInstance::HandleEvent(nsPluginEvent* event, PRBool* handled) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::HandleEvent()\n"); +#endif + + // Not used on unix platform. Only here for potential + // future porting efforts + *handled = (PRBool)PlatformHandleEvent(event); + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::GetValue(nsPluginInstanceVariable variable, void *value) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::GetValue()\n"); +#endif + + //Needs to be implemented + return NS_ERROR_NOT_IMPLEMENTED; +} + +void +nsSanePluginInstance::PlatformNew( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformNew\n"); +#endif + + fPlatform.window = 0; +} + +nsresult +nsSanePluginInstance::PlatformDestroy( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformDestroy()\n"); +#endif + + return NS_OK; +} + +nsresult +nsSanePluginInstance::PlatformSetWindow( nsPluginWindow* window ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformSetWindow()\n"); +#endif + + + if ( window == NULL || window->window == NULL ) + return NS_ERROR_NULL_POINTER; + + if ( fPlatform.superwin == (GdkSuperWin *)window->window ) + return NS_OK; + + // Record windowing info + fPlatform.superwin = (GdkSuperWin *)window->window; + fPlatform.height = window->height; + fPlatform.width = window->width; + + // cleanup old widgets + if (fPlatform.widget) + gtk_widget_destroy(fPlatform.widget); + if (mEvent_box) + gtk_widget_destroy(mEvent_box); + if (mDrawing_area) + gtk_widget_destroy(mDrawing_area); + + fPlatform.widget = gtk_mozbox_new(fPlatform.superwin->bin_window); + + // Initialize the zoom box to outline the entire image + mZoom_box.x = 0; + mZoom_box.y = 0; + mZoom_box.width = fPlatform.width; + mZoom_box.height = fPlatform.height; + + // Setup an event box to contain the drawable + mEvent_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(fPlatform.widget), mEvent_box); + gtk_widget_show(mEvent_box); + + // Setup the drawable + mDrawing_area = ( GtkWidget * )gtk_drawing_area_new(); + gtk_drawing_area_size( GTK_DRAWING_AREA( mDrawing_area ), + window->width, window->height); + gtk_container_add(GTK_CONTAINER(mEvent_box), mDrawing_area); + gtk_widget_show(mDrawing_area); + + // Initialize line attributes for GC + GdkGC * da_gc = mDrawing_area->style->fg_gc[mDrawing_area->state]; + gdk_gc_set_line_attributes ( da_gc, mLineWidth, mLineStyle, + mCapStyle, mJoinStyle); + + // Register signals + gtk_signal_connect (GTK_OBJECT(mDrawing_area), "expose_event", + GTK_SIGNAL_FUNC(draw), this); + + // Now make everything visable + gtk_widget_show( fPlatform.widget ); + + return NS_OK; +} + +int16 +nsSanePluginInstance::PlatformHandleEvent(nsPluginEvent* event) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformHandleEvent()\n"); +#endif + + /* UNIX Plugins do not use HandleEvent */ + return 0; +} + + +NS_IMETHODIMP +nsSanePluginInstance::PaintImage(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::PaintImage(%p)\n", this); +#endif + + if (mDrawing_area && fPlatform.widget) { + if (mImageFilename) { + + if (!mRGBData) { + + if (mState) + // we are currently scanning, so the jpeg file + // is not valid yet + return NS_OK; + + FILE * f = fopen(mImageFilename, "rb"); + if (!f) + // it's possible that an image hasn't been scanned yet + return NS_OK; + + mRGBData = jpeg_file_to_rgb(f, &mRGBWidth, &mRGBHeight); + if (!mRGBData) { + NS_ERROR("Error converting jpeg to rgb data!"); + return NS_ERROR_FAILURE; + } + + fclose(f); + + // scale down to window size + unsigned char * tmp = scale_image( mRGBData, + mRGBWidth, mRGBHeight, + fPlatform.width, + fPlatform.height); + if (!tmp) { + NS_ERROR("Error trying to scale image!"); + return NS_ERROR_FAILURE; + } + + // move to new data + PR_FREEIF(mRGBData); + mRGBData = tmp; + } + + // render the image + gdk_draw_rgb_image (mDrawing_area->window, + mDrawing_area->style->fg_gc[GTK_STATE_NORMAL], + 0, 0, fPlatform.width, fPlatform.height, + GDK_RGB_DITHER_MAX, mRGBData, + fPlatform.width * 3); + + gdk_draw_rectangle (mDrawing_area->window, + mDrawing_area->style->fg_gc[mDrawing_area->state], + FALSE, mZoom_box.x, mZoom_box.y, + mZoom_box.width, mZoom_box.height); + + gdk_flush(); + } + } + + return NS_OK; +} + +char * +nsSanePluginInstance::GetImageFilename() +{ + if (!mImageFilename) + return (char *)NULL; + + // Caller is responsible for freeing string + return PL_strdup(mImageFilename); +} + +GtkWidget * +nsSanePluginInstance::GetFileSelection() +{ + if (!mFileSelection) + return (GtkWidget *)NULL; + + return mFileSelection; +} + +PRBool +nsSanePluginInstance::IsUIThread() +{ + if (!mUIThread) + return PR_FALSE; + + return (mUIThread == PR_CurrentThread()); +} + +// End of Plugin Instance API Methods. +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// Image Preview Interface. + +NS_IMETHODIMP +nsSanePluginInstance::ZoomImage( PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::ZoomImage()\n"); +#endif + + int im_width, im_height, c_width, c_height, c_x, c_y; + + if (!mRGBData) + return NS_ERROR_FAILURE; + + // get the actual image width and height + im_width = mRGBWidth; + im_height = mRGBHeight; + + if ( x >= (PRUint16)fPlatform.width || + y >= (PRUint16)fPlatform.height ) + return NS_ERROR_FAILURE; + + if ( width + mZoom_box.x >= (PRUint16)fPlatform.width ) + mZoom_box.width = fPlatform.width - mZoom_box.x; + + if ( height + mZoom_box.y >= (PRUint16) fPlatform.height ) + mZoom_box.height = fPlatform.height - mZoom_box.y; + + mZoom_box.x = x; + mZoom_box.y = y; + + // translate the zoom box geometry to the image geometry + c_width = (int)(((double)im_width/(double)fPlatform.width) * + (double)mZoom_box.width); + c_height =(int)(((double)im_height/(double)fPlatform.height) * + (double)mZoom_box.height); + c_x = (int)(((double)im_width/(double)fPlatform.width) * + (double)mZoom_box.x); + c_y = (int)(((double)im_height/(double)fPlatform.height) * + (double)mZoom_box.y); + + unsigned char * tmp = crop_image(mRGBData, &mRGBWidth, &mRGBHeight, + c_x, c_y, c_width, c_height); + if (!tmp) + return NS_ERROR_FAILURE; + PR_FREEIF(mRGBData); + mRGBData = tmp; + + mRGBWidth = c_width; + mRGBHeight = c_height; + + mTopLeftXChange = (float)x/(float)fPlatform.width; + mTopLeftYChange = (float)y/(float)fPlatform.height; + mBottomRightXChange = (float)(fPlatform.width - (PRInt32)(x + mZoom_box.width))/(float)fPlatform.width; + mBottomRightYChange = (float)(fPlatform.height - (PRInt32)(y + mZoom_box.height))/(float)fPlatform.height; + + mZoom_box.x = 0; + mZoom_box.y = 0; + mZoom_box.width = (PRUint16)fPlatform.width; + mZoom_box.height = (PRUint16)fPlatform.height; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::ZoomImageWithAttributes( PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height, + PRInt32 req_line_width, + const char * req_line_style, + const char * req_cap_style, + const char * req_join_style ) +{ +#ifdef DEBUG + printf("nsSaneInstance::ZoomImageWithAttributes()\n"); +#endif + + if (!mRGBData) + return NS_ERROR_FAILURE; + + ZoomImage(x, y, width, height); + + /* + * Set zoom box line attributes + */ + + // Line Width + mLineWidth = req_line_width; + + // Line Style + if (PL_strcasecmp(req_line_style, "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(req_line_style, "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(req_line_style, "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + // Cap Style + if (PL_strcasecmp(req_cap_style, "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(req_cap_style, "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(req_cap_style, "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(req_cap_style, "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + // Join Style + if (PL_strcasecmp(req_join_style, "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(req_join_style, "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(req_join_style, "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + // Refresh + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::Crop(PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::Crop()\n"); +#endif + + if ( (x <= (PRUint16)fPlatform.width - width ) && + (y <= (PRUint16)fPlatform.height - height) ) { + + mZoom_box.x = x; mZoom_box.y = y; + mZoom_box.width = width; + mZoom_box.height = height; + + } else { + return NS_ERROR_FAILURE; + } + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::Restore(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::Restore()\n"); +#endif + + PR_FREEIF(mRGBData); + mRGBData = nsnull; + + // Reset zoom box + mZoom_box.x = mZoom_box.y = 0; + mZoom_box.width = (PRUint16) fPlatform.width; + mZoom_box.height = (PRUint16) fPlatform.height; + + PaintImage(); + + return NS_OK; +} + + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomX(PRUint16 * x) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomX()\n"); +#endif + + NS_ASSERTION(x != NULL, "null x pointer"); + if (!x) + return NS_ERROR_NULL_POINTER; + + *x = mZoom_box.x; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomX(PRUint16 x) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomX()\n"); +#endif + + if (x > (PRInt32) fPlatform.width) + mZoom_box.x = (PRInt32) fPlatform.width; + else + mZoom_box.x = x; + + return PaintImage(); +} + + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomY(PRUint16 * y) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomY()\n"); +#endif + + NS_ASSERTION(y != NULL, "null y pointer"); + if (!y) + return NS_ERROR_NULL_POINTER; + + *y = mZoom_box.y; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomY(PRUint16 y) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomY()\n"); +#endif + + if (y > (PRInt32) fPlatform.height) + mZoom_box.y = (PRInt32)fPlatform.height; + else + mZoom_box.y = y; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomWidth(PRUint16 * width) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomWidth()\n"); +#endif + + NS_ASSERTION(width != NULL, "null width pointer"); + if (!width) + return NS_ERROR_NULL_POINTER; + + *width = mZoom_box.width; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomWidth(PRUint16 width) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomWidth()\n"); +#endif + + if (width + mZoom_box.x > (PRUint16)fPlatform.width) + mZoom_box.width = (PRInt32) fPlatform.width - mZoom_box.x; + else + mZoom_box.width = width; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomHeight(PRUint16 * height) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomHeight()\n"); +#endif + + NS_ASSERTION(height != NULL, "null height pointer"); + if (!height) + return NS_ERROR_NULL_POINTER; + + *height = mZoom_box.height; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomHeight(PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomHeight()\n"); +#endif + + if (height + mZoom_box.y > (PRUint16) fPlatform.height) + mZoom_box.height = fPlatform.height - mZoom_box.y; + else + mZoom_box.height = height; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomLineWidth(PRInt32 * aZoomLineWidth) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomLineWidth()\n"); +#endif + + NS_ASSERTION(aZoomLineWidth != NULL, "null width pointer"); + if (!aZoomLineWidth) + return NS_ERROR_NULL_POINTER; + + *aZoomLineWidth = mLineWidth; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomLineWidth(PRInt32 aZoomLineWidth) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomLineWidth()\n"); +#endif + + if (aZoomLineWidth <= 0) + return NS_ERROR_INVALID_ARG; + + mLineWidth = aZoomLineWidth; + + return PaintImage(); +} + +#define MAX_LINE_ATTRIBUTE_LENGTH 20 + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomLineStyle(char ** aZoomLineStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomLineStyle()\n"); +#endif + + NS_ASSERTION(*aZoomLineStyle != NULL, "null width pointer"); + if (!*aZoomLineStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomLineStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomLineStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mLineStyle == GDK_LINE_ON_OFF_DASH) + PL_strcpy(*aZoomLineStyle, "dash"); + else if (mLineStyle == GDK_LINE_SOLID) + PL_strcpy(*aZoomLineStyle, "solid"); + else if (mLineStyle == GDK_LINE_DOUBLE_DASH) + PL_strcpy(*aZoomLineStyle, "double_dash"); + else + PL_strcpy(*aZoomLineStyle, "unknown"); + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomLineStyle(const char * aZoomLineStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomLineStyle()\n"); +#endif + + NS_ASSERTION(aZoomLineStyle != NULL, "null width pointer"); + if (!aZoomLineStyle) + return NS_ERROR_NULL_POINTER; + + // Line Style + if (PL_strcasecmp(aZoomLineStyle, "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(aZoomLineStyle, "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(aZoomLineStyle, "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomCapStyle(char ** aZoomCapStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomCapStyle()\n"); +#endif + + NS_ASSERTION(*aZoomCapStyle != NULL, "null width pointer"); + if (!*aZoomCapStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomCapStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomCapStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mCapStyle == GDK_CAP_ROUND) + PL_strcpy(*aZoomCapStyle, "round"); + else if (mCapStyle == GDK_CAP_PROJECTING) + PL_strcpy(*aZoomCapStyle, "projecting"); + else if (mCapStyle == GDK_CAP_BUTT) + PL_strcpy(*aZoomCapStyle, "butt"); + else if (mCapStyle == GDK_CAP_NOT_LAST) + PL_strcpy(*aZoomCapStyle, "not_last"); + else + PL_strcpy(*aZoomCapStyle, "unknown"); + + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomCapStyle(const char * aZoomCapStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomCapStyle()\n"); +#endif + + NS_ASSERTION(aZoomCapStyle != NULL, "null width pointer"); + if (!aZoomCapStyle) + return NS_ERROR_NULL_POINTER; + + // Cap Style + if (PL_strcasecmp(aZoomCapStyle, "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(aZoomCapStyle, "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(aZoomCapStyle, "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(aZoomCapStyle, "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomJoinStyle(char ** aZoomJoinStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomJoinStyle()\n"); +#endif + + NS_ASSERTION(*aZoomJoinStyle != NULL, "null width pointer"); + if (!*aZoomJoinStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomJoinStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomJoinStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mJoinStyle == GDK_JOIN_MITER) + PL_strcpy(*aZoomJoinStyle, "miter"); + else if (mJoinStyle == GDK_JOIN_ROUND) + PL_strcpy(*aZoomJoinStyle, "round"); + else if (mJoinStyle == GDK_JOIN_BEVEL) + PL_strcpy(*aZoomJoinStyle, "bevel"); + else + PL_strcpy(*aZoomJoinStyle, "unknown"); + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomJoinStyle(const char * aZoomJoinStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomJoinStyle()\n"); +#endif + + NS_ASSERTION(aZoomJoinStyle != NULL, "null width pointer"); + if (!aZoomJoinStyle) + return NS_ERROR_NULL_POINTER; + + // Join Style + if (PL_strcasecmp(aZoomJoinStyle, "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(aZoomJoinStyle, "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(aZoomJoinStyle, "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomBR_XChange(float * aZoomBR_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomBR_XChange()\n"); +#endif + + *aZoomBR_XChange = mBottomRightXChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomBR_XChange(float aZoomBR_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomBR_XChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomBR_YChange(float * aZoomBR_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomBR_YChange()\n"); +#endif + + *aZoomBR_YChange = mBottomRightYChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomBR_YChange(float aZoomBR_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomBR_YChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomTL_XChange(float * aZoomTL_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomTL_XChange()\n"); +#endif + + *aZoomTL_XChange = mTopLeftXChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomTL_XChange(float aZoomTL_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomTL_XChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomTL_YChange(float * aZoomTL_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomTL_YChange()\n"); +#endif + + *aZoomTL_YChange = mTopLeftYChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomTL_YChange(float aZoomTL_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomTL_YChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetQuality(PRInt32 * aQuality) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetQuality()\n"); +#endif + + NS_ASSERTION(aQuality != NULL, "null compression quality pointer"); + if (!aQuality) + return NS_ERROR_NULL_POINTER; + + *aQuality = mCompQuality; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetQuality(PRInt32 aQuality) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetQuality()\n"); +#endif + + NS_ASSERTION(aQuality > -1 && aQuality < 101, + "Invalid compression quality"); + if (aQuality < 0 || aQuality > 100) + return NS_ERROR_INVALID_ARG; + + mCompQuality = aQuality; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetMethod(char ** aMethod) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetMethod()\n"); +#endif + + NS_ASSERTION(aMethod != NULL, "Null method pointer"); + if (!aMethod) + return NS_ERROR_NULL_POINTER; + + *aMethod = (char*) nsAllocator::Alloc(8); + if (!*aMethod) + return NS_ERROR_OUT_OF_MEMORY; + + switch (mCompMethod) { + case JDCT_ISLOW: + PL_strcpy(*aMethod, "SLOW"); + break; + case JDCT_IFAST: + PL_strcpy(*aMethod, "FAST"); + break; + case JDCT_FLOAT: + PL_strcpy(*aMethod, "FLOAT"); + break; + default: + PL_strcpy(*aMethod, "DEFAULT"); + + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetMethod(const char * aMethod) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetMethod()\n"); +#endif + + NS_ASSERTION(aMethod != NULL, "Null method pointer"); + if (!aMethod) + return NS_ERROR_NULL_POINTER; + + if (PL_strcasecmp(aMethod, "SLOW") == 0) + mCompMethod = JDCT_ISLOW; + else if (PL_strcasecmp(aMethod, "FAST") == 0) + mCompMethod = JDCT_IFAST; + else if (PL_strcasecmp(aMethod, "FLOAT") == 0) + mCompMethod = JDCT_FLOAT; + else + mCompMethod = JDCT_DEFAULT; + + return NS_OK; +} + +// END of Image Preview Interface +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// Generic SANE Interface + +NS_IMETHODIMP +nsSanePluginInstance::GetDeviceOptions(char ** aDeviceOptions) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetDeviceOptions()\n"); +#endif + + const SANE_Option_Descriptor * optdesc; + SANE_Int string_size; + char *range_string, *word_string; + nsresult rv; + + NS_ASSERTION(aDeviceOptions != NULL, "null options pointer"); + if (!aDeviceOptions) + return NS_ERROR_NULL_POINTER; + + *aDeviceOptions = nsnull; + + rv = OpenSaneDeviceIF(); + if (rv != NS_OK) + return rv; + + // Find out how many options exist for device + optdesc = sane_get_option_descriptor(mSaneHandle, 0); + if (!optdesc || optdesc->size == 0) { + NS_ERROR("Error trying to get number of options for device!"); + return NS_ERROR_FAILURE; + } + + if (optdesc->size == 1) { /* size includes "size" option */ + NS_ERROR("Hmm.. That's odd! No options for this device?"); + return NS_OK; // I guess it's possible? + } + + // allocate string to contain data to return to caller + string_size = ((optdesc->size - 1) * MAX_OPT_SIZE) + 1; + *aDeviceOptions = (char*) nsAllocator::Alloc(string_size); + + // initialize options string + for (int n=0; ntype == SANE_TYPE_GROUP) + continue; + + /* + * WARNING! Need to replace ','s and ';'s with + * something else so that JavaScript parsing + * doesn't get hosed! + */ + + // NAME + if (optdesc->name) + optioncat(aDeviceOptions, optdesc->name, ", "); + else + strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // TITLE + if (optdesc->title) + optioncat(aDeviceOptions, optdesc->title, ", "); + else + PL_strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // DESCRIPTION + if (optdesc->desc) + optioncat(aDeviceOptions, optdesc->desc, ", "); + else + PL_strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // Option Type + switch (optdesc->type) { + + case SANE_TYPE_BOOL: + PL_strcat(*aDeviceOptions, "BOOL, "); + break; + + case SANE_TYPE_INT: + PL_strcat(*aDeviceOptions, "INT, "); + break; + + case SANE_TYPE_FIXED: + PL_strcat(*aDeviceOptions, "FIXED, "); + break; + + case SANE_TYPE_STRING: + PL_strcat(*aDeviceOptions, "STRING, "); + break; + + case SANE_TYPE_BUTTON: + PL_strcat(*aDeviceOptions, "BUTTON, "); + break; + + case SANE_TYPE_GROUP: + PL_strcat(*aDeviceOptions, "GROUP, "); + break; + + default: + PL_strcat(*aDeviceOptions, "UNKNOWN, "); + } + + //////////////////////////////////////// + // UNIT + switch (optdesc->unit) { + case SANE_UNIT_NONE: + PL_strcat(*aDeviceOptions, "NONE, "); + break; + + case SANE_UNIT_PIXEL: + PL_strcat(*aDeviceOptions, "PIXEL, "); + break; + + case SANE_UNIT_BIT: + PL_strcat(*aDeviceOptions, "BIT, "); + break; + + case SANE_UNIT_MM: + PL_strcat(*aDeviceOptions, "MM, "); + break; + + case SANE_UNIT_DPI: + PL_strcat(*aDeviceOptions, "PERCENT, "); + break; + + case SANE_UNIT_MICROSECOND: + PL_strcat(*aDeviceOptions, "MICROSECOND, "); + break; + + default: + PL_strcat(*aDeviceOptions, "UNKNOWN, "); + } + + //////////////////////////////////////// + // SIZE + char * size_option = (char *)PR_MALLOC(64); + if (!size_option) + return NS_ERROR_OUT_OF_MEMORY; + sprintf(size_option, "%i, ", optdesc->size); + PL_strcat(*aDeviceOptions, size_option); + PR_FREEIF(size_option); + + //////////////////////////////////////// + // ACTIVE? + if (SANE_OPTION_IS_ACTIVE(optdesc->cap)) + PL_strcat(*aDeviceOptions, "ACTIVE, "); + else + PL_strcat(*aDeviceOptions, "UNACTIVE, "); + + //////////////////////////////////////// + // SW SETTABLE? + if (SANE_OPTION_IS_SETTABLE(optdesc->cap)) + PL_strcat(*aDeviceOptions, "SETTABLE, "); + else + PL_strcat(*aDeviceOptions, "UNSETTABLE, "); + + //////////////////////////////////////// + // Contraints Union + switch (optdesc->constraint_type) { + case SANE_CONSTRAINT_RANGE: + range_string = (char *)PR_MALLOC(128); + if (!range_string) + return NS_ERROR_OUT_OF_MEMORY; + sprintf(range_string, "RANGE, %i, %i, %i; ", + optdesc->constraint.range->min, + optdesc->constraint.range->max, + optdesc->constraint.range->quant); + PL_strcat(*aDeviceOptions, range_string); + PR_FREEIF(range_string); + break; + + case SANE_CONSTRAINT_WORD_LIST: + word_string = (char *) PR_MALLOC(64); + if (!word_string) + return NS_ERROR_OUT_OF_MEMORY; + + PL_strcat(*aDeviceOptions, "WORD_LIST"); + for (int word_index=1; + word_index <= optdesc->constraint.word_list[0]; + word_index++) { + sprintf(word_string, ", %i", optdesc->constraint.word_list[i]); + PL_strcat(*aDeviceOptions, word_string); + } + PL_strcat(*aDeviceOptions, ";"); + PR_FREEIF(word_string); + break; + + case SANE_CONSTRAINT_STRING_LIST: + PL_strcat(*aDeviceOptions, "STRING_LIST"); + for (int string_index=0; + optdesc->constraint.string_list[string_index]; + string_index++) { + PL_strcat(*aDeviceOptions, ", "); + PL_strcat(*aDeviceOptions, + optdesc->constraint.string_list[string_index]); + } + PL_strcat(*aDeviceOptions, ";"); + break; + + default: + // no constraints + PL_strcat(*aDeviceOptions, "NONE;"); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetDeviceOptions(const char * aDeviceOptions) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetDeviceOptions()\n"); +#endif + + NS_ERROR("DeviceOptions is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetActiveDevice(char ** aActiveDevice) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetActiveDevice()\n"); +#endif + + NS_ASSERTION(aActiveDevice != NULL, "null device pointer"); + if (!aActiveDevice) + return NS_ERROR_NULL_POINTER; + + *aActiveDevice = nsnull; + if (!mSaneDevice) + return NS_OK; + + *aActiveDevice = (char*) nsAllocator::Alloc(PL_strlen(mSaneDevice) + 1); + PL_strcpy(*aActiveDevice, mSaneDevice); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetActiveDevice(const char * aActiveDevice) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetActiveDevice()\n"); +#endif + + NS_ASSERTION(aActiveDevice, "null active device pointer"); + if (!aActiveDevice) + return NS_ERROR_NULL_POINTER; + + PR_FREEIF (mSaneDevice); + mSaneDevice = PL_strdup(aActiveDevice); + + if (mSaneOpen) { + sane_close(mSaneHandle); + sane_exit(); + sane_init(0,0); + if (sane_open(mSaneDevice, &mSaneHandle) != SANE_STATUS_GOOD) { + mSaneOpen = SANE_FALSE; + NS_ERROR("Error trying to reopen sane device!\n"); + return NS_ERROR_FAILURE; + } + + } else { + if (sane_open(mSaneDevice, &mSaneHandle) != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to open closed sane device!\n"); + return NS_ERROR_FAILURE; + } + mSaneOpen = SANE_TRUE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::OpenSaneDeviceIF( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::OpenSaneDeviceIF()\n"); +#endif + + if (mSaneOpen) { + return NS_OK; + } + + if (!mSaneDevice) { + NS_ERROR("Device name not set!"); + return NS_ERROR_FAILURE; + } + + SANE_Status status = sane_open(mSaneDevice, &mSaneHandle); + if (status != SANE_STATUS_GOOD) { + NS_ERROR ("sane_open returned error code"); + return NS_ERROR_FAILURE; + } + + mSaneOpen = SANE_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetImageParameters(char ** aImageParameters) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetImageParameters()\n"); +#endif + + SANE_Status status; + SANE_Parameters params; + nsresult rv; + + NS_ASSERTION(aImageParameters != NULL, "null parameters pointer"); + if (!aImageParameters) + return NS_ERROR_NULL_POINTER; + + *aImageParameters = nsnull; + + rv = OpenSaneDeviceIF();; + if (rv != NS_OK) + return rv; + + status = sane_get_parameters(mSaneHandle, ¶ms); + if (status != SANE_STATUS_GOOD) { + sane_close(mSaneHandle); + NS_ERROR("Failed to get parameters for sane device!"); + return NS_ERROR_FAILURE; + } + +#define MAX_PARAM_LEN 256 + *aImageParameters = (char *) nsAllocator::Alloc(MAX_PARAM_LEN); + if (!*aImageParameters) + return NS_ERROR_OUT_OF_MEMORY; + + char * format_string; + switch (params.format) { + + case SANE_FRAME_GRAY: + format_string = PL_strdup("GRAY"); + break; + + case SANE_FRAME_RGB: + format_string = PL_strdup("RGB"); + break; + + case SANE_FRAME_RED: + format_string = PL_strdup("RED"); + break; + + case SANE_FRAME_GREEN: + format_string = PL_strdup("GREEN"); + break; + + case SANE_FRAME_BLUE: + format_string = PL_strdup("BLUE"); + break; + + default: + format_string = PL_strdup("UNKNOWN"); + } + + sprintf(*aImageParameters, "%s, %s, %i, %i, %i, %i", + format_string, + params.last_frame ? "TRUE" : "FALSE", + params.lines, + params.depth, + params.pixels_per_line, + params.bytes_per_line); + + + // cleanup + PR_FREEIF(format_string); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetImageParameters(const char * aImageParameters) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetImageParameters()\n"); +#endif + + NS_ERROR("ImageParameters is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetAvailableDevices(char ** aAvailableDevices) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetAvailableDevices()\n"); +#endif + + SANE_Status status; + const SANE_Device ** device_list; + int num, i; + + NS_ASSERTION(aAvailableDevices != NULL, "null parameters pointer"); + if (!aAvailableDevices) + return NS_ERROR_NULL_POINTER; + + *aAvailableDevices = nsnull; + + status = sane_get_devices(&device_list, SANE_TRUE); + if (status != SANE_STATUS_GOOD) + return NS_ERROR_FAILURE; + + // find out how many devices are available + for ( num=0; device_list[num]; num++); + + // allocate return string +#define MAX_DEVICE_SIZE 256 + *aAvailableDevices = (char*) nsAllocator::Alloc(num * MAX_DEVICE_SIZE); + if (!*aAvailableDevices) + return NS_ERROR_OUT_OF_MEMORY; + + for ( i=0; i < num * MAX_DEVICE_SIZE; i++) + (*aAvailableDevices)[i] = '\0'; + + for ( i=0; iname, ", "); + optioncat(aAvailableDevices, device_list[i]->vendor, ", "); + optioncat(aAvailableDevices, device_list[i]->model, ", "); + optioncat(aAvailableDevices, device_list[i]->type, "; "); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetAvailableDevices(const char * aAvailableDevices) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetAvailableDevices()\n"); +#endif + + NS_ERROR("AvailableDevices is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::ScanImage(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::ScanImage()\n"); +#endif + + mState = 1; + mSuccess = 0; + + mScanThread = PR_CreateThread( PR_USER_THREAD, scanimage_thread_routine, + (void*)this, PR_PRIORITY_HIGH, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + + if (mScanThread == NULL) { + NS_ERROR("Could not create thread!"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetOption(const char * name, const char * value) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetOption()\n"); +#endif + + nsresult rv; + SANE_Int optnum; + const SANE_Option_Descriptor * optdesc; + void * option_value; + SANE_Status status; + + NS_ASSERTION(name != NULL && value != NULL, "null pointer"); + if (!name || !value) + return NS_ERROR_NULL_POINTER; + + rv = OpenSaneDeviceIF(); + if (rv != NS_OK) + return rv; + + // find the option number + for ( optnum=1; + ((optdesc = sane_get_option_descriptor(mSaneHandle, optnum)) != NULL) + && (PL_strcasecmp(name, optdesc->name) != 0); + optnum++ ); + if (!optdesc) { + NS_ERROR("Option not found for sane device!"); + return NS_ERROR_INVALID_ARG; + } + + // determine the value to pass to device + if (optdesc->type == SANE_TYPE_BOOL) { + SANE_Bool sbool; + if (PL_strcasecmp(value, "TRUE") == 0) { + sbool = SANE_TRUE; + } else + sbool = SANE_FALSE; + + option_value = (void *) &sbool; + + } else if ( optdesc->type == SANE_TYPE_INT || + optdesc->type == SANE_TYPE_FIXED ) { + SANE_Int sint; + sscanf(value, "%i", &sint); + + option_value = (void *) &sint; + + } else if (optdesc->type == SANE_TYPE_STRING) { + option_value = (void *) value; + } else { + option_value = NULL; + } + + // set the option + status = sane_control_option( mSaneHandle, optnum, + SANE_ACTION_SET_VALUE, option_value, + NULL); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to set option!"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +int +nsSanePluginInstance::WritePNMHeader (int fd, SANE_Frame format, + int width, int height, int depth) +{ +#ifdef DEBUG + printf("nsSaneInstance::WritePNMHeader()\n"); +#endif + + char b[32]; + + switch (format) { + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + case SANE_FRAME_RGB: + sprintf (b, "P6\n%d %d\n255\n", width, height); + break; + + default: + if (depth == 1) + sprintf (b, "P4\n%d %d\n", width, height); + else + sprintf (b, "P5\n%d %d\n255\n", width, height); + break; + } + + return write(fd, b, PL_strlen(b)); +} + +// END of Generic SANE Interface +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Scanner Status Interface + +NS_IMETHODIMP +nsSanePluginInstance::GetSuccess(PRBool * aSuccess) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetSuccess()\n"); +#endif + + NS_PRECONDITION(aSuccess != nsnull, "null ptr"); + if (! aSuccess) { + NS_ERROR("Null pointer passed to GetSuccess!\n"); + return NS_ERROR_NULL_POINTER; + } + + *aSuccess = mSuccess; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetSuccess(PRBool aSuccess) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetSuccess()\n"); +#endif + + // This is a read-only flag. + NS_ERROR("Success is a read-only flag!\n"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetState(char ** aState) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetState()\n"); +#endif + + NS_PRECONDITION(aState != nsnull, "null ptr"); + if (! aState) { + NS_ERROR("Null pointer passed to GetSuccess!\n"); + return NS_ERROR_NULL_POINTER; + } + + *aState = (char*) nsAllocator::Alloc(5); + if (!*aState) { + NS_ERROR("Unable to allocate State string!\n"); + return NS_ERROR_OUT_OF_MEMORY; + } + + if (mState == 0) + PL_strcpy(*aState, "IDLE"); + else + PL_strcpy(*aState, "BUSY"); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetState(const char * aState) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetState()\n"); +#endif + + // This is a read-only flag. + NS_ERROR("Success is a read-only flag!\n"); + return NS_ERROR_FAILURE; +} + +// END of Scanner Status Interface +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// snPluginStreamListener Methods + +nsSanePluginStreamListener::nsSanePluginStreamListener(nsSanePluginInstance* inst) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::nsSanePluginStreamListener()\n"); +#endif + + PR_AtomicIncrement(&gPluginObjectCount); + NS_INIT_REFCNT(); + + mPlugInst = inst; +} + +nsSanePluginStreamListener::~nsSanePluginStreamListener(void) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::~nsSanePluginStreamListener()\n"); +#endif + + PR_AtomicDecrement(&gPluginObjectCount); +} + + +// This macro produces a simple version of QueryInterface, AddRef and Release. +// See the nsISupports.h header file for details. +NS_IMPL_ISUPPORTS( nsSanePluginStreamListener, kIPluginStreamListenerIID ); + +NS_METHOD +nsSanePluginStreamListener::OnStartBinding( nsIPluginStreamInfo* pluginInfo ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnStartBinding()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnDataAvailable( nsIPluginStreamInfo* pluginInfo, + nsIInputStream* input, + PRUint32 length ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnDataAvailable()\n"); +#endif + + // This plugin doesn't support + // streaming input data. + return NS_OK; +} + + +NS_METHOD +nsSanePluginStreamListener::OnFileAvailable( nsIPluginStreamInfo* pluginInfo, + const char* fileName ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnFileAvailable()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnStopBinding( nsIPluginStreamInfo* pluginInfo, + nsresult status ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnStopBinding()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnNotify( const char* url, nsresult status ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnNotify()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::GetStreamType(nsPluginStreamType *result) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::GetStreamType()\n"); +#endif + + *result = nsPluginStreamType_Normal; + return NS_OK; +} + +// End snPluginStreamListener methods +////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Helper Global Fuctions + +gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data) { + + nsSanePluginInstance * pthis = (nsSanePluginInstance *)data; + + pthis->PaintImage(); + return TRUE; +} + +nsresult optioncat ( char ** dest, const char * src, const char * ending ) +{ + char * p; + int i; + + NS_ASSERTION(dest != NULL, "null option string pointer"); + if (!dest) + return NS_ERROR_NULL_POINTER; + + NS_ASSERTION(src != NULL, "null option string pointer"); + if (!src) + return NS_ERROR_NULL_POINTER; + + NS_ASSERTION(ending != NULL, "null option string pointer"); + if (!ending) + return NS_ERROR_NULL_POINTER; + + // ensure that src string doesn't contain any ','s or ';'s + p = PL_strdup(src); + for ( i=0; p[i]; i++ ) { + if (p[i] == ',') + p[i] = '~'; // Replace ','s with '~'s + else if (p[i] == ';') + p[i] = '|'; // Replace ';'s with '|'s + + } + + + PL_strcat(*dest, p); + PL_strcat(*dest, ending); + PR_FREEIF(p); + + return NS_OK; +} + +void +my_jpeg_error_exit (j_common_ptr cinfo) +{ + emptr errmgr; + + errmgr = (emptr) cinfo->err; + cinfo->err->output_message(cinfo); + siglongjmp(errmgr->setjmp_buffer, 1); + return; +} + +unsigned char * +jpeg_file_to_rgb (FILE * f, int *w, int *h) +{ + struct jpeg_decompress_struct cinfo; + struct my_JPEG_error_mgr jerr; + unsigned char *data, *line[16], *ptr; + int x, y, i; + + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = my_jpeg_error_exit; + + /* error handler to longjmp to, we want to preserve signals */ + if (sigsetjmp(jerr.setjmp_buffer, 1)) { + + /* Whoops there was a jpeg error */ + jpeg_destroy_decompress(&cinfo); + return NULL; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, f); + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + + cinfo.scale_num = 2; + cinfo.scale_denom = 1; + jpeg_start_decompress(&cinfo); + *w = cinfo.output_width; + *h = cinfo.output_height; + data = (unsigned char *)PR_Malloc(*w ** h * 3); + if (!data) { + NS_ERROR("jpeg_file_to_rgb: Error trying to allocate buffer!\n"); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + ptr = data; + + if (cinfo.rec_outbuf_height > 16) { + NS_ERROR("ERROR: JPEG uses line buffers > 16. Cannot load.\n"); + return NULL; + } + + if (cinfo.output_components == 3) { + for (y = 0; y < *h; y += cinfo.rec_outbuf_height) { + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + line[i] = ptr; + ptr += *w * 3; + } + + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + } + } else if (cinfo.output_components == 1) { + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + if ((line[i] = (unsigned char *)PR_Malloc(*w)) == NULL) { + int t = 0; + + NS_ERROR("jpeg_file_to_rgb: Error tying to allocate line!\n"); + + for (t = 0; t < i; t++) + PR_FREEIF(line[t]); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + } + + for (y = 0; y < *h; y += cinfo.rec_outbuf_height) { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + for (x = 0; x < *w; x++) { + *ptr++ = line[i][x]; + *ptr++ = line[i][x]; + *ptr++ = line[i][x]; + } + } + } + + for (i = 0; i < cinfo.rec_outbuf_height; i++) + PR_FREEIF(line[i]); + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return data; +} + +// Caller must free memory +unsigned char * +scale_image(unsigned char *rgb_data, int rgb_width, int rgb_height, + int w, int h) +{ + int x, y, *xarray; + unsigned char **yarray, *ptr, *ptr2, *ptr22, *new_data; + int l, r, m, pos, inc, w3; + + new_data = (unsigned char *) PR_Malloc( w * h * 3); + if (!new_data) { + NS_ERROR("ERROR: Cannot allocate image buffer\n"); + return NULL; + } + + xarray = (int *)PR_Malloc(sizeof(int) * w); + if (!xarray) { + NS_ERROR("ERROR: Cannot allocate X co-ord buffer\n"); + PR_FREEIF(new_data); + return NULL; + } + + yarray = (unsigned char **)PR_Malloc(sizeof(unsigned char *) * h); + if (!yarray) { + NS_ERROR("ERROR: Cannot allocate Y co-ord buffer\n"); + PR_FREEIF(new_data); + PR_FREEIF(xarray); + return NULL; + } + + ptr22 = rgb_data; + w3 = rgb_width * 3; + inc = 0; + l = 0; + r = 0; + m = w - l - r; + + inc = (rgb_width << 16) / m; + pos = 0; + + for (x = l; x < l + m; x++) { + xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16); + pos += inc; + } + + pos = (rgb_width - r) << 16; + for (x = w - r; x < w; x++) { + xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16); + pos += 0x10000; + } + + l = 0; + r = 0; + m = h - l - r; + + if (m > 0) + inc = (rgb_height << 16) / m; + + pos = 0; + + for (x = l; x < l + m; x++) { + yarray[x] = ptr22 + ((pos >> 16) * w3); + pos += inc; + } + + pos = (rgb_height - r) << 16; + for (x = h - r; x < h; x++) { + yarray[x] = ptr22 + ((pos >> 16) * w3); + pos += 0x10000; + } + + ptr = new_data; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + ptr2 = yarray[y] + xarray[x]; + *ptr++ = (int)*ptr2++; + *ptr++ = (int)*ptr2++; + *ptr++ = (int)*ptr2; + } + } + + PR_FREEIF(xarray); + PR_FREEIF(yarray); + + return new_data; +} + + +// Caller must free memory +unsigned char * +crop_image(unsigned char *rgb_data, int *rgb_width, int *rgb_height, + gint x, gint y, gint w, gint h) +{ + unsigned char *data; + int xx, yy, w3, w4; + unsigned char *ptr1, *ptr2; + + if (!rgb_data) + return NULL; + + if (x < 0) + { + w += x; + x = 0; + } + if (y < 0) + { + h += y; + y = 0; + } + if (x >= *rgb_width) + return NULL; + if (y >= *rgb_height) + return NULL; + if (w <= 0) + return NULL; + if (h <= 0) + return NULL; + if (x + w > *rgb_width) + w = *rgb_width - x; + if (y + h > *rgb_height) + h = *rgb_height - y; + if (w <= 0) + return NULL; + if (h <= 0) + return NULL; + + w3 = *rgb_width * 3; + w4 = (*rgb_width - w) * 3; + data = (unsigned char *)PR_Malloc(w * h * 3); + if (data == NULL) + return NULL; + ptr1 = rgb_data + (y * w3) + (x * 3); + ptr2 = data; + for (yy = 0; yy < h; yy++) + { + for (xx = 0; xx < w; xx++) + { + *ptr2++ = *ptr1++; + *ptr2++ = *ptr1++; + *ptr2++ = *ptr1++; + } + ptr1 += w4; + } + + *rgb_width = w; + *rgb_height = h; + + return data; +} + +void PR_CALLBACK scanimage_thread_routine( void * arg ) +{ + nsSanePluginInstance * pthis = (nsSanePluginInstance *)arg; + + nsresult rv; + SANE_Status status; + SANE_Parameters param; + int len; + FILE * out; + SANE_Byte * buf; + JSAMPROW row_pointer[1]; + char *fname; + + // JPEG stuff + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + // Error handling + jerr.error_exit = my_jpeg_error_exit; // fatal errors + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_compress(&cinfo); + + rv = pthis->OpenSaneDeviceIF(); + if (rv != NS_OK) + goto error_exit; + + // open image file + fname = pthis->GetImageFilename(); + if (!fname) + goto error_exit; + out = fopen(fname, "wb"); + PR_FREEIF(fname); + if (out == NULL) { + NS_ERROR("Unable to open mImageFilename!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + jpeg_stdio_dest(&cinfo, out); + + status = sane_start(pthis->mSaneHandle); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to start sane device!"); + goto error_exit; + } + + status = sane_get_parameters(pthis->mSaneHandle, ¶m); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to get image parameters!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + cinfo.image_width = param.pixels_per_line; + cinfo.image_height = param.lines; + + switch (param.format) { + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + // NOT Supported! + NS_ERROR("Multiframe devices not supported!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + break; + + case SANE_FRAME_RGB: + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + break; + + case SANE_FRAME_GRAY: + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + break; + } + jpeg_set_defaults(&cinfo); + cinfo.dct_method = pthis->mCompMethod; + jpeg_set_quality(&cinfo, pthis->mCompQuality, FALSE); + jpeg_start_compress(&cinfo, TRUE); + + buf = (SANE_Byte *)PR_MALLOC(param.bytes_per_line); + if (!buf) { + NS_ERROR("Error trying to allocate buffer!\n"); + jpeg_destroy_compress(&cinfo); + fclose(out); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + + int total; + while(1) { + total = 0; + while (total < param.bytes_per_line) { + // Read in data from sane device + status = sane_read(pthis->mSaneHandle, buf + total, + param.bytes_per_line - total, + &len); + if (status != SANE_STATUS_GOOD) { + if (status == SANE_STATUS_EOF) + // done with this frame + goto finished_scan; + else { + NS_ERROR("Error trying to read from sane device!\n"); + PR_FREEIF(buf); + jpeg_destroy_compress(&cinfo); + fclose(out); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + } + total += len; + } + + row_pointer[0] = (JSAMPLE *) buf; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + + } // end while + + // cleanup + finished_scan: + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + PR_FREEIF(buf); + sane_cancel(pthis->mSaneHandle); + fclose(out); + pthis->SetSuccess(PR_TRUE); // allow caller to check for success + + error_exit: + + //////////////////////////////////////////////// + // Call onScanComplete callback in the UI thread + + nsCOMPtr eventQ; + + // Get the event queue of the current thread... + NS_WITH_SERVICE(nsIEventQueueService, eventQService, + kEventQueueService, &rv); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to get event queue service!\n"); + return; + } + + rv = eventQService->CreateThreadEventQueue(); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to create new thread event queue!\n"); + // Does this mean that an event queue is already at + // our disposal? + } + + rv = eventQService->GetThreadEventQueue(pthis->mUIThread, + getter_AddRefs(eventQ)); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to get thread queue!\n"); + return; + } + + PLEvent *event = new PLEvent; + if (!event) { + NS_ERROR("Unable to create new event!\n"); + return; + } + + // ThreadHandleEvent will now execute in the UI thread. + PL_InitEvent(event, + pthis, + (PLHandleEventProc) ThreadedHandleEvent, + (PLDestroyEventProc) ThreadedDestroyEvent); + + if (eventQ->PostEvent(event) != PR_SUCCESS) { + NS_ERROR("Error trying to post event!\n"); + return; + } + + return; +} + +void PR_CALLBACK ThreadedHandleEvent(PLEvent * event) +{ + nsSanePluginInstance *pthis = (nsSanePluginInstance *)event->owner; + + // If this isn't the UI's thread then we are hosed! + if (PR_FALSE == pthis->IsUIThread()) { + NS_ERROR("Attempt to execute event from the wrong thread!\n"); + return; + } + + pthis->SetState(0); // no longer scanning + pthis->Restore(); // repaint image + + // Notify user that routine is finished + pthis->DoScanCompleteCallback(); +} + +void PR_CALLBACK ThreadedDestroyEvent(PLEvent* aEvent) +{ + delete aEvent; +} + +void store_filename(GtkFileSelection *selector, gpointer user_data) +{ + nsSanePluginInstance *pthis = (nsSanePluginInstance *) user_data; + gchar *cmd; + GtkWidget * fs; + char * orig_filename; + char * dest_filename; + + if (!user_data || !selector) + return; + + fs = pthis->GetFileSelection(); + orig_filename = pthis->GetImageFilename(); + if ( !fs || !orig_filename ) + return; + + dest_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)); + if ( !dest_filename ) + return; + +#ifdef DEBUG + printf("Saving %s to %s\n", orig_filename, dest_filename); +#endif + + /* + * I'm just doing a simple 'cp' command with the shell. + * This probobly should be done in a NSPR safe kind + * of way. + */ + + // cp /tmp/nsSanePlugin.###.jpg /path/to/save/to.jpg + cmd = (gchar *)PR_Malloc(PL_strlen(orig_filename) + + PL_strlen(dest_filename) + 6); + if (!cmd) + return; + + sprintf(cmd, "cp %s %s", orig_filename, dest_filename); + system(cmd); + + PR_FREEIF(cmd); +} + +// END of Helper Global Functions +////////////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePlugin.h b/mozilla/modules/plugin/SanePlugin/nsSanePlugin.h new file mode 100644 index 00000000000..b4d2ed6d76f --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePlugin.h @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Declares the nsSanePluginInterface class for the SANE plugin. + */ + +#ifndef __NS_SANE_PLUGIN_H__ +#define __NS_SANE_PLUGIN_H__ + +#include "prthread.h" +#include "nscore.h" +#include "nsplugin.h" +#include "gdksuperwin.h" +#include "gtkmozbox.h" +#include "gtk/gtk.h" +#include "nsIPlugin.h" +#include "nsSanePluginControl.h" +#include "nsIScriptContext.h" +#include "nsIServiceManager.h" +#include "nsISupports.h" +#include "nsISupportsUtils.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" + +/////////////////////////////////// +// Needed for encoding jpeg images +extern "C" +{ +#include +} + +/////////////////////////////////// +// Needed for SANE interface +extern "C" +{ +#include +#include +#include +} + +typedef struct _PlatformInstance +{ + Window window; + GtkWidget * widget; + GdkSuperWin * superwin; + Display * display; + uint16 x; + uint16 y; + uint32 width; + uint32 height; + +} PlatformInstance; + +// Threaded routine for grabbing a frame from device. +// If a callback for onScanComplete was set in the embed/object +// tag, then this routine will call it before exiting. +//void PR_CALLBACK scanimage_thread_routine( void * arg); +void PR_CALLBACK scanimage_thread_routine( void * arg ); + +class nsSanePluginInstance : public nsIPluginInstance, + public nsISanePluginInstance +{ + friend void PR_CALLBACK scanimage_thread_routine( void *); + + public: + + nsSanePluginInstance(void); + virtual ~nsSanePluginInstance(void); + + ///////////////////////////////////////////////////////////////////// + // nsIPluginInstance inherited interface + NS_IMETHOD Initialize(nsIPluginInstancePeer* peer); + NS_IMETHOD GetPeer(nsIPluginInstancePeer* *result); + NS_IMETHOD Start(void); + NS_IMETHOD Stop(void); + NS_IMETHOD Destroy( void ); + NS_IMETHOD SetWindow( nsPluginWindow* window ); + NS_IMETHOD NewStream( nsIPluginStreamListener** listener ); + NS_IMETHOD Print( nsPluginPrint* platformPrint ); + NS_IMETHOD GetValue( nsPluginInstanceVariable variable, void *value ); + + // Not used for this platform! Only a placeholder. + NS_IMETHOD HandleEvent( nsPluginEvent* event, PRBool* handled ); + + // End of nsIPlugIninstance inherited interface. + ///////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////// + // nsSanePluginInstance specific methods: + NS_DECL_ISUPPORTS ; + + // Execute given callback in window's JavaScript + NS_IMETHOD DoScanCompleteCallback(); + NS_IMETHOD DoInitCompleteCallback(); + + void SetMode(nsPluginMode mode) { fMode = mode; } + void SetState(PRInt32 aState) { mState = aState; }; + NS_IMETHOD PaintImage(void); + char * GetImageFilename(); + GtkWidget * GetFileSelection(); + PRBool IsUIThread(); + nsresult OpenSaneDeviceIF( void ); + + //*** Methods exposed through the XPConnect interface *** + NS_DECL_NSISANEPLUGININSTANCE + +private: + + GtkWidget *mDrawing_area; + GtkWidget *mEvent_box; + PlatformInstance fPlatform; + char mImageFilename[255]; + GtkWidget *mFileSelection; + GdkRectangle mZoom_box; + unsigned char *mRGBData; + int mRGBWidth, mRGBHeight; + + // line attributes for zoom box + PRInt32 mLineWidth; + GdkLineStyle mLineStyle; + GdkCapStyle mCapStyle; + GdkJoinStyle mJoinStyle; + + // zoom box change variables + float mTopLeftXChange; + float mTopLeftYChange; + float mBottomRightXChange; + float mBottomRightYChange; + + // jpeg compression attributes + int mCompQuality; + enum J_DCT_METHOD mCompMethod; + + // sane specific members + SANE_Handle mSaneHandle; + SANE_String mSaneDevice; + SANE_Bool mSaneOpen; + PRBool mSuccess; + PRInt32 mState; + + // needed for JavaScript Callbacks + char *mOnScanCompleteScript; + char *mOnInitCompleteScript; + PRThread *mScanThread; + PRThread *mUIThread; + +protected: + + nsIPluginInstancePeer* fPeer; + nsPluginWindow* fWindow; + nsPluginMode fMode; + nsIPluginManager* mPluginManager; + +private: + + int WritePNMHeader (int fd, SANE_Frame format, + int width, int height, + int depth); + + void PlatformNew( void ); + nsresult PlatformDestroy( void ); + PRInt16 PlatformHandleEvent( nsPluginEvent* event ); + nsresult PlatformSetWindow( nsPluginWindow* window ); +}; + +class nsSanePluginStreamListener : public nsIPluginStreamListener +{ + public: + + NS_DECL_ISUPPORTS ; + + /* + * Notify the observer that the URL has started to load. This method is + * called only once, at the beginning of a URL load.

+ * + * @return The return value is currently ignored. In the future it may be + * used to cancel the URL load.. + */ + NS_IMETHOD OnStartBinding( nsIPluginStreamInfo* pluginInfo ); + + /** + * Notify the client that data is available in the input stream. This + * method is called whenver data is written into the input stream by the + * networking library...

+ * + * @param aIStream The input stream containing the data. This stream can + * be either a blocking or non-blocking stream. + * @param length The amount of data that was just pushed into the + * stream. + * @return The return value is currently ignored. + */ + NS_IMETHOD OnDataAvailable( nsIPluginStreamInfo* pluginInfo, + nsIInputStream* input, + PRUint32 length ); + NS_IMETHOD OnFileAvailable( nsIPluginStreamInfo* pluginInfo, + const char* fileName ); + + /** + * Notify the observer that the URL has finished loading. This method is + * called once when the networking library has finished processing the + * URL transaction initiatied via the nsINetService::Open(...) call. + *

+ * + * This method is called regardless of whether the URL loaded + * successfully.

+ * + * @param status Status code for the URL load. + * @param msg A text string describing the error. + * @return The return value is currently ignored. + */ + NS_IMETHOD OnStopBinding( nsIPluginStreamInfo* pluginInfo, + nsresult status ); + NS_IMETHOD OnNotify( const char* url, nsresult status ); + NS_IMETHOD GetStreamType( nsPluginStreamType *result ); + + /////////////////////////////////////////////////////////////////////////// + // snPluginStreamListener specific methods: + + nsSanePluginStreamListener( nsSanePluginInstance* inst ); + virtual ~nsSanePluginStreamListener( void ); + + nsSanePluginInstance* mPlugInst; +}; + +#endif // __NS_SANE_PLUGIN_H__ diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePluginControl.idl b/mozilla/modules/plugin/SanePlugin/nsSanePluginControl.idl new file mode 100644 index 00000000000..58eafab50f5 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePluginControl.idl @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Defines scriptable interface to SANE plugin. + */ + +#include "nsISupports.idl" +#include "domstubs.idl" + +[scriptable, uuid(10982800-365e-11d3-a2bf-0004ac779ef3)] +/** + * This interface can be obtained with the following HTML/JavaScript sequence: + * + *
+ * 
+ *
+ * 
+ * + * + * + *
+ * + *
+ * + * This fragment will create an embedded plugin, which can then be accessed + * and controlled by the nsISanePluginInstance interface which is instantiated + * in the script. + */ +interface nsISanePluginInstance : nsISupports +{ + ///////////////////////////////////////////////////////////////////// + // Plugin Status Interface + + // Read-Only: Contains completion status of last operation + attribute boolean Success; + + // Read-Only: Contains the current state of the scanner (IDLE|BUSY) + attribute string State; + + ///////////////////////////////////////////////////////////////////// + // Image Preview Interface + + // Move zoom box to a given geometry in one step + void ZoomImage(in unsigned short x, in unsigned short y, + in unsigned short width, in unsigned short height); + void ZoomImageWithAttributes(in unsigned short x, + in unsigned short y, + in unsigned short width, + in unsigned short height, + in long req_line_width, + in string req_line_style, + in string req_cap_style, + in string req_join_style); + + // Undo all croping and reset zoom box for entire image + void Restore (); + + // Zoom in on the image and reset the zoom box to contain + // the entire image. + void Crop(in unsigned short x, in unsigned short y, + in unsigned short width, in unsigned short height); + + // Read/Write zoom box geometry + // (Each write will trigger a refresh!) + attribute unsigned short ZoomX; + attribute unsigned short ZoomY; + attribute unsigned short ZoomWidth; + attribute unsigned short ZoomHeight; + + // Read/Write zoom box line attributes + // (Each write will trigger a refresh!) + attribute long ZoomLineWidth; + attribute string ZoomLineStyle; + attribute string ZoomCapStyle; + attribute string ZoomJoinStyle; + + // Read-Only zoom box change indicators + // (For SANE devices that support changing the scan area, + // this allows for the controling JavaScript to safely adjust + // the scan area in a device specific manner.) + attribute float ZoomBR_XChange; // % bottom right x change on last zoom + attribute float ZoomBR_YChange; // % bottom right y ... + attribute float ZoomTL_XChange; // % top left x ... + attribute float ZoomTL_YChange; // % top left y ... + + // Read/Write JPEG compression attributes + attribute long Quality; + attribute string Method; + + ////////////////////////////////////////////////////////////////////// + // Generic SANE Interface + + // READ_ONLY: returns a colon delimeted list of option descriptions + // where each option description is a comma delimited + // list of values + attribute string DeviceOptions; + + // Returns or sets the active device. + attribute string ActiveDevice; + + // READ_ONLY: returns a comma delimited list of image parameters + attribute string ImageParameters; + + // READ_ONLY: returns a comma delimited list of available devices + attribute string AvailableDevices; + + // Pull image from device with current option settings. + // As a side effect, the zoom box is set to contain the + // entire image. + void ScanImage(); + void SetOption(in string name, in string value); + + // Pop up a dialog to save current image to a file + void SaveImage(); + }; + +%{ C++ +//10982800-365e-11d3-a2bf-0004ac780ef3 +#define NS_SANE_PLUGIN_CONTROL_CID \ + { 0x10982800, 0x365e, 0x11d3, { 0xa2, 0xbf, 0x0, 0x04, 0xac, 0x78, 0x0e, 0xf3 }} + +%} diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.cpp b/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.cpp new file mode 100644 index 00000000000..977aaa6bec1 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.cpp @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Implements the SANE plugin factory class. + */ + +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsSanePlugin_CID.h" +#include "nsSanePlugin.h" +#include "nsSanePluginFactory.h" +#include "plstr.h" + +#define PLUGIN_NAME "SANE Plugin" +#define PLUGIN_DESCRIPTION "SANE Plugin is a generic scanner interface" +#define PLUGIN_MIME_DESCRIPTION "application/X-sane-plugin::Scanner/Camera" +#define PLUGIN_MIME_TYPE "application/X-sane-plugin" + +static NS_DEFINE_IID( kISupportsIID, NS_ISUPPORTS_IID ); +static NS_DEFINE_IID( kIFactoryIID, NS_IFACTORY_IID ); +static NS_DEFINE_IID( kIPluginIID, NS_IPLUGIN_IID ); +static NS_DEFINE_CID( kComponentManagerCID, NS_COMPONENTMANAGER_CID ); +static NS_DEFINE_CID( knsSanePluginControlCID, NS_SANE_PLUGIN_CONTROL_CID ); +static NS_DEFINE_CID( knsSanePluginInst, NS_SANE_PLUGIN_CID ); + +//////////////////////////////////////////////////////////////////////// + +nsSanePluginFactoryImpl::nsSanePluginFactoryImpl( const nsCID &aClass, + const char* className, + const char* progID ) + : mClassID(aClass), mClassName(className), mProgID(progID) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::nsSanePluginFactoryImpl()\n"); +#endif + + NS_INIT_REFCNT(); + +} + +nsSanePluginFactoryImpl::~nsSanePluginFactoryImpl() +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::~nsSanePluginFactoryImpl()\n"); +#endif + + printf("mRefCnt = %i\n", mRefCnt); + NS_ASSERTION(mRefCnt == 0, "non-zero refcnt at destruction"); +} + +NS_IMETHODIMP +nsSanePluginFactoryImpl::QueryInterface(const nsIID &aIID, + void **aInstancePtr) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::QueryInterface()\n"); +#endif + + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*,this); + } else if (aIID.Equals(kIFactoryIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*, + NS_STATIC_CAST(nsIFactory*,this)); + } else if (aIID.Equals(kIPluginIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*, + NS_STATIC_CAST(nsIPlugin*,this)); + } else { + *aInstancePtr = nsnull; + return NS_ERROR_NO_INTERFACE; + } + + NS_ADDREF(NS_REINTERPRET_CAST(nsISupports*,*aInstancePtr)); + + return NS_OK; +} + +// Standard implementation of AddRef and Release +NS_IMPL_ADDREF( nsSanePluginFactoryImpl ); +NS_IMPL_RELEASE( nsSanePluginFactoryImpl ); + +NS_IMETHODIMP +nsSanePluginFactoryImpl::CreateInstance( nsISupports *aOuter, + const nsIID &aIID, + void **aResult) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::CreateInstance()\n"); +#endif + + if ( !aResult ) + return NS_ERROR_NULL_POINTER; + + if ( aOuter ) + return NS_ERROR_NO_AGGREGATION; + + nsSanePluginInstance * inst = new nsSanePluginInstance(); + if (!inst) + return NS_ERROR_OUT_OF_MEMORY; + + inst->AddRef(); + *aResult = inst; + return NS_OK; +} + +nsresult +nsSanePluginFactoryImpl::LockFactory(PRBool aLock) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::LockFactory()\n"); +#endif + + // Needs to be implemented + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::Initialize() +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::Initialize()\n"); +#endif + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::Shutdown( void ) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::Shutdown()\n"); +#endif + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::GetMIMEDescription(const char* *result) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::GetMIMEDescription()\n"); +#endif + + // caller is responsible for releasing + *result = PL_strdup( PLUGIN_MIME_DESCRIPTION ); + + return NS_OK; +} + +NS_METHOD +nsSanePluginFactoryImpl::GetValue( nsPluginVariable variable, void *value ) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::GetValue()\n"); +#endif + + nsresult err = NS_OK; + + if ( variable == nsPluginVariable_NameString ) { + + *( ( char ** )value ) = strdup( PLUGIN_NAME ); + + } else if ( variable == nsPluginVariable_DescriptionString ) { + + *( ( char ** )value ) = strdup( PLUGIN_DESCRIPTION ); + + } else { + + err = NS_ERROR_FAILURE; + + } + + return err; +} + + +NS_IMETHODIMP +nsSanePluginFactoryImpl::CreatePluginInstance( nsISupports *aOuter, + REFNSIID aIID, + const char* aPluginMIMEType, + void **aResult) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::CreatePluginInstance()\n"); +#endif + + // Need to find out what this is used for. The npsimple + // plugin looks like it just does a CreateInstance and + // ignores the mime type. + return NS_ERROR_NOT_IMPLEMENTED; +} + + +//////////////////////////////////////////////////////////////////////// + +/** + * The XPCOM runtime will call this to get a new factory object for the + * CID/progID it passes in. XPCOM is responsible for caching the resulting + * factory. + * + * return the proper factory to the caller + */ +extern "C" PR_IMPLEMENT(nsresult) +NSGetFactory( nsISupports* aServMgr, + const nsCID &aClass, + const char *aClassName, + const char *aProgID, + nsIFactory **aFactory) +{ + if (! aFactory) + return NS_ERROR_NULL_POINTER; + + nsSanePluginFactoryImpl* factory = new nsSanePluginFactoryImpl(aClass, + aClassName, + aProgID); + if ( factory == nsnull ) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(factory); + *aFactory = factory; + return NS_OK; + +} + +char *buf; + +extern "C" PR_IMPLEMENT( nsresult ) +NSRegisterSelf( nsISupports* aServMgr, const char* aPath ) +{ + nsresult rv; + + nsCOMPtr servMgr( do_QueryInterface( aServMgr, &rv ) ); + if ( NS_FAILED( rv ) ) + return rv; + + NS_WITH_SERVICE( nsIComponentManager, compMgr, kComponentManagerCID, &rv ); + if ( NS_FAILED( rv ) ) + return rv; + + // Register the plugin control portion. + rv = compMgr->RegisterComponent(knsSanePluginControlCID, + "SANE Plugin Control", + "component://netscape/plugins/sane-control", + aPath, PR_TRUE, PR_TRUE ); + + // Register the plugin portion. + nsString progID; + progID.AssignWithConversion( NS_INLINE_PLUGIN_PROGID_PREFIX ); + + progID.AppendWithConversion(PLUGIN_MIME_TYPE); + buf = ( char * )calloc( 2000, sizeof( char ) ); + progID.ToCString( buf, 1999 ); + + rv = compMgr->RegisterComponent( knsSanePluginInst, + "SANE Plugin Component", + buf, + aPath, PR_TRUE, PR_TRUE); + free( buf ); + + if ( NS_FAILED( rv ) ) + return rv; + + return NS_OK; + +} + +extern "C" PR_IMPLEMENT( nsresult ) +NSUnregisterSelf(nsISupports* aServMgr, const char* aPath) +{ + nsresult rv; + + nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); + if (NS_FAILED(rv)) return rv; + + NS_WITH_SERVICE(nsIComponentManager, compMgr, kComponentManagerCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = compMgr->UnregisterComponent(knsSanePluginControlCID, aPath); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.h b/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.h new file mode 100644 index 00000000000..9c1b2f9b6e8 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePluginFactory.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Contributor(s): + * Rusty Lynch + */ +/* + * Declares the SANE plugin factory class. + */ + +#ifndef __NS_SANE_PLUGIN_FACTORY_H__ +#define __NS_SANE_PLUGIN_FACTORY_H__ + +class nsSanePluginFactoryImpl : public nsIPlugin +{ +public: + nsSanePluginFactoryImpl(const nsCID &aClass, const char* className, + const char* progID); + + // nsISupports methods + NS_DECL_ISUPPORTS ; + + // nsIFactory methods + NS_IMETHOD CreateInstance(nsISupports *aOuter, + const nsIID &aIID, + void **aResult); + + NS_IMETHOD LockFactory(PRBool aLock); + NS_IMETHOD Initialize(void); + NS_IMETHOD Shutdown(void); + NS_IMETHOD GetMIMEDescription(const char* *result); + NS_IMETHOD GetValue(nsPluginVariable variable, void *value); + NS_IMETHOD CreatePluginInstance(nsISupports *aOuter, REFNSIID aIID, + const char* aPluginMIMEType, + void **aResult); + +protected: + virtual ~nsSanePluginFactoryImpl(); + + nsCID mClassID; + const char* mClassName; + const char* mProgID; + +}; + +#endif // __NS_SANE_PLUGIN_FACTORY_H__ + + + + + diff --git a/mozilla/modules/plugin/SanePlugin/nsSanePlugin_CID.h b/mozilla/modules/plugin/SanePlugin/nsSanePlugin_CID.h new file mode 100644 index 00000000000..1efbf6c526f --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/nsSanePlugin_CID.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Defines the CID for the SANE plugin. + */ + +#ifndef __NS_SANE_PLUGIN_CID_H__ +#define __NS_SANE_PLUGIN_CID_H__ + +#define NS_SANE_PLUGIN_CID { 0x302da841, 0x3dbf, 0x11d3, { 0xbc, 0xfb, 0x0, 0xa0, 0xc9, 0xc8, 0xd9, 0x1d } } + +#endif // __NS_SANE_PLUGIN_CID_H__ diff --git a/mozilla/modules/plugin/SanePlugin/test/camera.html b/mozilla/modules/plugin/SanePlugin/test/camera.html new file mode 100644 index 00000000000..c42774a81d0 --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/test/camera.html @@ -0,0 +1,167 @@ + + + CPia Digital Camera Test Application + + + +

+ This is a test page for grabing frames from a USB Cpia camera + using the v4l (Video4Linux) SANE module. +

+ + + + + + +
+
+ +
+
+
+ + + + + + + + +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/SanePlugin/test/scanner.html b/mozilla/modules/plugin/SanePlugin/test/scanner.html new file mode 100644 index 00000000000..146025eaf9b --- /dev/null +++ b/mozilla/modules/plugin/SanePlugin/test/scanner.html @@ -0,0 +1,301 @@ + + + Image Scanner Application + + +

+ This is a simple test page for the HP 6300C USB scanner. +

+ + + + + +
+
+ +
+
+
+ + +
+ +
+

+ + + +

+ + + +
+

+ + + + +
+
+ + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/Makefile.in b/mozilla/modules/plugin/samples/SanePlugin/Makefile.in new file mode 100644 index 00000000000..8c9678de5ac --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/Makefile.in @@ -0,0 +1,57 @@ +############################################################################ +## Makefile.in (Generic SANE Plugin Tree) +## +## The contents of this file are subject to the Mozilla Public +## License Version 1.1 (the "License"); you may not use this file +## except in compliance with the License. You may obtain a copy of +## the License at http://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS +## IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +## implied. See the License for the specific language governing +## rights and limitations under the License. +## Contributor(s): +## +## Rusty Lynch +############################################################################ + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = SanePlugin +LIBRARY_NAME = Sane +IS_COMPONENT = 1 + +REQUIRES = raptor xpcom + +CPPSRCS = \ + nsSanePluginFactory.cpp \ + nsSanePlugin.cpp \ + $(NULL) + +LOCAL_INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../../public \ + -I/usr/lib/glib/include + +EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lgtksuperwin \ + $(TK_LIBS) -lsane -ljpeg + +XPIDLSRCS = nsSanePluginControl.idl + +include $(topsrcdir)/config/rules.mk + +install:: $(TARGETS) + $(INSTALL) $(srcdir)/test/camera.html $(DIST)/bin/res/samples + $(INSTALL) $(srcdir)/test/scanner.html $(DIST)/bin/res/samples + $(INSTALL) $(srcdir)/test/camera.html /home/httpd/html/test + $(INSTALL) $(srcdir)/test/scanner.html /home/httpd/html/test + +superclean: clean + rm -f *~ test/*~ + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/README b/mozilla/modules/plugin/samples/SanePlugin/README new file mode 100644 index 00000000000..68e649eb637 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/README @@ -0,0 +1,45 @@ +What is this plug-in? +--------------------- + +This is a Linux/Mozilla plug-in that enables a scanner/digital +camera application to be created with JavaScript. To this end +the SANE plug-in provides two things: + + 1. I have attempted to export all of the SANE API's + from the SANE native libraries to JavaScript. + (I have provided two brain-dead samples for a + CPia USB digital camera and HP 6300 scanner.) + + 2. A drawing surface for displaying the scanned image. + +===================================================================== + +Why did you bother? +------------------ + +Originally, I used this plug-in to cut my teeth on the new Mozilla +XPCom plug-in model. Now I am hoping to provide a Linux/Mozilla +plug-in sample that exercises most of the functionality that the +Linux community would want to use (like connecting JavaScript +to native code.) + +===================================================================== + +You know, Linux isn't the only UN*X around! +------------------------------------------- + +I say Linux because that is the only platform I have built/tested on. +SANE is supported by a long list of UN*X OS's so there is no reason +that other platforms shouldn't work with this plug-in. + +(I have tried to stick to NSPR functions for this reason, but it's easy +to over look a couple of Linux specific functions.) + +====================================================================== + +How do I build this beast? +------------------------- + +In order to build this plug-in, you need the SANE (Scanner Access +Now Easy) libraries/headers installed on your system. For more +info on SANE, go to http://www.mostang.com/sane. diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp new file mode 100644 index 00000000000..7ad44b58db4 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp @@ -0,0 +1,2643 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Implements the nsSanePluginInterface for the SANE plugin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nsIIOService.h" +#include "nsSanePlugin.h" +#include "plstr.h" +#include "prmem.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" + +extern "C" +{ +#include "jpeglib.h" +#include "sane/sane.h" +#include "sane/sanei.h" +#include "sane/saneopts.h" +} + +#define MAX_OPT_SIZE (1024 * 2) + +struct my_JPEG_error_mgr +{ + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; +}; +typedef struct my_JPEG_error_mgr *emptr; + +static void PR_CALLBACK ThreadedDestroyEvent(PLEvent* aEvent); +static void PR_CALLBACK ThreadedHandleEvent(PLEvent* aEvent); + +//static void PR_CALLBACK scanimage_thread_routine( void * arg ); + +static void store_filename(GtkFileSelection *selector, gpointer user_data); +static nsresult optioncat( char ** dest, const char * src, + const char * ending ); +static void my_jpeg_error_exit (j_common_ptr cinfo); +static unsigned char* jpeg_file_to_rgb (FILE * f, int *w, int *h); +static gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data); +static unsigned char * crop_image(unsigned char *rgb_data, int *rgb_width, + int *rgb_height, gint x, gint y, + gint w, gint h); +static unsigned char * scale_image(unsigned char *rgb_data, int rgb_width, + int rgb_height, int w, int h); + +static PRInt32 gPluginObjectCount = 0; + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID ); +static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID ); +static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID ); +static NS_DEFINE_IID(knsSanePluginControlIID, NS_ISANEPLUGININSTANCE_IID ); +static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID ); +static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID ); +static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID ); +static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID ); + + +/////////////////////////////////////////////////////////////////////////////// +// Plugin Instance API Methods + +nsSanePluginInstance::nsSanePluginInstance( void ) + : fPeer( NULL ), fWindow( NULL ), fMode( nsPluginMode_Embedded ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::nsSanePluginInstance()\n"); +#endif + + NS_INIT_REFCNT(); + PR_AtomicIncrement(&gPluginObjectCount); + + // set default jpeg compression attributes + mCompQuality = 10; + mCompMethod = JDCT_FASTEST; + + // Initialize sane interface + mSaneDevice = nsnull; + mSaneOpen = SANE_FALSE; + mRGBData = nsnull; + mBottomRightXChange = mBottomRightYChange = + mTopLeftXChange = mTopLeftYChange = 0; + + // initialize JavaScript callback members + mOnScanCompleteScript = nsnull; + mOnInitCompleteScript = nsnull; + mScanThread = nsnull; + mUIThread = PR_CurrentThread(); + + mState = 0; // idle + mSuccess = 1; // success + mFileSelection = nsnull; + + // initialize gtk widgets + fPlatform.widget = nsnull; + mEvent_box = nsnull; + mDrawing_area = nsnull; + + if(nsComponentManager::CreateInstance(kCPluginManagerCID, + NULL, kIPluginManagerIID, + (void**)&mPluginManager) != NS_OK) { + NS_ERROR("Error trying to create plugin manager!"); + return; + } +} + +nsSanePluginInstance::~nsSanePluginInstance(void) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::~nsSanePluginInstance(%i)\n", + gPluginObjectCount ); +#endif + + PR_AtomicDecrement(&gPluginObjectCount); + PlatformDestroy(); // Perform platform specific cleanup + + if (mPluginManager) { + mPluginManager->Release(); + mPluginManager = nsnull; + } + + sane_exit(); + + // cleanup old widgets + if (fPlatform.widget) + gtk_widget_destroy(fPlatform.widget); + if (mEvent_box) + gtk_widget_destroy(mEvent_box); + if (mDrawing_area) + gtk_widget_destroy(mDrawing_area); + + NS_IF_RELEASE(fPeer); + PR_FREEIF(mSaneDevice); + PR_FREEIF(mOnScanCompleteScript); + PR_FREEIF(mOnInitCompleteScript); + +} + +// This macro produces simple versions of QueryInterface, AddRef and Release +// See the nsISupports.h header file for details. +NS_IMPL_ISUPPORTS2(nsSanePluginInstance, + nsIPluginInstance, + nsISanePluginInstance) + +NS_METHOD +nsSanePluginInstance::Initialize( nsIPluginInstancePeer* peer ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Initialize()\n"); +#endif + + NS_ASSERTION(peer != NULL, "null peer"); + + nsIPluginTagInfo * taginfo; + const char * const * names = nsnull; + const char * const * values = nsnull; + PRUint16 count = 0; + nsresult result; + + gdk_rgb_init(); + + fPeer = peer; + peer->AddRef(); + result = peer->GetMode( &fMode ); + + if ( NS_FAILED( result ) ) + return result; + + result = peer->QueryInterface( nsIPluginTagInfo::GetIID(), + ( void ** )&taginfo ); + + if ( !NS_FAILED( result ) ) + { + taginfo->GetAttributes( count, names, values ); + NS_IF_RELEASE( taginfo ); + } + + // Initialize default line attribute info + mLineWidth = 5; + mLineStyle = GDK_LINE_ON_OFF_DASH; + mCapStyle = GDK_CAP_BUTT; + mJoinStyle = GDK_JOIN_MITER; + + // Get user requested attribute info + for (int i=0; i < count; i++) { + + if (PL_strcasecmp(names[i], "line_width") == 0) { + ////////////// + // Line width + sscanf(values[i], "%i", &mLineWidth); + + } else if (PL_strcasecmp(names[i], "line_style") == 0) { + ////////////// + // Line style + if (PL_strcasecmp(values[i], "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(values[i], "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(values[i], "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + } else if (PL_strcasecmp(names[i], "cap_style") == 0) { + ///////////// + // Cap style + if (PL_strcasecmp(values[i], "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(values[i], "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(values[i], "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(values[i], "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + } else if (PL_strcasecmp(names[i], "join_style") == 0) { + ////////////// + // Join style + if (PL_strcasecmp(values[i], "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(values[i], "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(values[i], "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + } else if (PL_strcasecmp(names[i], "device") == 0) { + ////////// + // Device + PR_FREEIF(mSaneDevice); + mSaneDevice = PL_strdup(values[i]); + } else if (PL_strcasecmp(names[i], "onScanComplete") == 0) { + ////////////////////////// + // onScanComplete Callback + PR_FREEIF(mOnScanCompleteScript); + mOnScanCompleteScript = PL_strdup(values[i]); + } else if(PL_strcasecmp(names[i], "onInitComplete") == 0) { + ////////////////////////// + // onInitComplete Callback + PR_FREEIF(mOnInitCompleteScript); + mOnInitCompleteScript = PL_strdup(values[i]); + } + } + + // the mImageFilename is used to store data directly + // from the sane device and also rendering to the screen + sprintf( mImageFilename, "/tmp/nsSanePlugin.%p.jpg", (void *)this ); + + PlatformNew(); /* Call Platform-specific initializations */ + return NS_OK; + +} + +NS_METHOD +nsSanePluginInstance::SaveImage() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::SaveImage()\n"); +#endif + + if (!mImageFilename) { + NS_ERROR("Attempt to save image before initial scan!"); + return NS_ERROR_FAILURE; + } + + mFileSelection = gtk_file_selection_new("Choose file to save image to."); + + // Set a default filename + gtk_file_selection_set_filename(GTK_FILE_SELECTION(mFileSelection), + "saved.jpg"); + + // Callback for getting the filename + gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->ok_button), + "clicked", GTK_SIGNAL_FUNC (store_filename), + (gpointer) this); + + // Ensure the file selection dialog goes away after + // 'OK' or 'CANCEL' button is selected + gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->ok_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT(mFileSelection)); + gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(mFileSelection)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT(mFileSelection)); + + // ok, now show the file selection dialog + gtk_widget_show(mFileSelection); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::DoScanCompleteCallback() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::DoScanCompleteCallback()\n"); +#endif + + char *url; + + if (mOnScanCompleteScript && mPluginManager) { + url = (char *)PR_Malloc(PL_strlen(mOnScanCompleteScript) + 13); + if (!url) + return NS_ERROR_OUT_OF_MEMORY; + + sprintf(url, "javascript:%s", mOnScanCompleteScript); + mPluginManager->GetURL((nsIPluginInstance *)this, url, "_self"); + + PR_FREEIF(url); + } + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::DoInitCompleteCallback() +{ +#ifdef DEBUG + printf("nsSanePluginInstance::DoInitCompleteCallback()\n"); +#endif + + char *url; + + if (mOnInitCompleteScript && mPluginManager) { + url = (char *)PR_Malloc(PL_strlen(mOnInitCompleteScript) + 13); + if (!url) + return NS_ERROR_OUT_OF_MEMORY; + + sprintf(url, "javascript:%s", mOnInitCompleteScript); + mPluginManager->GetURL((nsIPluginInstance *)this, url, "_self"); + + PR_FREEIF(url); + } + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::GetPeer( nsIPluginInstancePeer* *result ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::GetPeer()\n"); +#endif + + fPeer->AddRef(); + *result = fPeer; + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Start( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Start()\n"); +#endif + + PR_FREEIF(mRGBData); + mRGBData = nsnull; + + sane_init(0,0); + mSaneOpen = SANE_FALSE; + nsresult rv = OpenSaneDeviceIF(); + if (rv != NS_OK) { + NS_ERROR("Failed to open device in Start()\n"); + return rv; + } + + PlatformNew(); + + // call onInitComplete callback + DoInitCompleteCallback(); + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Stop( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Stop()\n"); +#endif + + // Wait for scanner operation to complete. + // TODO: Warn user that they are still scanning + // and send abort command to scanner if + // the user wishes. + if (mScanThread) + PR_JoinThread(mScanThread); + + PR_FREEIF(mRGBData); + + // close sane + if (mSaneOpen) { + sane_close(mSaneHandle); + sane_exit(); + mSaneOpen = SANE_FALSE; + } + + if (remove ( mImageFilename ) == -1) + perror(mImageFilename); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Destroy( void ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Destroy()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::SetWindow(nsPluginWindow* window) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::SetWindow()\n"); +#endif + + nsresult result; + + // The real work is handled in PlatformSetWindow + // (as if this plug-in was multiplatform) + result = PlatformSetWindow( window ); + fWindow = window; + + return result; +} + +NS_METHOD +nsSanePluginInstance::NewStream( nsIPluginStreamListener** listener ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::NewStream()\n"); +#endif + + if ( listener != NULL ) + *listener = new nsSanePluginStreamListener( this ); + + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::Print( nsPluginPrint* printInfo ) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::Print()\n"); +#endif + + if ( printInfo == NULL ) + return NS_ERROR_FAILURE; + + // needs to be implemented + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD +nsSanePluginInstance::HandleEvent(nsPluginEvent* event, PRBool* handled) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::HandleEvent()\n"); +#endif + + // Not used on unix platform. Only here for potential + // future porting efforts + *handled = (PRBool)PlatformHandleEvent(event); + return NS_OK; +} + +NS_METHOD +nsSanePluginInstance::GetValue(nsPluginInstanceVariable variable, void *value) +{ +#ifdef DEBUG + printf("nsSanePluginInstance::GetValue()\n"); +#endif + + //Needs to be implemented + return NS_ERROR_NOT_IMPLEMENTED; +} + +void +nsSanePluginInstance::PlatformNew( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformNew\n"); +#endif + + fPlatform.window = 0; +} + +nsresult +nsSanePluginInstance::PlatformDestroy( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformDestroy()\n"); +#endif + + return NS_OK; +} + +nsresult +nsSanePluginInstance::PlatformSetWindow( nsPluginWindow* window ) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformSetWindow()\n"); +#endif + + + if ( window == NULL || window->window == NULL ) + return NS_ERROR_NULL_POINTER; + + if ( fPlatform.superwin == (GdkSuperWin *)window->window ) + return NS_OK; + + // Record windowing info + fPlatform.superwin = (GdkSuperWin *)window->window; + fPlatform.height = window->height; + fPlatform.width = window->width; + + // cleanup old widgets + if (fPlatform.widget) + gtk_widget_destroy(fPlatform.widget); + if (mEvent_box) + gtk_widget_destroy(mEvent_box); + if (mDrawing_area) + gtk_widget_destroy(mDrawing_area); + + fPlatform.widget = gtk_mozbox_new(fPlatform.superwin->bin_window); + + // Initialize the zoom box to outline the entire image + mZoom_box.x = 0; + mZoom_box.y = 0; + mZoom_box.width = fPlatform.width; + mZoom_box.height = fPlatform.height; + + // Setup an event box to contain the drawable + mEvent_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(fPlatform.widget), mEvent_box); + gtk_widget_show(mEvent_box); + + // Setup the drawable + mDrawing_area = ( GtkWidget * )gtk_drawing_area_new(); + gtk_drawing_area_size( GTK_DRAWING_AREA( mDrawing_area ), + window->width, window->height); + gtk_container_add(GTK_CONTAINER(mEvent_box), mDrawing_area); + gtk_widget_show(mDrawing_area); + + // Initialize line attributes for GC + GdkGC * da_gc = mDrawing_area->style->fg_gc[mDrawing_area->state]; + gdk_gc_set_line_attributes ( da_gc, mLineWidth, mLineStyle, + mCapStyle, mJoinStyle); + + // Register signals + gtk_signal_connect (GTK_OBJECT(mDrawing_area), "expose_event", + GTK_SIGNAL_FUNC(draw), this); + + // Now make everything visable + gtk_widget_show( fPlatform.widget ); + + return NS_OK; +} + +int16 +nsSanePluginInstance::PlatformHandleEvent(nsPluginEvent* event) +{ +#ifdef DEBUG + printf("nsSaneInstance::PlatformHandleEvent()\n"); +#endif + + /* UNIX Plugins do not use HandleEvent */ + return 0; +} + + +NS_IMETHODIMP +nsSanePluginInstance::PaintImage(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::PaintImage(%p)\n", this); +#endif + + if (mDrawing_area && fPlatform.widget) { + if (mImageFilename) { + + if (!mRGBData) { + + if (mState) + // we are currently scanning, so the jpeg file + // is not valid yet + return NS_OK; + + FILE * f = fopen(mImageFilename, "rb"); + if (!f) + // it's possible that an image hasn't been scanned yet + return NS_OK; + + mRGBData = jpeg_file_to_rgb(f, &mRGBWidth, &mRGBHeight); + if (!mRGBData) { + NS_ERROR("Error converting jpeg to rgb data!"); + return NS_ERROR_FAILURE; + } + + fclose(f); + + // scale down to window size + unsigned char * tmp = scale_image( mRGBData, + mRGBWidth, mRGBHeight, + fPlatform.width, + fPlatform.height); + if (!tmp) { + NS_ERROR("Error trying to scale image!"); + return NS_ERROR_FAILURE; + } + + // move to new data + PR_FREEIF(mRGBData); + mRGBData = tmp; + } + + // render the image + gdk_draw_rgb_image (mDrawing_area->window, + mDrawing_area->style->fg_gc[GTK_STATE_NORMAL], + 0, 0, fPlatform.width, fPlatform.height, + GDK_RGB_DITHER_MAX, mRGBData, + fPlatform.width * 3); + + gdk_draw_rectangle (mDrawing_area->window, + mDrawing_area->style->fg_gc[mDrawing_area->state], + FALSE, mZoom_box.x, mZoom_box.y, + mZoom_box.width, mZoom_box.height); + + gdk_flush(); + } + } + + return NS_OK; +} + +char * +nsSanePluginInstance::GetImageFilename() +{ + if (!mImageFilename) + return (char *)NULL; + + // Caller is responsible for freeing string + return PL_strdup(mImageFilename); +} + +GtkWidget * +nsSanePluginInstance::GetFileSelection() +{ + if (!mFileSelection) + return (GtkWidget *)NULL; + + return mFileSelection; +} + +PRBool +nsSanePluginInstance::IsUIThread() +{ + if (!mUIThread) + return PR_FALSE; + + return (mUIThread == PR_CurrentThread()); +} + +// End of Plugin Instance API Methods. +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// Image Preview Interface. + +NS_IMETHODIMP +nsSanePluginInstance::ZoomImage( PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::ZoomImage()\n"); +#endif + + int im_width, im_height, c_width, c_height, c_x, c_y; + + if (!mRGBData) + return NS_ERROR_FAILURE; + + // get the actual image width and height + im_width = mRGBWidth; + im_height = mRGBHeight; + + if ( x >= (PRUint16)fPlatform.width || + y >= (PRUint16)fPlatform.height ) + return NS_ERROR_FAILURE; + + if ( width + mZoom_box.x >= (PRUint16)fPlatform.width ) + mZoom_box.width = fPlatform.width - mZoom_box.x; + + if ( height + mZoom_box.y >= (PRUint16) fPlatform.height ) + mZoom_box.height = fPlatform.height - mZoom_box.y; + + mZoom_box.x = x; + mZoom_box.y = y; + + // translate the zoom box geometry to the image geometry + c_width = (int)(((double)im_width/(double)fPlatform.width) * + (double)mZoom_box.width); + c_height =(int)(((double)im_height/(double)fPlatform.height) * + (double)mZoom_box.height); + c_x = (int)(((double)im_width/(double)fPlatform.width) * + (double)mZoom_box.x); + c_y = (int)(((double)im_height/(double)fPlatform.height) * + (double)mZoom_box.y); + + unsigned char * tmp = crop_image(mRGBData, &mRGBWidth, &mRGBHeight, + c_x, c_y, c_width, c_height); + if (!tmp) + return NS_ERROR_FAILURE; + PR_FREEIF(mRGBData); + mRGBData = tmp; + + mRGBWidth = c_width; + mRGBHeight = c_height; + + mTopLeftXChange = (float)x/(float)fPlatform.width; + mTopLeftYChange = (float)y/(float)fPlatform.height; + mBottomRightXChange = (float)(fPlatform.width - (PRInt32)(x + mZoom_box.width))/(float)fPlatform.width; + mBottomRightYChange = (float)(fPlatform.height - (PRInt32)(y + mZoom_box.height))/(float)fPlatform.height; + + mZoom_box.x = 0; + mZoom_box.y = 0; + mZoom_box.width = (PRUint16)fPlatform.width; + mZoom_box.height = (PRUint16)fPlatform.height; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::ZoomImageWithAttributes( PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height, + PRInt32 req_line_width, + const char * req_line_style, + const char * req_cap_style, + const char * req_join_style ) +{ +#ifdef DEBUG + printf("nsSaneInstance::ZoomImageWithAttributes()\n"); +#endif + + if (!mRGBData) + return NS_ERROR_FAILURE; + + ZoomImage(x, y, width, height); + + /* + * Set zoom box line attributes + */ + + // Line Width + mLineWidth = req_line_width; + + // Line Style + if (PL_strcasecmp(req_line_style, "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(req_line_style, "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(req_line_style, "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + // Cap Style + if (PL_strcasecmp(req_cap_style, "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(req_cap_style, "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(req_cap_style, "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(req_cap_style, "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + // Join Style + if (PL_strcasecmp(req_join_style, "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(req_join_style, "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(req_join_style, "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + // Refresh + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::Crop(PRUint16 x, PRUint16 y, + PRUint16 width, PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::Crop()\n"); +#endif + + if ( (x <= (PRUint16)fPlatform.width - width ) && + (y <= (PRUint16)fPlatform.height - height) ) { + + mZoom_box.x = x; mZoom_box.y = y; + mZoom_box.width = width; + mZoom_box.height = height; + + } else { + return NS_ERROR_FAILURE; + } + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::Restore(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::Restore()\n"); +#endif + + PR_FREEIF(mRGBData); + mRGBData = nsnull; + + // Reset zoom box + mZoom_box.x = mZoom_box.y = 0; + mZoom_box.width = (PRUint16) fPlatform.width; + mZoom_box.height = (PRUint16) fPlatform.height; + + PaintImage(); + + return NS_OK; +} + + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomX(PRUint16 * x) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomX()\n"); +#endif + + NS_ASSERTION(x != NULL, "null x pointer"); + if (!x) + return NS_ERROR_NULL_POINTER; + + *x = mZoom_box.x; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomX(PRUint16 x) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomX()\n"); +#endif + + if (x > (PRInt32) fPlatform.width) + mZoom_box.x = (PRInt32) fPlatform.width; + else + mZoom_box.x = x; + + return PaintImage(); +} + + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomY(PRUint16 * y) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomY()\n"); +#endif + + NS_ASSERTION(y != NULL, "null y pointer"); + if (!y) + return NS_ERROR_NULL_POINTER; + + *y = mZoom_box.y; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomY(PRUint16 y) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomY()\n"); +#endif + + if (y > (PRInt32) fPlatform.height) + mZoom_box.y = (PRInt32)fPlatform.height; + else + mZoom_box.y = y; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomWidth(PRUint16 * width) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomWidth()\n"); +#endif + + NS_ASSERTION(width != NULL, "null width pointer"); + if (!width) + return NS_ERROR_NULL_POINTER; + + *width = mZoom_box.width; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomWidth(PRUint16 width) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomWidth()\n"); +#endif + + if (width + mZoom_box.x > (PRUint16)fPlatform.width) + mZoom_box.width = (PRInt32) fPlatform.width - mZoom_box.x; + else + mZoom_box.width = width; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomHeight(PRUint16 * height) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomHeight()\n"); +#endif + + NS_ASSERTION(height != NULL, "null height pointer"); + if (!height) + return NS_ERROR_NULL_POINTER; + + *height = mZoom_box.height; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomHeight(PRUint16 height) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomHeight()\n"); +#endif + + if (height + mZoom_box.y > (PRUint16) fPlatform.height) + mZoom_box.height = fPlatform.height - mZoom_box.y; + else + mZoom_box.height = height; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomLineWidth(PRInt32 * aZoomLineWidth) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomLineWidth()\n"); +#endif + + NS_ASSERTION(aZoomLineWidth != NULL, "null width pointer"); + if (!aZoomLineWidth) + return NS_ERROR_NULL_POINTER; + + *aZoomLineWidth = mLineWidth; + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomLineWidth(PRInt32 aZoomLineWidth) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomLineWidth()\n"); +#endif + + if (aZoomLineWidth <= 0) + return NS_ERROR_INVALID_ARG; + + mLineWidth = aZoomLineWidth; + + return PaintImage(); +} + +#define MAX_LINE_ATTRIBUTE_LENGTH 20 + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomLineStyle(char ** aZoomLineStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomLineStyle()\n"); +#endif + + NS_ASSERTION(*aZoomLineStyle != NULL, "null width pointer"); + if (!*aZoomLineStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomLineStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomLineStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mLineStyle == GDK_LINE_ON_OFF_DASH) + PL_strcpy(*aZoomLineStyle, "dash"); + else if (mLineStyle == GDK_LINE_SOLID) + PL_strcpy(*aZoomLineStyle, "solid"); + else if (mLineStyle == GDK_LINE_DOUBLE_DASH) + PL_strcpy(*aZoomLineStyle, "double_dash"); + else + PL_strcpy(*aZoomLineStyle, "unknown"); + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomLineStyle(const char * aZoomLineStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomLineStyle()\n"); +#endif + + NS_ASSERTION(aZoomLineStyle != NULL, "null width pointer"); + if (!aZoomLineStyle) + return NS_ERROR_NULL_POINTER; + + // Line Style + if (PL_strcasecmp(aZoomLineStyle, "dash") == 0) + mLineStyle = GDK_LINE_ON_OFF_DASH; + else if (PL_strcasecmp(aZoomLineStyle, "solid") == 0) + mLineStyle = GDK_LINE_SOLID; + else if (PL_strcasecmp(aZoomLineStyle, "double_dash") == 0) + mLineStyle = GDK_LINE_DOUBLE_DASH; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomCapStyle(char ** aZoomCapStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomCapStyle()\n"); +#endif + + NS_ASSERTION(*aZoomCapStyle != NULL, "null width pointer"); + if (!*aZoomCapStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomCapStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomCapStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mCapStyle == GDK_CAP_ROUND) + PL_strcpy(*aZoomCapStyle, "round"); + else if (mCapStyle == GDK_CAP_PROJECTING) + PL_strcpy(*aZoomCapStyle, "projecting"); + else if (mCapStyle == GDK_CAP_BUTT) + PL_strcpy(*aZoomCapStyle, "butt"); + else if (mCapStyle == GDK_CAP_NOT_LAST) + PL_strcpy(*aZoomCapStyle, "not_last"); + else + PL_strcpy(*aZoomCapStyle, "unknown"); + + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomCapStyle(const char * aZoomCapStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomCapStyle()\n"); +#endif + + NS_ASSERTION(aZoomCapStyle != NULL, "null width pointer"); + if (!aZoomCapStyle) + return NS_ERROR_NULL_POINTER; + + // Cap Style + if (PL_strcasecmp(aZoomCapStyle, "round") == 0) + mCapStyle = GDK_CAP_ROUND; + else if (PL_strcasecmp(aZoomCapStyle, "projecting") == 0) + mCapStyle = GDK_CAP_PROJECTING; + else if (PL_strcasecmp(aZoomCapStyle, "butt") == 0) + mCapStyle = GDK_CAP_BUTT; + else if (PL_strcasecmp(aZoomCapStyle, "not_last") == 0) + mCapStyle = GDK_CAP_NOT_LAST; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomJoinStyle(char ** aZoomJoinStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomJoinStyle()\n"); +#endif + + NS_ASSERTION(*aZoomJoinStyle != NULL, "null width pointer"); + if (!*aZoomJoinStyle) + return NS_ERROR_NULL_POINTER; + + *aZoomJoinStyle = (char*) nsAllocator::Alloc(MAX_LINE_ATTRIBUTE_LENGTH); + if (! *aZoomJoinStyle) + return NS_ERROR_OUT_OF_MEMORY; + + if (mJoinStyle == GDK_JOIN_MITER) + PL_strcpy(*aZoomJoinStyle, "miter"); + else if (mJoinStyle == GDK_JOIN_ROUND) + PL_strcpy(*aZoomJoinStyle, "round"); + else if (mJoinStyle == GDK_JOIN_BEVEL) + PL_strcpy(*aZoomJoinStyle, "bevel"); + else + PL_strcpy(*aZoomJoinStyle, "unknown"); + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomJoinStyle(const char * aZoomJoinStyle) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomJoinStyle()\n"); +#endif + + NS_ASSERTION(aZoomJoinStyle != NULL, "null width pointer"); + if (!aZoomJoinStyle) + return NS_ERROR_NULL_POINTER; + + // Join Style + if (PL_strcasecmp(aZoomJoinStyle, "miter") == 0) + mJoinStyle = GDK_JOIN_MITER; + else if (PL_strcasecmp(aZoomJoinStyle, "round") == 0) + mJoinStyle = GDK_JOIN_ROUND; + else if (PL_strcasecmp(aZoomJoinStyle, "bevel") == 0) + mJoinStyle = GDK_JOIN_BEVEL; + + return PaintImage(); +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomBR_XChange(float * aZoomBR_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomBR_XChange()\n"); +#endif + + *aZoomBR_XChange = mBottomRightXChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomBR_XChange(float aZoomBR_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomBR_XChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomBR_YChange(float * aZoomBR_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomBR_YChange()\n"); +#endif + + *aZoomBR_YChange = mBottomRightYChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomBR_YChange(float aZoomBR_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomBR_YChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomTL_XChange(float * aZoomTL_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomTL_XChange()\n"); +#endif + + *aZoomTL_XChange = mTopLeftXChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomTL_XChange(float aZoomTL_XChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomTL_XChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetZoomTL_YChange(float * aZoomTL_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetZoomTL_YChange()\n"); +#endif + + *aZoomTL_YChange = mTopLeftYChange; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetZoomTL_YChange(float aZoomTL_YChange) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetZoomTL_YChange()\n"); +#endif + + // Read-Only + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetQuality(PRInt32 * aQuality) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetQuality()\n"); +#endif + + NS_ASSERTION(aQuality != NULL, "null compression quality pointer"); + if (!aQuality) + return NS_ERROR_NULL_POINTER; + + *aQuality = mCompQuality; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetQuality(PRInt32 aQuality) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetQuality()\n"); +#endif + + NS_ASSERTION(aQuality > -1 && aQuality < 101, + "Invalid compression quality"); + if (aQuality < 0 || aQuality > 100) + return NS_ERROR_INVALID_ARG; + + mCompQuality = aQuality; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetMethod(char ** aMethod) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetMethod()\n"); +#endif + + NS_ASSERTION(aMethod != NULL, "Null method pointer"); + if (!aMethod) + return NS_ERROR_NULL_POINTER; + + *aMethod = (char*) nsAllocator::Alloc(8); + if (!*aMethod) + return NS_ERROR_OUT_OF_MEMORY; + + switch (mCompMethod) { + case JDCT_ISLOW: + PL_strcpy(*aMethod, "SLOW"); + break; + case JDCT_IFAST: + PL_strcpy(*aMethod, "FAST"); + break; + case JDCT_FLOAT: + PL_strcpy(*aMethod, "FLOAT"); + break; + default: + PL_strcpy(*aMethod, "DEFAULT"); + + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetMethod(const char * aMethod) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetMethod()\n"); +#endif + + NS_ASSERTION(aMethod != NULL, "Null method pointer"); + if (!aMethod) + return NS_ERROR_NULL_POINTER; + + if (PL_strcasecmp(aMethod, "SLOW") == 0) + mCompMethod = JDCT_ISLOW; + else if (PL_strcasecmp(aMethod, "FAST") == 0) + mCompMethod = JDCT_IFAST; + else if (PL_strcasecmp(aMethod, "FLOAT") == 0) + mCompMethod = JDCT_FLOAT; + else + mCompMethod = JDCT_DEFAULT; + + return NS_OK; +} + +// END of Image Preview Interface +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// Generic SANE Interface + +NS_IMETHODIMP +nsSanePluginInstance::GetDeviceOptions(char ** aDeviceOptions) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetDeviceOptions()\n"); +#endif + + const SANE_Option_Descriptor * optdesc; + SANE_Int string_size; + char *range_string, *word_string; + nsresult rv; + + NS_ASSERTION(aDeviceOptions != NULL, "null options pointer"); + if (!aDeviceOptions) + return NS_ERROR_NULL_POINTER; + + *aDeviceOptions = nsnull; + + rv = OpenSaneDeviceIF(); + if (rv != NS_OK) + return rv; + + // Find out how many options exist for device + optdesc = sane_get_option_descriptor(mSaneHandle, 0); + if (!optdesc || optdesc->size == 0) { + NS_ERROR("Error trying to get number of options for device!"); + return NS_ERROR_FAILURE; + } + + if (optdesc->size == 1) { /* size includes "size" option */ + NS_ERROR("Hmm.. That's odd! No options for this device?"); + return NS_OK; // I guess it's possible? + } + + // allocate string to contain data to return to caller + string_size = ((optdesc->size - 1) * MAX_OPT_SIZE) + 1; + *aDeviceOptions = (char*) nsAllocator::Alloc(string_size); + + // initialize options string + for (int n=0; ntype == SANE_TYPE_GROUP) + continue; + + /* + * WARNING! Need to replace ','s and ';'s with + * something else so that JavaScript parsing + * doesn't get hosed! + */ + + // NAME + if (optdesc->name) + optioncat(aDeviceOptions, optdesc->name, ", "); + else + strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // TITLE + if (optdesc->title) + optioncat(aDeviceOptions, optdesc->title, ", "); + else + PL_strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // DESCRIPTION + if (optdesc->desc) + optioncat(aDeviceOptions, optdesc->desc, ", "); + else + PL_strcat(*aDeviceOptions, "N/A, "); + + //////////////////////////////////// + // Option Type + switch (optdesc->type) { + + case SANE_TYPE_BOOL: + PL_strcat(*aDeviceOptions, "BOOL, "); + break; + + case SANE_TYPE_INT: + PL_strcat(*aDeviceOptions, "INT, "); + break; + + case SANE_TYPE_FIXED: + PL_strcat(*aDeviceOptions, "FIXED, "); + break; + + case SANE_TYPE_STRING: + PL_strcat(*aDeviceOptions, "STRING, "); + break; + + case SANE_TYPE_BUTTON: + PL_strcat(*aDeviceOptions, "BUTTON, "); + break; + + case SANE_TYPE_GROUP: + PL_strcat(*aDeviceOptions, "GROUP, "); + break; + + default: + PL_strcat(*aDeviceOptions, "UNKNOWN, "); + } + + //////////////////////////////////////// + // UNIT + switch (optdesc->unit) { + case SANE_UNIT_NONE: + PL_strcat(*aDeviceOptions, "NONE, "); + break; + + case SANE_UNIT_PIXEL: + PL_strcat(*aDeviceOptions, "PIXEL, "); + break; + + case SANE_UNIT_BIT: + PL_strcat(*aDeviceOptions, "BIT, "); + break; + + case SANE_UNIT_MM: + PL_strcat(*aDeviceOptions, "MM, "); + break; + + case SANE_UNIT_DPI: + PL_strcat(*aDeviceOptions, "PERCENT, "); + break; + + case SANE_UNIT_MICROSECOND: + PL_strcat(*aDeviceOptions, "MICROSECOND, "); + break; + + default: + PL_strcat(*aDeviceOptions, "UNKNOWN, "); + } + + //////////////////////////////////////// + // SIZE + char * size_option = (char *)PR_MALLOC(64); + if (!size_option) + return NS_ERROR_OUT_OF_MEMORY; + sprintf(size_option, "%i, ", optdesc->size); + PL_strcat(*aDeviceOptions, size_option); + PR_FREEIF(size_option); + + //////////////////////////////////////// + // ACTIVE? + if (SANE_OPTION_IS_ACTIVE(optdesc->cap)) + PL_strcat(*aDeviceOptions, "ACTIVE, "); + else + PL_strcat(*aDeviceOptions, "UNACTIVE, "); + + //////////////////////////////////////// + // SW SETTABLE? + if (SANE_OPTION_IS_SETTABLE(optdesc->cap)) + PL_strcat(*aDeviceOptions, "SETTABLE, "); + else + PL_strcat(*aDeviceOptions, "UNSETTABLE, "); + + //////////////////////////////////////// + // Contraints Union + switch (optdesc->constraint_type) { + case SANE_CONSTRAINT_RANGE: + range_string = (char *)PR_MALLOC(128); + if (!range_string) + return NS_ERROR_OUT_OF_MEMORY; + sprintf(range_string, "RANGE, %i, %i, %i; ", + optdesc->constraint.range->min, + optdesc->constraint.range->max, + optdesc->constraint.range->quant); + PL_strcat(*aDeviceOptions, range_string); + PR_FREEIF(range_string); + break; + + case SANE_CONSTRAINT_WORD_LIST: + word_string = (char *) PR_MALLOC(64); + if (!word_string) + return NS_ERROR_OUT_OF_MEMORY; + + PL_strcat(*aDeviceOptions, "WORD_LIST"); + for (int word_index=1; + word_index <= optdesc->constraint.word_list[0]; + word_index++) { + sprintf(word_string, ", %i", optdesc->constraint.word_list[i]); + PL_strcat(*aDeviceOptions, word_string); + } + PL_strcat(*aDeviceOptions, ";"); + PR_FREEIF(word_string); + break; + + case SANE_CONSTRAINT_STRING_LIST: + PL_strcat(*aDeviceOptions, "STRING_LIST"); + for (int string_index=0; + optdesc->constraint.string_list[string_index]; + string_index++) { + PL_strcat(*aDeviceOptions, ", "); + PL_strcat(*aDeviceOptions, + optdesc->constraint.string_list[string_index]); + } + PL_strcat(*aDeviceOptions, ";"); + break; + + default: + // no constraints + PL_strcat(*aDeviceOptions, "NONE;"); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetDeviceOptions(const char * aDeviceOptions) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetDeviceOptions()\n"); +#endif + + NS_ERROR("DeviceOptions is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetActiveDevice(char ** aActiveDevice) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetActiveDevice()\n"); +#endif + + NS_ASSERTION(aActiveDevice != NULL, "null device pointer"); + if (!aActiveDevice) + return NS_ERROR_NULL_POINTER; + + *aActiveDevice = nsnull; + if (!mSaneDevice) + return NS_OK; + + *aActiveDevice = (char*) nsAllocator::Alloc(PL_strlen(mSaneDevice) + 1); + PL_strcpy(*aActiveDevice, mSaneDevice); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetActiveDevice(const char * aActiveDevice) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetActiveDevice()\n"); +#endif + + NS_ASSERTION(aActiveDevice, "null active device pointer"); + if (!aActiveDevice) + return NS_ERROR_NULL_POINTER; + + PR_FREEIF (mSaneDevice); + mSaneDevice = PL_strdup(aActiveDevice); + + if (mSaneOpen) { + sane_close(mSaneHandle); + sane_exit(); + sane_init(0,0); + if (sane_open(mSaneDevice, &mSaneHandle) != SANE_STATUS_GOOD) { + mSaneOpen = SANE_FALSE; + NS_ERROR("Error trying to reopen sane device!\n"); + return NS_ERROR_FAILURE; + } + + } else { + if (sane_open(mSaneDevice, &mSaneHandle) != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to open closed sane device!\n"); + return NS_ERROR_FAILURE; + } + mSaneOpen = SANE_TRUE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::OpenSaneDeviceIF( void ) +{ +#ifdef DEBUG + printf("nsSaneInstance::OpenSaneDeviceIF()\n"); +#endif + + if (mSaneOpen) { + return NS_OK; + } + + if (!mSaneDevice) { + NS_ERROR("Device name not set!"); + return NS_ERROR_FAILURE; + } + + SANE_Status status = sane_open(mSaneDevice, &mSaneHandle); + if (status != SANE_STATUS_GOOD) { + NS_ERROR ("sane_open returned error code"); + return NS_ERROR_FAILURE; + } + + mSaneOpen = SANE_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetImageParameters(char ** aImageParameters) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetImageParameters()\n"); +#endif + + SANE_Status status; + SANE_Parameters params; + nsresult rv; + + NS_ASSERTION(aImageParameters != NULL, "null parameters pointer"); + if (!aImageParameters) + return NS_ERROR_NULL_POINTER; + + *aImageParameters = nsnull; + + rv = OpenSaneDeviceIF();; + if (rv != NS_OK) + return rv; + + status = sane_get_parameters(mSaneHandle, ¶ms); + if (status != SANE_STATUS_GOOD) { + sane_close(mSaneHandle); + NS_ERROR("Failed to get parameters for sane device!"); + return NS_ERROR_FAILURE; + } + +#define MAX_PARAM_LEN 256 + *aImageParameters = (char *) nsAllocator::Alloc(MAX_PARAM_LEN); + if (!*aImageParameters) + return NS_ERROR_OUT_OF_MEMORY; + + char * format_string; + switch (params.format) { + + case SANE_FRAME_GRAY: + format_string = PL_strdup("GRAY"); + break; + + case SANE_FRAME_RGB: + format_string = PL_strdup("RGB"); + break; + + case SANE_FRAME_RED: + format_string = PL_strdup("RED"); + break; + + case SANE_FRAME_GREEN: + format_string = PL_strdup("GREEN"); + break; + + case SANE_FRAME_BLUE: + format_string = PL_strdup("BLUE"); + break; + + default: + format_string = PL_strdup("UNKNOWN"); + } + + sprintf(*aImageParameters, "%s, %s, %i, %i, %i, %i", + format_string, + params.last_frame ? "TRUE" : "FALSE", + params.lines, + params.depth, + params.pixels_per_line, + params.bytes_per_line); + + + // cleanup + PR_FREEIF(format_string); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetImageParameters(const char * aImageParameters) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetImageParameters()\n"); +#endif + + NS_ERROR("ImageParameters is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetAvailableDevices(char ** aAvailableDevices) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetAvailableDevices()\n"); +#endif + + SANE_Status status; + const SANE_Device ** device_list; + int num, i; + + NS_ASSERTION(aAvailableDevices != NULL, "null parameters pointer"); + if (!aAvailableDevices) + return NS_ERROR_NULL_POINTER; + + *aAvailableDevices = nsnull; + + status = sane_get_devices(&device_list, SANE_TRUE); + if (status != SANE_STATUS_GOOD) + return NS_ERROR_FAILURE; + + // find out how many devices are available + for ( num=0; device_list[num]; num++); + + // allocate return string +#define MAX_DEVICE_SIZE 256 + *aAvailableDevices = (char*) nsAllocator::Alloc(num * MAX_DEVICE_SIZE); + if (!*aAvailableDevices) + return NS_ERROR_OUT_OF_MEMORY; + + for ( i=0; i < num * MAX_DEVICE_SIZE; i++) + (*aAvailableDevices)[i] = '\0'; + + for ( i=0; iname, ", "); + optioncat(aAvailableDevices, device_list[i]->vendor, ", "); + optioncat(aAvailableDevices, device_list[i]->model, ", "); + optioncat(aAvailableDevices, device_list[i]->type, "; "); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetAvailableDevices(const char * aAvailableDevices) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetAvailableDevices()\n"); +#endif + + NS_ERROR("AvailableDevices is read-only!\n"); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSanePluginInstance::ScanImage(void) +{ +#ifdef DEBUG + printf("nsSaneInstance::ScanImage()\n"); +#endif + + mState = 1; + mSuccess = 0; + + mScanThread = PR_CreateThread( PR_USER_THREAD, scanimage_thread_routine, + (void*)this, PR_PRIORITY_HIGH, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + + if (mScanThread == NULL) { + NS_ERROR("Could not create thread!"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetOption(const char * name, const char * value) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetOption()\n"); +#endif + + nsresult rv; + SANE_Int optnum; + const SANE_Option_Descriptor * optdesc; + void * option_value; + SANE_Status status; + + NS_ASSERTION(name != NULL && value != NULL, "null pointer"); + if (!name || !value) + return NS_ERROR_NULL_POINTER; + + rv = OpenSaneDeviceIF(); + if (rv != NS_OK) + return rv; + + // find the option number + for ( optnum=1; + ((optdesc = sane_get_option_descriptor(mSaneHandle, optnum)) != NULL) + && (PL_strcasecmp(name, optdesc->name) != 0); + optnum++ ); + if (!optdesc) { + NS_ERROR("Option not found for sane device!"); + return NS_ERROR_INVALID_ARG; + } + + // determine the value to pass to device + if (optdesc->type == SANE_TYPE_BOOL) { + SANE_Bool sbool; + if (PL_strcasecmp(value, "TRUE") == 0) { + sbool = SANE_TRUE; + } else + sbool = SANE_FALSE; + + option_value = (void *) &sbool; + + } else if ( optdesc->type == SANE_TYPE_INT || + optdesc->type == SANE_TYPE_FIXED ) { + SANE_Int sint; + sscanf(value, "%i", &sint); + + option_value = (void *) &sint; + + } else if (optdesc->type == SANE_TYPE_STRING) { + option_value = (void *) value; + } else { + option_value = NULL; + } + + // set the option + status = sane_control_option( mSaneHandle, optnum, + SANE_ACTION_SET_VALUE, option_value, + NULL); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to set option!"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +int +nsSanePluginInstance::WritePNMHeader (int fd, SANE_Frame format, + int width, int height, int depth) +{ +#ifdef DEBUG + printf("nsSaneInstance::WritePNMHeader()\n"); +#endif + + char b[32]; + + switch (format) { + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + case SANE_FRAME_RGB: + sprintf (b, "P6\n%d %d\n255\n", width, height); + break; + + default: + if (depth == 1) + sprintf (b, "P4\n%d %d\n", width, height); + else + sprintf (b, "P5\n%d %d\n255\n", width, height); + break; + } + + return write(fd, b, PL_strlen(b)); +} + +// END of Generic SANE Interface +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Scanner Status Interface + +NS_IMETHODIMP +nsSanePluginInstance::GetSuccess(PRBool * aSuccess) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetSuccess()\n"); +#endif + + NS_PRECONDITION(aSuccess != nsnull, "null ptr"); + if (! aSuccess) { + NS_ERROR("Null pointer passed to GetSuccess!\n"); + return NS_ERROR_NULL_POINTER; + } + + *aSuccess = mSuccess; + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetSuccess(PRBool aSuccess) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetSuccess()\n"); +#endif + + // This is a read-only flag. + NS_ERROR("Success is a read-only flag!\n"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsSanePluginInstance::GetState(char ** aState) +{ +#ifdef DEBUG + printf("nsSaneInstance::GetState()\n"); +#endif + + NS_PRECONDITION(aState != nsnull, "null ptr"); + if (! aState) { + NS_ERROR("Null pointer passed to GetSuccess!\n"); + return NS_ERROR_NULL_POINTER; + } + + *aState = (char*) nsAllocator::Alloc(5); + if (!*aState) { + NS_ERROR("Unable to allocate State string!\n"); + return NS_ERROR_OUT_OF_MEMORY; + } + + if (mState == 0) + PL_strcpy(*aState, "IDLE"); + else + PL_strcpy(*aState, "BUSY"); + + return NS_OK; +} + +NS_IMETHODIMP +nsSanePluginInstance::SetState(const char * aState) +{ +#ifdef DEBUG + printf("nsSaneInstance::SetState()\n"); +#endif + + // This is a read-only flag. + NS_ERROR("Success is a read-only flag!\n"); + return NS_ERROR_FAILURE; +} + +// END of Scanner Status Interface +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// snPluginStreamListener Methods + +nsSanePluginStreamListener::nsSanePluginStreamListener(nsSanePluginInstance* inst) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::nsSanePluginStreamListener()\n"); +#endif + + PR_AtomicIncrement(&gPluginObjectCount); + NS_INIT_REFCNT(); + + mPlugInst = inst; +} + +nsSanePluginStreamListener::~nsSanePluginStreamListener(void) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::~nsSanePluginStreamListener()\n"); +#endif + + PR_AtomicDecrement(&gPluginObjectCount); +} + + +// This macro produces a simple version of QueryInterface, AddRef and Release. +// See the nsISupports.h header file for details. +NS_IMPL_ISUPPORTS( nsSanePluginStreamListener, kIPluginStreamListenerIID ); + +NS_METHOD +nsSanePluginStreamListener::OnStartBinding( nsIPluginStreamInfo* pluginInfo ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnStartBinding()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnDataAvailable( nsIPluginStreamInfo* pluginInfo, + nsIInputStream* input, + PRUint32 length ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnDataAvailable()\n"); +#endif + + // This plugin doesn't support + // streaming input data. + return NS_OK; +} + + +NS_METHOD +nsSanePluginStreamListener::OnFileAvailable( nsIPluginStreamInfo* pluginInfo, + const char* fileName ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnFileAvailable()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnStopBinding( nsIPluginStreamInfo* pluginInfo, + nsresult status ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnStopBinding()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::OnNotify( const char* url, nsresult status ) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::OnNotify()\n"); +#endif + + return NS_OK; +} + +NS_METHOD +nsSanePluginStreamListener::GetStreamType(nsPluginStreamType *result) +{ +#ifdef DEBUG + printf("nsSanePluginStreamListener::GetStreamType()\n"); +#endif + + *result = nsPluginStreamType_Normal; + return NS_OK; +} + +// End snPluginStreamListener methods +////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Helper Global Fuctions + +gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data) { + + nsSanePluginInstance * pthis = (nsSanePluginInstance *)data; + + pthis->PaintImage(); + return TRUE; +} + +nsresult optioncat ( char ** dest, const char * src, const char * ending ) +{ + char * p; + int i; + + NS_ASSERTION(dest != NULL, "null option string pointer"); + if (!dest) + return NS_ERROR_NULL_POINTER; + + NS_ASSERTION(src != NULL, "null option string pointer"); + if (!src) + return NS_ERROR_NULL_POINTER; + + NS_ASSERTION(ending != NULL, "null option string pointer"); + if (!ending) + return NS_ERROR_NULL_POINTER; + + // ensure that src string doesn't contain any ','s or ';'s + p = PL_strdup(src); + for ( i=0; p[i]; i++ ) { + if (p[i] == ',') + p[i] = '~'; // Replace ','s with '~'s + else if (p[i] == ';') + p[i] = '|'; // Replace ';'s with '|'s + + } + + + PL_strcat(*dest, p); + PL_strcat(*dest, ending); + PR_FREEIF(p); + + return NS_OK; +} + +void +my_jpeg_error_exit (j_common_ptr cinfo) +{ + emptr errmgr; + + errmgr = (emptr) cinfo->err; + cinfo->err->output_message(cinfo); + siglongjmp(errmgr->setjmp_buffer, 1); + return; +} + +unsigned char * +jpeg_file_to_rgb (FILE * f, int *w, int *h) +{ + struct jpeg_decompress_struct cinfo; + struct my_JPEG_error_mgr jerr; + unsigned char *data, *line[16], *ptr; + int x, y, i; + + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = my_jpeg_error_exit; + + /* error handler to longjmp to, we want to preserve signals */ + if (sigsetjmp(jerr.setjmp_buffer, 1)) { + + /* Whoops there was a jpeg error */ + jpeg_destroy_decompress(&cinfo); + return NULL; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, f); + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + + cinfo.scale_num = 2; + cinfo.scale_denom = 1; + jpeg_start_decompress(&cinfo); + *w = cinfo.output_width; + *h = cinfo.output_height; + data = (unsigned char *)PR_Malloc(*w ** h * 3); + if (!data) { + NS_ERROR("jpeg_file_to_rgb: Error trying to allocate buffer!\n"); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + ptr = data; + + if (cinfo.rec_outbuf_height > 16) { + NS_ERROR("ERROR: JPEG uses line buffers > 16. Cannot load.\n"); + return NULL; + } + + if (cinfo.output_components == 3) { + for (y = 0; y < *h; y += cinfo.rec_outbuf_height) { + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + line[i] = ptr; + ptr += *w * 3; + } + + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + } + } else if (cinfo.output_components == 1) { + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + if ((line[i] = (unsigned char *)PR_Malloc(*w)) == NULL) { + int t = 0; + + NS_ERROR("jpeg_file_to_rgb: Error tying to allocate line!\n"); + + for (t = 0; t < i; t++) + PR_FREEIF(line[t]); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + } + + for (y = 0; y < *h; y += cinfo.rec_outbuf_height) { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + for (i = 0; i < cinfo.rec_outbuf_height; i++) { + for (x = 0; x < *w; x++) { + *ptr++ = line[i][x]; + *ptr++ = line[i][x]; + *ptr++ = line[i][x]; + } + } + } + + for (i = 0; i < cinfo.rec_outbuf_height; i++) + PR_FREEIF(line[i]); + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return data; +} + +// Caller must free memory +unsigned char * +scale_image(unsigned char *rgb_data, int rgb_width, int rgb_height, + int w, int h) +{ + int x, y, *xarray; + unsigned char **yarray, *ptr, *ptr2, *ptr22, *new_data; + int l, r, m, pos, inc, w3; + + new_data = (unsigned char *) PR_Malloc( w * h * 3); + if (!new_data) { + NS_ERROR("ERROR: Cannot allocate image buffer\n"); + return NULL; + } + + xarray = (int *)PR_Malloc(sizeof(int) * w); + if (!xarray) { + NS_ERROR("ERROR: Cannot allocate X co-ord buffer\n"); + PR_FREEIF(new_data); + return NULL; + } + + yarray = (unsigned char **)PR_Malloc(sizeof(unsigned char *) * h); + if (!yarray) { + NS_ERROR("ERROR: Cannot allocate Y co-ord buffer\n"); + PR_FREEIF(new_data); + PR_FREEIF(xarray); + return NULL; + } + + ptr22 = rgb_data; + w3 = rgb_width * 3; + inc = 0; + l = 0; + r = 0; + m = w - l - r; + + inc = (rgb_width << 16) / m; + pos = 0; + + for (x = l; x < l + m; x++) { + xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16); + pos += inc; + } + + pos = (rgb_width - r) << 16; + for (x = w - r; x < w; x++) { + xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16); + pos += 0x10000; + } + + l = 0; + r = 0; + m = h - l - r; + + if (m > 0) + inc = (rgb_height << 16) / m; + + pos = 0; + + for (x = l; x < l + m; x++) { + yarray[x] = ptr22 + ((pos >> 16) * w3); + pos += inc; + } + + pos = (rgb_height - r) << 16; + for (x = h - r; x < h; x++) { + yarray[x] = ptr22 + ((pos >> 16) * w3); + pos += 0x10000; + } + + ptr = new_data; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + ptr2 = yarray[y] + xarray[x]; + *ptr++ = (int)*ptr2++; + *ptr++ = (int)*ptr2++; + *ptr++ = (int)*ptr2; + } + } + + PR_FREEIF(xarray); + PR_FREEIF(yarray); + + return new_data; +} + + +// Caller must free memory +unsigned char * +crop_image(unsigned char *rgb_data, int *rgb_width, int *rgb_height, + gint x, gint y, gint w, gint h) +{ + unsigned char *data; + int xx, yy, w3, w4; + unsigned char *ptr1, *ptr2; + + if (!rgb_data) + return NULL; + + if (x < 0) + { + w += x; + x = 0; + } + if (y < 0) + { + h += y; + y = 0; + } + if (x >= *rgb_width) + return NULL; + if (y >= *rgb_height) + return NULL; + if (w <= 0) + return NULL; + if (h <= 0) + return NULL; + if (x + w > *rgb_width) + w = *rgb_width - x; + if (y + h > *rgb_height) + h = *rgb_height - y; + if (w <= 0) + return NULL; + if (h <= 0) + return NULL; + + w3 = *rgb_width * 3; + w4 = (*rgb_width - w) * 3; + data = (unsigned char *)PR_Malloc(w * h * 3); + if (data == NULL) + return NULL; + ptr1 = rgb_data + (y * w3) + (x * 3); + ptr2 = data; + for (yy = 0; yy < h; yy++) + { + for (xx = 0; xx < w; xx++) + { + *ptr2++ = *ptr1++; + *ptr2++ = *ptr1++; + *ptr2++ = *ptr1++; + } + ptr1 += w4; + } + + *rgb_width = w; + *rgb_height = h; + + return data; +} + +void PR_CALLBACK scanimage_thread_routine( void * arg ) +{ + nsSanePluginInstance * pthis = (nsSanePluginInstance *)arg; + + nsresult rv; + SANE_Status status; + SANE_Parameters param; + int len; + FILE * out; + SANE_Byte * buf; + JSAMPROW row_pointer[1]; + char *fname; + + // JPEG stuff + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + // Error handling + jerr.error_exit = my_jpeg_error_exit; // fatal errors + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_compress(&cinfo); + + rv = pthis->OpenSaneDeviceIF(); + if (rv != NS_OK) + goto error_exit; + + // open image file + fname = pthis->GetImageFilename(); + if (!fname) + goto error_exit; + out = fopen(fname, "wb"); + PR_FREEIF(fname); + if (out == NULL) { + NS_ERROR("Unable to open mImageFilename!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + jpeg_stdio_dest(&cinfo, out); + + status = sane_start(pthis->mSaneHandle); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to start sane device!"); + goto error_exit; + } + + status = sane_get_parameters(pthis->mSaneHandle, ¶m); + if (status != SANE_STATUS_GOOD) { + NS_ERROR("Error trying to get image parameters!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + cinfo.image_width = param.pixels_per_line; + cinfo.image_height = param.lines; + + switch (param.format) { + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + // NOT Supported! + NS_ERROR("Multiframe devices not supported!\n"); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + break; + + case SANE_FRAME_RGB: + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + break; + + case SANE_FRAME_GRAY: + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + break; + } + jpeg_set_defaults(&cinfo); + cinfo.dct_method = pthis->mCompMethod; + jpeg_set_quality(&cinfo, pthis->mCompQuality, FALSE); + jpeg_start_compress(&cinfo, TRUE); + + buf = (SANE_Byte *)PR_MALLOC(param.bytes_per_line); + if (!buf) { + NS_ERROR("Error trying to allocate buffer!\n"); + jpeg_destroy_compress(&cinfo); + fclose(out); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + + int total; + while(1) { + total = 0; + while (total < param.bytes_per_line) { + // Read in data from sane device + status = sane_read(pthis->mSaneHandle, buf + total, + param.bytes_per_line - total, + &len); + if (status != SANE_STATUS_GOOD) { + if (status == SANE_STATUS_EOF) + // done with this frame + goto finished_scan; + else { + NS_ERROR("Error trying to read from sane device!\n"); + PR_FREEIF(buf); + jpeg_destroy_compress(&cinfo); + fclose(out); + sane_cancel(pthis->mSaneHandle); + goto error_exit; + } + } + total += len; + } + + row_pointer[0] = (JSAMPLE *) buf; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + + } // end while + + // cleanup + finished_scan: + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + PR_FREEIF(buf); + sane_cancel(pthis->mSaneHandle); + fclose(out); + pthis->SetSuccess(PR_TRUE); // allow caller to check for success + + error_exit: + + //////////////////////////////////////////////// + // Call onScanComplete callback in the UI thread + + nsCOMPtr eventQ; + + // Get the event queue of the current thread... + NS_WITH_SERVICE(nsIEventQueueService, eventQService, + kEventQueueService, &rv); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to get event queue service!\n"); + return; + } + + rv = eventQService->CreateThreadEventQueue(); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to create new thread event queue!\n"); + // Does this mean that an event queue is already at + // our disposal? + } + + rv = eventQService->GetThreadEventQueue(pthis->mUIThread, + getter_AddRefs(eventQ)); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to get thread queue!\n"); + return; + } + + PLEvent *event = new PLEvent; + if (!event) { + NS_ERROR("Unable to create new event!\n"); + return; + } + + // ThreadHandleEvent will now execute in the UI thread. + PL_InitEvent(event, + pthis, + (PLHandleEventProc) ThreadedHandleEvent, + (PLDestroyEventProc) ThreadedDestroyEvent); + + if (eventQ->PostEvent(event) != PR_SUCCESS) { + NS_ERROR("Error trying to post event!\n"); + return; + } + + return; +} + +void PR_CALLBACK ThreadedHandleEvent(PLEvent * event) +{ + nsSanePluginInstance *pthis = (nsSanePluginInstance *)event->owner; + + // If this isn't the UI's thread then we are hosed! + if (PR_FALSE == pthis->IsUIThread()) { + NS_ERROR("Attempt to execute event from the wrong thread!\n"); + return; + } + + pthis->SetState(0); // no longer scanning + pthis->Restore(); // repaint image + + // Notify user that routine is finished + pthis->DoScanCompleteCallback(); +} + +void PR_CALLBACK ThreadedDestroyEvent(PLEvent* aEvent) +{ + delete aEvent; +} + +void store_filename(GtkFileSelection *selector, gpointer user_data) +{ + nsSanePluginInstance *pthis = (nsSanePluginInstance *) user_data; + gchar *cmd; + GtkWidget * fs; + char * orig_filename; + char * dest_filename; + + if (!user_data || !selector) + return; + + fs = pthis->GetFileSelection(); + orig_filename = pthis->GetImageFilename(); + if ( !fs || !orig_filename ) + return; + + dest_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)); + if ( !dest_filename ) + return; + +#ifdef DEBUG + printf("Saving %s to %s\n", orig_filename, dest_filename); +#endif + + /* + * I'm just doing a simple 'cp' command with the shell. + * This probobly should be done in a NSPR safe kind + * of way. + */ + + // cp /tmp/nsSanePlugin.###.jpg /path/to/save/to.jpg + cmd = (gchar *)PR_Malloc(PL_strlen(orig_filename) + + PL_strlen(dest_filename) + 6); + if (!cmd) + return; + + sprintf(cmd, "cp %s %s", orig_filename, dest_filename); + system(cmd); + + PR_FREEIF(cmd); +} + +// END of Helper Global Functions +////////////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.h b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.h new file mode 100644 index 00000000000..b4d2ed6d76f --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.h @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Declares the nsSanePluginInterface class for the SANE plugin. + */ + +#ifndef __NS_SANE_PLUGIN_H__ +#define __NS_SANE_PLUGIN_H__ + +#include "prthread.h" +#include "nscore.h" +#include "nsplugin.h" +#include "gdksuperwin.h" +#include "gtkmozbox.h" +#include "gtk/gtk.h" +#include "nsIPlugin.h" +#include "nsSanePluginControl.h" +#include "nsIScriptContext.h" +#include "nsIServiceManager.h" +#include "nsISupports.h" +#include "nsISupportsUtils.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" + +/////////////////////////////////// +// Needed for encoding jpeg images +extern "C" +{ +#include +} + +/////////////////////////////////// +// Needed for SANE interface +extern "C" +{ +#include +#include +#include +} + +typedef struct _PlatformInstance +{ + Window window; + GtkWidget * widget; + GdkSuperWin * superwin; + Display * display; + uint16 x; + uint16 y; + uint32 width; + uint32 height; + +} PlatformInstance; + +// Threaded routine for grabbing a frame from device. +// If a callback for onScanComplete was set in the embed/object +// tag, then this routine will call it before exiting. +//void PR_CALLBACK scanimage_thread_routine( void * arg); +void PR_CALLBACK scanimage_thread_routine( void * arg ); + +class nsSanePluginInstance : public nsIPluginInstance, + public nsISanePluginInstance +{ + friend void PR_CALLBACK scanimage_thread_routine( void *); + + public: + + nsSanePluginInstance(void); + virtual ~nsSanePluginInstance(void); + + ///////////////////////////////////////////////////////////////////// + // nsIPluginInstance inherited interface + NS_IMETHOD Initialize(nsIPluginInstancePeer* peer); + NS_IMETHOD GetPeer(nsIPluginInstancePeer* *result); + NS_IMETHOD Start(void); + NS_IMETHOD Stop(void); + NS_IMETHOD Destroy( void ); + NS_IMETHOD SetWindow( nsPluginWindow* window ); + NS_IMETHOD NewStream( nsIPluginStreamListener** listener ); + NS_IMETHOD Print( nsPluginPrint* platformPrint ); + NS_IMETHOD GetValue( nsPluginInstanceVariable variable, void *value ); + + // Not used for this platform! Only a placeholder. + NS_IMETHOD HandleEvent( nsPluginEvent* event, PRBool* handled ); + + // End of nsIPlugIninstance inherited interface. + ///////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////// + // nsSanePluginInstance specific methods: + NS_DECL_ISUPPORTS ; + + // Execute given callback in window's JavaScript + NS_IMETHOD DoScanCompleteCallback(); + NS_IMETHOD DoInitCompleteCallback(); + + void SetMode(nsPluginMode mode) { fMode = mode; } + void SetState(PRInt32 aState) { mState = aState; }; + NS_IMETHOD PaintImage(void); + char * GetImageFilename(); + GtkWidget * GetFileSelection(); + PRBool IsUIThread(); + nsresult OpenSaneDeviceIF( void ); + + //*** Methods exposed through the XPConnect interface *** + NS_DECL_NSISANEPLUGININSTANCE + +private: + + GtkWidget *mDrawing_area; + GtkWidget *mEvent_box; + PlatformInstance fPlatform; + char mImageFilename[255]; + GtkWidget *mFileSelection; + GdkRectangle mZoom_box; + unsigned char *mRGBData; + int mRGBWidth, mRGBHeight; + + // line attributes for zoom box + PRInt32 mLineWidth; + GdkLineStyle mLineStyle; + GdkCapStyle mCapStyle; + GdkJoinStyle mJoinStyle; + + // zoom box change variables + float mTopLeftXChange; + float mTopLeftYChange; + float mBottomRightXChange; + float mBottomRightYChange; + + // jpeg compression attributes + int mCompQuality; + enum J_DCT_METHOD mCompMethod; + + // sane specific members + SANE_Handle mSaneHandle; + SANE_String mSaneDevice; + SANE_Bool mSaneOpen; + PRBool mSuccess; + PRInt32 mState; + + // needed for JavaScript Callbacks + char *mOnScanCompleteScript; + char *mOnInitCompleteScript; + PRThread *mScanThread; + PRThread *mUIThread; + +protected: + + nsIPluginInstancePeer* fPeer; + nsPluginWindow* fWindow; + nsPluginMode fMode; + nsIPluginManager* mPluginManager; + +private: + + int WritePNMHeader (int fd, SANE_Frame format, + int width, int height, + int depth); + + void PlatformNew( void ); + nsresult PlatformDestroy( void ); + PRInt16 PlatformHandleEvent( nsPluginEvent* event ); + nsresult PlatformSetWindow( nsPluginWindow* window ); +}; + +class nsSanePluginStreamListener : public nsIPluginStreamListener +{ + public: + + NS_DECL_ISUPPORTS ; + + /* + * Notify the observer that the URL has started to load. This method is + * called only once, at the beginning of a URL load.

+ * + * @return The return value is currently ignored. In the future it may be + * used to cancel the URL load.. + */ + NS_IMETHOD OnStartBinding( nsIPluginStreamInfo* pluginInfo ); + + /** + * Notify the client that data is available in the input stream. This + * method is called whenver data is written into the input stream by the + * networking library...

+ * + * @param aIStream The input stream containing the data. This stream can + * be either a blocking or non-blocking stream. + * @param length The amount of data that was just pushed into the + * stream. + * @return The return value is currently ignored. + */ + NS_IMETHOD OnDataAvailable( nsIPluginStreamInfo* pluginInfo, + nsIInputStream* input, + PRUint32 length ); + NS_IMETHOD OnFileAvailable( nsIPluginStreamInfo* pluginInfo, + const char* fileName ); + + /** + * Notify the observer that the URL has finished loading. This method is + * called once when the networking library has finished processing the + * URL transaction initiatied via the nsINetService::Open(...) call. + *

+ * + * This method is called regardless of whether the URL loaded + * successfully.

+ * + * @param status Status code for the URL load. + * @param msg A text string describing the error. + * @return The return value is currently ignored. + */ + NS_IMETHOD OnStopBinding( nsIPluginStreamInfo* pluginInfo, + nsresult status ); + NS_IMETHOD OnNotify( const char* url, nsresult status ); + NS_IMETHOD GetStreamType( nsPluginStreamType *result ); + + /////////////////////////////////////////////////////////////////////////// + // snPluginStreamListener specific methods: + + nsSanePluginStreamListener( nsSanePluginInstance* inst ); + virtual ~nsSanePluginStreamListener( void ); + + nsSanePluginInstance* mPlugInst; +}; + +#endif // __NS_SANE_PLUGIN_H__ diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginControl.idl b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginControl.idl new file mode 100644 index 00000000000..58eafab50f5 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginControl.idl @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Defines scriptable interface to SANE plugin. + */ + +#include "nsISupports.idl" +#include "domstubs.idl" + +[scriptable, uuid(10982800-365e-11d3-a2bf-0004ac779ef3)] +/** + * This interface can be obtained with the following HTML/JavaScript sequence: + * + *
+ * 
+ *
+ * 
+ * + * + * + *
+ * + *
+ * + * This fragment will create an embedded plugin, which can then be accessed + * and controlled by the nsISanePluginInstance interface which is instantiated + * in the script. + */ +interface nsISanePluginInstance : nsISupports +{ + ///////////////////////////////////////////////////////////////////// + // Plugin Status Interface + + // Read-Only: Contains completion status of last operation + attribute boolean Success; + + // Read-Only: Contains the current state of the scanner (IDLE|BUSY) + attribute string State; + + ///////////////////////////////////////////////////////////////////// + // Image Preview Interface + + // Move zoom box to a given geometry in one step + void ZoomImage(in unsigned short x, in unsigned short y, + in unsigned short width, in unsigned short height); + void ZoomImageWithAttributes(in unsigned short x, + in unsigned short y, + in unsigned short width, + in unsigned short height, + in long req_line_width, + in string req_line_style, + in string req_cap_style, + in string req_join_style); + + // Undo all croping and reset zoom box for entire image + void Restore (); + + // Zoom in on the image and reset the zoom box to contain + // the entire image. + void Crop(in unsigned short x, in unsigned short y, + in unsigned short width, in unsigned short height); + + // Read/Write zoom box geometry + // (Each write will trigger a refresh!) + attribute unsigned short ZoomX; + attribute unsigned short ZoomY; + attribute unsigned short ZoomWidth; + attribute unsigned short ZoomHeight; + + // Read/Write zoom box line attributes + // (Each write will trigger a refresh!) + attribute long ZoomLineWidth; + attribute string ZoomLineStyle; + attribute string ZoomCapStyle; + attribute string ZoomJoinStyle; + + // Read-Only zoom box change indicators + // (For SANE devices that support changing the scan area, + // this allows for the controling JavaScript to safely adjust + // the scan area in a device specific manner.) + attribute float ZoomBR_XChange; // % bottom right x change on last zoom + attribute float ZoomBR_YChange; // % bottom right y ... + attribute float ZoomTL_XChange; // % top left x ... + attribute float ZoomTL_YChange; // % top left y ... + + // Read/Write JPEG compression attributes + attribute long Quality; + attribute string Method; + + ////////////////////////////////////////////////////////////////////// + // Generic SANE Interface + + // READ_ONLY: returns a colon delimeted list of option descriptions + // where each option description is a comma delimited + // list of values + attribute string DeviceOptions; + + // Returns or sets the active device. + attribute string ActiveDevice; + + // READ_ONLY: returns a comma delimited list of image parameters + attribute string ImageParameters; + + // READ_ONLY: returns a comma delimited list of available devices + attribute string AvailableDevices; + + // Pull image from device with current option settings. + // As a side effect, the zoom box is set to contain the + // entire image. + void ScanImage(); + void SetOption(in string name, in string value); + + // Pop up a dialog to save current image to a file + void SaveImage(); + }; + +%{ C++ +//10982800-365e-11d3-a2bf-0004ac780ef3 +#define NS_SANE_PLUGIN_CONTROL_CID \ + { 0x10982800, 0x365e, 0x11d3, { 0xa2, 0xbf, 0x0, 0x04, 0xac, 0x78, 0x0e, 0xf3 }} + +%} diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.cpp b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.cpp new file mode 100644 index 00000000000..977aaa6bec1 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.cpp @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Implements the SANE plugin factory class. + */ + +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsSanePlugin_CID.h" +#include "nsSanePlugin.h" +#include "nsSanePluginFactory.h" +#include "plstr.h" + +#define PLUGIN_NAME "SANE Plugin" +#define PLUGIN_DESCRIPTION "SANE Plugin is a generic scanner interface" +#define PLUGIN_MIME_DESCRIPTION "application/X-sane-plugin::Scanner/Camera" +#define PLUGIN_MIME_TYPE "application/X-sane-plugin" + +static NS_DEFINE_IID( kISupportsIID, NS_ISUPPORTS_IID ); +static NS_DEFINE_IID( kIFactoryIID, NS_IFACTORY_IID ); +static NS_DEFINE_IID( kIPluginIID, NS_IPLUGIN_IID ); +static NS_DEFINE_CID( kComponentManagerCID, NS_COMPONENTMANAGER_CID ); +static NS_DEFINE_CID( knsSanePluginControlCID, NS_SANE_PLUGIN_CONTROL_CID ); +static NS_DEFINE_CID( knsSanePluginInst, NS_SANE_PLUGIN_CID ); + +//////////////////////////////////////////////////////////////////////// + +nsSanePluginFactoryImpl::nsSanePluginFactoryImpl( const nsCID &aClass, + const char* className, + const char* progID ) + : mClassID(aClass), mClassName(className), mProgID(progID) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::nsSanePluginFactoryImpl()\n"); +#endif + + NS_INIT_REFCNT(); + +} + +nsSanePluginFactoryImpl::~nsSanePluginFactoryImpl() +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::~nsSanePluginFactoryImpl()\n"); +#endif + + printf("mRefCnt = %i\n", mRefCnt); + NS_ASSERTION(mRefCnt == 0, "non-zero refcnt at destruction"); +} + +NS_IMETHODIMP +nsSanePluginFactoryImpl::QueryInterface(const nsIID &aIID, + void **aInstancePtr) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::QueryInterface()\n"); +#endif + + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*,this); + } else if (aIID.Equals(kIFactoryIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*, + NS_STATIC_CAST(nsIFactory*,this)); + } else if (aIID.Equals(kIPluginIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports*, + NS_STATIC_CAST(nsIPlugin*,this)); + } else { + *aInstancePtr = nsnull; + return NS_ERROR_NO_INTERFACE; + } + + NS_ADDREF(NS_REINTERPRET_CAST(nsISupports*,*aInstancePtr)); + + return NS_OK; +} + +// Standard implementation of AddRef and Release +NS_IMPL_ADDREF( nsSanePluginFactoryImpl ); +NS_IMPL_RELEASE( nsSanePluginFactoryImpl ); + +NS_IMETHODIMP +nsSanePluginFactoryImpl::CreateInstance( nsISupports *aOuter, + const nsIID &aIID, + void **aResult) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::CreateInstance()\n"); +#endif + + if ( !aResult ) + return NS_ERROR_NULL_POINTER; + + if ( aOuter ) + return NS_ERROR_NO_AGGREGATION; + + nsSanePluginInstance * inst = new nsSanePluginInstance(); + if (!inst) + return NS_ERROR_OUT_OF_MEMORY; + + inst->AddRef(); + *aResult = inst; + return NS_OK; +} + +nsresult +nsSanePluginFactoryImpl::LockFactory(PRBool aLock) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::LockFactory()\n"); +#endif + + // Needs to be implemented + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::Initialize() +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::Initialize()\n"); +#endif + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::Shutdown( void ) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::Shutdown()\n"); +#endif + + return NS_OK; +} + + +NS_METHOD +nsSanePluginFactoryImpl::GetMIMEDescription(const char* *result) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::GetMIMEDescription()\n"); +#endif + + // caller is responsible for releasing + *result = PL_strdup( PLUGIN_MIME_DESCRIPTION ); + + return NS_OK; +} + +NS_METHOD +nsSanePluginFactoryImpl::GetValue( nsPluginVariable variable, void *value ) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::GetValue()\n"); +#endif + + nsresult err = NS_OK; + + if ( variable == nsPluginVariable_NameString ) { + + *( ( char ** )value ) = strdup( PLUGIN_NAME ); + + } else if ( variable == nsPluginVariable_DescriptionString ) { + + *( ( char ** )value ) = strdup( PLUGIN_DESCRIPTION ); + + } else { + + err = NS_ERROR_FAILURE; + + } + + return err; +} + + +NS_IMETHODIMP +nsSanePluginFactoryImpl::CreatePluginInstance( nsISupports *aOuter, + REFNSIID aIID, + const char* aPluginMIMEType, + void **aResult) +{ +#ifdef DEBUG + printf("nsSanePluginFactoryImpl::CreatePluginInstance()\n"); +#endif + + // Need to find out what this is used for. The npsimple + // plugin looks like it just does a CreateInstance and + // ignores the mime type. + return NS_ERROR_NOT_IMPLEMENTED; +} + + +//////////////////////////////////////////////////////////////////////// + +/** + * The XPCOM runtime will call this to get a new factory object for the + * CID/progID it passes in. XPCOM is responsible for caching the resulting + * factory. + * + * return the proper factory to the caller + */ +extern "C" PR_IMPLEMENT(nsresult) +NSGetFactory( nsISupports* aServMgr, + const nsCID &aClass, + const char *aClassName, + const char *aProgID, + nsIFactory **aFactory) +{ + if (! aFactory) + return NS_ERROR_NULL_POINTER; + + nsSanePluginFactoryImpl* factory = new nsSanePluginFactoryImpl(aClass, + aClassName, + aProgID); + if ( factory == nsnull ) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(factory); + *aFactory = factory; + return NS_OK; + +} + +char *buf; + +extern "C" PR_IMPLEMENT( nsresult ) +NSRegisterSelf( nsISupports* aServMgr, const char* aPath ) +{ + nsresult rv; + + nsCOMPtr servMgr( do_QueryInterface( aServMgr, &rv ) ); + if ( NS_FAILED( rv ) ) + return rv; + + NS_WITH_SERVICE( nsIComponentManager, compMgr, kComponentManagerCID, &rv ); + if ( NS_FAILED( rv ) ) + return rv; + + // Register the plugin control portion. + rv = compMgr->RegisterComponent(knsSanePluginControlCID, + "SANE Plugin Control", + "component://netscape/plugins/sane-control", + aPath, PR_TRUE, PR_TRUE ); + + // Register the plugin portion. + nsString progID; + progID.AssignWithConversion( NS_INLINE_PLUGIN_PROGID_PREFIX ); + + progID.AppendWithConversion(PLUGIN_MIME_TYPE); + buf = ( char * )calloc( 2000, sizeof( char ) ); + progID.ToCString( buf, 1999 ); + + rv = compMgr->RegisterComponent( knsSanePluginInst, + "SANE Plugin Component", + buf, + aPath, PR_TRUE, PR_TRUE); + free( buf ); + + if ( NS_FAILED( rv ) ) + return rv; + + return NS_OK; + +} + +extern "C" PR_IMPLEMENT( nsresult ) +NSUnregisterSelf(nsISupports* aServMgr, const char* aPath) +{ + nsresult rv; + + nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); + if (NS_FAILED(rv)) return rv; + + NS_WITH_SERVICE(nsIComponentManager, compMgr, kComponentManagerCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = compMgr->UnregisterComponent(knsSanePluginControlCID, aPath); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.h b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.h new file mode 100644 index 00000000000..9c1b2f9b6e8 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePluginFactory.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Contributor(s): + * Rusty Lynch + */ +/* + * Declares the SANE plugin factory class. + */ + +#ifndef __NS_SANE_PLUGIN_FACTORY_H__ +#define __NS_SANE_PLUGIN_FACTORY_H__ + +class nsSanePluginFactoryImpl : public nsIPlugin +{ +public: + nsSanePluginFactoryImpl(const nsCID &aClass, const char* className, + const char* progID); + + // nsISupports methods + NS_DECL_ISUPPORTS ; + + // nsIFactory methods + NS_IMETHOD CreateInstance(nsISupports *aOuter, + const nsIID &aIID, + void **aResult); + + NS_IMETHOD LockFactory(PRBool aLock); + NS_IMETHOD Initialize(void); + NS_IMETHOD Shutdown(void); + NS_IMETHOD GetMIMEDescription(const char* *result); + NS_IMETHOD GetValue(nsPluginVariable variable, void *value); + NS_IMETHOD CreatePluginInstance(nsISupports *aOuter, REFNSIID aIID, + const char* aPluginMIMEType, + void **aResult); + +protected: + virtual ~nsSanePluginFactoryImpl(); + + nsCID mClassID; + const char* mClassName; + const char* mProgID; + +}; + +#endif // __NS_SANE_PLUGIN_FACTORY_H__ + + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin_CID.h b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin_CID.h new file mode 100644 index 00000000000..1efbf6c526f --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin_CID.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * Contributor(s): + * + * Rusty Lynch + */ +/* + * Defines the CID for the SANE plugin. + */ + +#ifndef __NS_SANE_PLUGIN_CID_H__ +#define __NS_SANE_PLUGIN_CID_H__ + +#define NS_SANE_PLUGIN_CID { 0x302da841, 0x3dbf, 0x11d3, { 0xbc, 0xfb, 0x0, 0xa0, 0xc9, 0xc8, 0xd9, 0x1d } } + +#endif // __NS_SANE_PLUGIN_CID_H__ diff --git a/mozilla/modules/plugin/samples/SanePlugin/test/camera.html b/mozilla/modules/plugin/samples/SanePlugin/test/camera.html new file mode 100644 index 00000000000..c42774a81d0 --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/test/camera.html @@ -0,0 +1,167 @@ + + + CPia Digital Camera Test Application + + + +

+ This is a test page for grabing frames from a USB Cpia camera + using the v4l (Video4Linux) SANE module. +

+ + + + + + +
+
+ +
+
+
+ + + + + + + + +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/plugin/samples/SanePlugin/test/scanner.html b/mozilla/modules/plugin/samples/SanePlugin/test/scanner.html new file mode 100644 index 00000000000..146025eaf9b --- /dev/null +++ b/mozilla/modules/plugin/samples/SanePlugin/test/scanner.html @@ -0,0 +1,301 @@ + + + Image Scanner Application + + +

+ This is a simple test page for the HP 6300C USB scanner. +

+ + + + + +
+
+ +
+
+
+ + +
+ +
+

+ + + +

+ + + +
+

+ + + + +
+
+ + + + + + + + + + + + + +