/***** * plc.c : XmHTML Progressive Loader Context interfacing routines. * * This file Version $Revision: 1.10 $ * * Creation date: Thu Jun 12 16:46:34 GMT+0100 1997 * Last modification: $Date: 1999/07/29 01:26:29 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * * Copyright (C) 1994-1997 by Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library. * * Changes for the Gtk port by Federico Mena * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: plc.c,v $ * Revision 1.10 1999/07/29 01:26:29 sopwith * * * Fix all warnings. * * Revision 1.9 1998/02/12 03:09:43 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.8 1998/01/07 01:45:38 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include #endif #include #include #include #if defined (HAVE_LIBPNG) || defined(HAVE_LIBZ) #include #endif #if defined(HAVE_LIBJPEG) #include #endif #ifndef WITH_MOTIF # include # include #endif #include "XmHTMLP.h" #include "XmHTMLfuncs.h" #ifdef WITH_MOTIF # include "XCCP.h" #endif #include "plc.h" /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ /*** Private Function Prototype Declarations ****/ static void _PLCInsert(PLC *plc); static void _PLCRemove(PLC *plc); static void _PLCRun(PLC *plc); static void _PLCEndData(PLC *plc); static void _PLCRecomputeDelays(XmHTMLWidget html); #ifdef PLC_WORKPROCS static Boolean _PLCSubCycler(TPointer call_data); #else static void _PLCRecomputeDelays(XmHTMLWidget html); #endif /* PLC_WORKPROCS */ /***** * PLCProc transfer, finalize and low-level init functions for images. *****/ static void _PLC_IMG_Init(PLC *plc); static void _PLC_IMG_Transfer(PLC *plc); static void _PLC_IMG_Finalize(PLC *plc); static void _PLC_IMG_UpdateScreen(XmHTMLWidget html, XmHTMLImage *image, XmHTMLObjectTableElement elePtr, int src_y, Dimension height); static void _PLC_IMG_UpdateScreenCopies(XmHTMLWidget html, XmHTMLImage *image, int src_y, Dimension height); /***** * PLCProc transfer, finalize and low-level init functions for documents. *****/ static void _PLC_DOC_Init(PLC *plc); static void _PLC_DOC_Transfer(PLC *plc); static void _PLC_DOC_Finalize(PLC *plc); /***** * PLCProc transfer, finalize and low-level init functions for unknown objects. *****/ static void _PLC_ANY_Init(PLC *plc); static void _PLC_ANY_Transfer(PLC *plc); static void _PLC_ANY_Finalize(PLC *plc); /*** Private Variable Declarations ***/ /*************** ***** PLC Support functions ***************/ /***** * Name: _PLCDataRequest * Return Type: Boolean * Description: makes a get_data() request for the current PLC * In: * plc: current PLC * Returns: * True when request was served, False if not. plc_status is also updated * to reflect actual request return code. *****/ Boolean _PLCDataRequest(PLC *plc) { int status; static XmHTMLPLCStream plc_context; if(plc == NULL) return(False); _XmHTMLDebug(14, ("plc.c: _PLCDataRequest called for %s\n", plc->url)); /* *very* usefull sanity */ if(plc->max_in == 0 || plc->max_in < plc->min_in) plc->max_in = plc->input_size; /***** * next_in is the current position in the destination buffer, * so we need to make sure that next and max_in do not exceed input * buffer size *****/ if(plc->left + plc->max_in > plc->buf_size) plc->max_in = plc->buf_size - plc->left; /* yet another sanity */ if(plc->max_in && plc->min_in >= plc->max_in) plc->min_in = 0; /* fill stream buffer */ plc_context.total_in = plc->total_in; /* bytes received so far */ plc_context.min_out = plc->min_in; /* minimum no of bytes requested */ plc_context.max_out = plc->max_in; /* maximum no of bytes requested */ plc_context.user_data = plc->user_data; /* user_data for this PLC */ _XmHTMLDebug(14, ("plc.c: _PLCDataRequest, requesting anywhere between %i " "and %i bytes\n", plc->min_in, plc->max_in)); /* get data from the external data stream */ if((status = plc->sf.get_data(&plc_context, plc->input_buffer)) > 0) { /* bad copy, issue warning but proceed. */ if(status < plc->min_in) { _XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLCGetData"), "Improperly served PLC get_data() request:\n" " Received %i bytes while %i is minimally required.", status, plc->min_in); } if(status > plc->max_in) { _XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLCGetData"), "Improperly served PLC get_data() request:\n" " Received %i bytes while %i is maximally allowed. Excess " " data ignored.", status, plc->max_in); status = plc->max_in; } _XmHTMLDebug(14, ("plc.c: _PLCDataRequest, got %i bytes.\n", status)); /* more than min_in bytes returned, activate plc */ plc->plc_status = PLC_ACTIVE; /* update received byte count */ plc->total_in += status; /***** * move data left to the beginning of the buffer (thereby discarding * already processed data) *****/ if(plc->left) plc->buffer = memmove(plc->buffer, plc->buffer + (plc->size - plc->left), plc->left); /* append newly received data */ (void)memcpy(plc->buffer + plc->left, plc->input_buffer, status); /* this many bytes are valid in the buffer */ plc->size = plc->left + status; /* reset current ptr position */ plc->next_in = plc->buffer; /* this many bytes left for reading in the buffer */ plc->left += status; return(True); } /* check return value in most logical (?) order */ if(status == STREAM_RESIZE) { /* we have been requested to resize the buffers */ if(plc_context.max_out <= 0) { /* this *is* definitly an error */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLCDataRequest"), "You are using a darn stupid application: " " It requested me to crash\n myself by resizing my PLC " "buffers to zero!\n Go complain to the stupid devil " "that wrote it."); return(False); } /* resize input buffer */ plc->input_buffer = (Byte*)realloc(plc->input_buffer, plc_context.max_out * sizeof(Byte)); plc->input_size = plc_context.max_out; plc->buf_size = plc_context.max_out; plc->max_in = plc_context.max_out; /***** * Always backtrack if we have data left in the current buffer. * We make it ourselves easy here and let the user worry about it. *****/ if(plc->left) { plc->total_in -= plc->left; plc->left = 0; plc->next_in = NULL; plc->size = 0; } /* resize current data buffer */ plc->buffer = (Byte*)realloc(plc->buffer, plc->buf_size * sizeof(Byte)); /* and call ourselves again with the new buffers in place */ return(_PLCDataRequest(plc->self)); } if(status == STREAM_SUSPEND) { /* not enough data available, suspend this plc */ _XmHTMLDebug(14, ("plc.c: _PLCDataRequest, suspending this PLC\n")); plc->plc_status = PLC_SUSPEND; plc->plc_data_status = STREAM_SUSPEND; } else if(status == STREAM_END) { /* all data has been received, terminate plc */ _XmHTMLDebug(14, ("plc.c: _PLCDataRequest, terminating PLC\n")); plc->plc_status = PLC_COMPLETE; plc->plc_data_status = STREAM_END; } else { /* plc has been aborted */ _XmHTMLDebug(14, ("plc.c: _PLCDataRequest, aborting PLC\n")); plc->plc_status = PLC_ABORT; plc->plc_data_status = STREAM_ABORT; } return(False); } /***** * Name: _PLCEndData * Return Type: void * Description: calls the end_data() function to signal this PLC is * terminating. * In: * plc: current PLC * Returns: * *****/ static void _PLCEndData(PLC *plc) { static XmHTMLPLCStream plc_context; PLCImage *any_image = &(plc->object->plc_any_image); XmImageInfo *image; /* potential memory leak if XmNprogressiveEndProc resource is empty */ if(plc->sf.end_data == NULL) { _XmHTMLWarning(__WFUNC__(any_image->owner, "_PLCEndData"), "Potential memory leak detected: no XmNprogressiveEndProc " "function present!"); return; } _XmHTMLDebug(14, ("plc.c: _PLCEndData called for %s\n", plc->url)); plc_context.total_in = plc->total_in; /* bytes received so far */ plc_context.min_out = 0; /* meaningless */ plc_context.max_out = 0; /* meaningless */ plc_context.user_data = plc->user_data; /* user_data for this PLC */ if(plc->object->type == plcAny || plc->object->type == plcDocument) { plc->sf.end_data(&plc_context, NULL, XmHTML_NONE, /* FIXME: XmHTML_NONE -> ??? */ (plc->plc_status == PLC_COMPLETE)); return; } image = any_image->info; plc->sf.end_data(&plc_context, (TPointer)image, XmPLC_IMAGE, (plc->plc_status == PLC_COMPLETE)); } /***** * Name: _PLCReadOK * Return Type: size_t * Description: copy len bytes to buf from an ImageBuffer * In: * *plc: current PLC * buf: data destination * len: no of bytes to copy * Returns: * actual no of bytes read or 0 on failure or end of buffer. * This function will make a data request if the current buffer runs out * of data. *****/ size_t _PLCReadOK(PLC *plc, Byte *buf, int len) { /* plc->left is the number of bytes left in the input buffer. */ if(len <= plc->left) { buf = memcpy(buf, plc->next_in, len); /* new position in buffer */ plc->next_in += len; /* this many bytes still available */ plc->left -= len; return(len); } /* not enough data available, make a request */ plc->min_in = len - plc->left; plc->max_in = PLC_MAX_BUFFER_SIZE; if(!(_PLCDataRequest(plc))) return(0); /* suspended, aborted or end of data */ /* read again */ return(_PLCReadOK(plc, buf, len)); } /***** * Name: _PLCGetDataBlock * Return Type: int * Description: gets the next amount of data from the input buffer * In: * plc: current PLC * buf: storage buffer, filled upon return. * Returns: * no of bytes copied into buf or 0 when no more data. *****/ size_t _PLCGetDataBlock(PLC *plc, Byte *buf) { Byte count = 0; if(!_PLCReadOK(plc, &count, 1)) return(0); if(((int)count != 0) && (!_PLCReadOK(plc, buf, (int)count))) return(0); return((size_t)count); } /*************** ***** PLC usage functions ***************/ /***** * Name: _XmHTMLPLCCreate * Return Type: PLC* * Description: creates a PLC for the given widget * In: * html: XmXmHTMLWidget id; * url: object for which this PLC is created; * priv..: private data to be registered for this PLC. * type: type of PLC to be created * Returns: * a newly created PLC. * Note: * Type indicates what type of object should be created. It can be * Xm_HTML_NONE, XmPLC_IMAGE or XmPLC_DOCUMENT. * The priv_data argument is not the same as the user_data field. It allows * an application to attach internal data to a PLC. ****/ PLC* _XmHTMLPLCCreate(XmHTMLWidget html, TPointer priv_data, String url, Byte type) { static PLC *plc; _XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCreate for %s\n", url)); plc = (PLC*)malloc(sizeof(PLC)); memset(plc, 0, sizeof(PLC)); plc->url = strdup(url); plc->buffer = (Byte*)malloc(PLC_MAX_BUFFER_SIZE); plc->buf_size = (int)PLC_MAX_BUFFER_SIZE; plc->size = (int)0; plc->left = (int)0; plc->next_in = (Byte*)NULL; plc->input_buffer = (Byte*)malloc(PLC_MAX_BUFFER_SIZE); plc->input_size = (int)PLC_MAX_BUFFER_SIZE; plc->total_in = (int)0; plc->max_in = (int)PLC_MAX_BUFFER_SIZE; plc->min_in = (int)0; plc->object = (PLCObject*)calloc(1, sizeof(PLCObject)); plc->object->plc_any.owner = html; /* a privatly ownded GC for XPutImage to use */ if(html->html.plc_gc == NULL) { #ifdef WITH_GTK GdkGCValues values; GtkWidget *w = GTK_WIDGET (html->html.work_area); values.function = GDK_COPY; /* X default for plane_mask is AllPlanes, so there is no need to set it */ html->html.plc_gc = gdk_gc_new_with_values((GTK_WIDGET_REALIZED(GTK_WIDGET(html)) ? Toolkit_Widget_Window (w) : Toolkit_Default_Root_Window (0)), &values, GDK_GC_FUNCTION); #else XGCValues xgc; xgc.function = GXcopy; xgc.plane_mask = AllPlanes; html->html.plc_gc = XCreateGC(XtDisplay((Widget)html), (XtIsRealized((Widget)html) ? XtWindow(html->html.work_area) : DefaultRootWindow(XtDisplay((Widget)html))), GCFunction | GCPlaneMask , &xgc); #endif } plc->plc_status = PLC_ACTIVE; plc->plc_data_status = STREAM_OK; plc->priv_data = priv_data; /* must be updated by caller */ plc->user_data = (XtPointer)NULL; /* can be overriden by caller */ plc->sf.get_data = html->html.get_data; plc->sf.end_data = html->html.end_data; /* object specific transfer, finalize and low-level init functions */ if(type == XmPLC_IMAGE) { plc->object->type = plcAnyImage; plc->transfer = (PLCProc)_PLC_IMG_Transfer; plc->finalize = (PLCProc)_PLC_IMG_Finalize; plc->sf.c_new = (PLCProc)_PLC_IMG_Init; plc->obj_set = False; } else if(type == XmPLC_DOCUMENT) { plc->object->type = plcDocument; plc->transfer = (PLCProc)_PLC_DOC_Transfer; plc->finalize = (PLCProc)_PLC_DOC_Finalize; plc->sf.c_new = (PLCProc)_PLC_DOC_Init; plc->obj_set = False; } else /* type is unknown */ { plc->object->type = plcAny; plc->transfer = (PLCProc)_PLC_ANY_Transfer; plc->finalize = (PLCProc)_PLC_ANY_Finalize; plc->sf.c_new = (PLCProc)_PLC_ANY_Init; plc->obj_set = False; } /* these *must* be set when the plc->sf.c_new() function is called */ plc->init = (PLCProc)NULL; plc->destructor = (PLCProc)NULL; plc->initialized = False; /***** * These must be set by the caller, and can be set by the * init method (which happens to be the case for images). *****/ plc->obj_funcs[0] = (PLCProc)NULL; plc->obj_funcs[1] = (PLCProc)NULL; plc->obj_funcs[2] = (PLCProc)NULL; plc->curr_obj_func = 0; plc->obj_funcs_complete = False; plc->next_plc = NULL; plc->prev_plc = NULL; plc->self = plc; /* insert it */ _PLCInsert(plc); return(plc); } /***** * Name: _PLCInsert * Return Type: void * Description: insert a PLC in the PLC ringbuffer * In: * plc: current PLC * Returns: * nothing. *****/ static void _PLCInsert(PLC *plc) { PLC *tmp; XmHTMLWidget html = plc->object->plc_any.owner; _XmHTMLDebug(14, ("plc.c: _XmHTMLPLCInsert for %s\n", plc->url)); /* first element, let it point to itself */ if(html->html.plc_buffer == NULL) { plc->next_plc = plc->prev_plc = plc; html->html.plc_buffer = plc; html->html.num_plcs++; return; } /* * We already have elements in the plc buffer. * The new plc is inserted as the next element of the current PLC so it * will get activated immediatly. */ tmp = html->html.plc_buffer->next_plc; tmp->prev_plc = plc; plc->next_plc = tmp; plc->prev_plc = html->html.plc_buffer; html->html.plc_buffer->next_plc = plc; /* keep up running PLC count as well */ html->html.num_plcs++; } /***** * Name: _PLCRemove * Return Type: void * Description: removes a PLC from the PLC ringbuffer and calls the object * destructor method. * In: * plc: current PLC * Returns: * nothing. *****/ static void _PLCRemove(PLC *plc) { PLC *next, *prev; XmHTMLWidget html = plc->object->plc_any.owner; _XmHTMLDebug(14, ("plc.c: _PLCRemove for %s\n", plc->url)); /* call finalize method if this plc was ended prematurely */ if(plc->obj_funcs_complete == False) plc->finalize(plc->self); /***** * call the end_data() function to signal the user that this PLC is * about to be destroyed. *****/ _PLCEndData(plc->self); /* call destructor method */ plc->destructor(plc->self); /* now remove it */ next = plc->next_plc; prev = plc->prev_plc; /* this is the last PLC in the plc ringbuffer */ if(next == plc->self || prev == plc->self) { /* kill the main plc cycler */ html->html.plc_buffer = NULL; _XmHTMLKillPLCCycler(html); } else { next->prev_plc = prev; prev->next_plc = next; /* if this is the current plc, advance the ring buffer */ if(html->html.plc_buffer == plc->self) html->html.plc_buffer = next; } /***** * If no more PLC's are left in the buffer, call the end_data() function * to signal the user that we are done loading progressively. *****/ if(html->html.plc_buffer == NULL || html->html.num_plcs == 1) { if(plc->sf.end_data != NULL) plc->sf.end_data(NULL, NULL, XmPLC_FINISHED, True); } /* and destroy the PLC itself */ free(plc->url); free(plc->object); /* all object data */ free(plc->buffer); /* current data buffer */ free(plc->input_buffer); /* current input buffer */ free(plc); plc = NULL; /* keep up running PLC count as well */ if(html->html.num_plcs) html->html.num_plcs--; if(html->html.num_plcs == 0 && html->html.plc_buffer != NULL) _XmHTMLWarning(__WFUNC__(html, "_PLCRemove"), "Internal PLC Error: ringbuffer != NULL but num_plcs == 0."); } #ifndef PLC_WORKPROCS /***** * Name: _PLCRecomputeDelays * Return Type: void * Description: computes a new PLC polling interval. * In: * html: XmXmHTMLWidget id; * Returns: * nothing, but the PLC polling interval is updated upon return. * Note1: * Adjust delay using connection effectiveness (pload below). * We use a simple equation: x = -y + 50, where y is the connection * effectiveness and x is the required delay adjustment. This is a simple * ramp with start point at (50,0) and ending point at (-50,100), which * cuts the current delay in two with a pload of 100% (all PLC's active) * and doubles it with a pload of 0% (all PLC's suspended). * Note2: * This function is only called when PLC cycling is done using timeouts. *****/ static void _PLCRecomputeDelays(XmHTMLWidget html) { int delay, min_delay, max_delay, nplcs, i, nactive, pload, pinc; PLC *plc; if((nplcs = html->html.num_plcs) == 0) { html->html.plc_delay = html->html.plc_def_delay; return; } delay = html->html.plc_delay; min_delay = html->html.plc_min_delay; max_delay = html->html.plc_max_delay; plc = html->html.plc_buffer; /* make a guess at the effectiveness of the user's connection */ for(i = 0, nactive = 0; i < nplcs; i++, plc = plc->next_plc) if(plc->plc_status == PLC_ACTIVE) nactive++; /***** * compute new polling interval using the equation mentioned above. *****/ pload = (nactive/(float)nplcs)*100; pinc = delay * (-pload+50)/100; delay += pinc; if(delay < min_delay) delay = min_delay; if(delay > max_delay) delay = max_delay; html->html.plc_delay = delay; } #endif /* !PLC_WORKPROCS */ /***** * Name: _PLCRun * Return Type: void * Description: activate a PLC * In: * plc: current PLC * Returns: * nothing. * Note: * This is the actual PLC cycler. It will activate routines as necessary. *****/ static void _PLCRun(PLC *plc) { XmHTMLWidget html; _XmHTMLDebug(14, ("plc.c: _PLCRun for %s\n", plc->url)); /* see if the object has been set */ if(plc->obj_set == False) { plc->sf.c_new(plc->self); return; } /* check if we have been suspended in between calls */ html = plc->object->plc_any.owner; if(html->html.plc_suspended) { _XmHTMLDebug(14, ("plc.c: _PLCRun, plc suspension, returning.\n")); return; } /* see if we have been initialized */ if(plc->initialized == False) { /* call object initializer */ plc->init(plc->self); return; } /* we have been initialized, call the current obj function */ plc->obj_funcs[plc->curr_obj_func](plc->self); /***** * If the plc_status is PLC_ACTIVE or PLC_COMPLETE when the above * function returns, call the object transfer function as well. *****/ if(plc->plc_status == PLC_ACTIVE || plc->plc_status == PLC_COMPLETE) plc->transfer(plc->self); /***** * if the object functions are finished, call the finalize method * as well. *****/ if(plc->obj_funcs_complete == True) { plc->finalize(plc->self); plc->plc_status = PLC_COMPLETE; } } #ifdef PLC_WORKPROCS /***** * Name: _PLCSubCycler * Return Type: Boolean * Description: PLC cycling engine when work procedures are to be used. * In: * call_data: data registered for this function. * Returns: * False when this function should be called again by X (as long as * there are PLC's in the list), and True when processing is complete. * This function is actually of type XtWorkProc. *****/ #ifdef WITH_GTK static gint _PLCSubCycler(gpointer call_data) #else static Boolean _PLCSubCycler(XtPointer call_data) #endif { XmHTMLWidget html = XmHTML (call_data); PLC *plc = html->html.plc_buffer; /* return if we haven't got any PLC's installed */ if(plc == NULL || html->html.plc_suspended) return TIdleRemove; _XmHTMLDebug(14, ("plc.c: _PLCSubCycler for %s\n", plc->url)); html->html.plc_proc_id = TNullTimeout; /***** * As this is called thru a work procedure, we don't have to concern * ourselves with the speedups in the timeout variant. It will be called * *much* sooner. *****/ switch(plc->plc_status) { case PLC_SUSPEND: /***** * _PLCDataRequest failed on previous call, skip it this time * to give the connection a bit more time and set plc_status so * it will get activated the next time this plc is called. *****/ plc->plc_status = PLC_ACTIVE; /* move to next plc */ html->html.plc_buffer = plc->next_plc; break; case PLC_ACTIVE: _PLCRun(plc->self); /* move to next plc */ html->html.plc_buffer = plc->next_plc; break; case PLC_COMPLETE: case PLC_ABORT: /* _PLCRemove will advance to the next PLC by itself */ _PLCRemove(plc->self); break; default: _XmHTMLWarning(__WFUNC__(html, "_PLCSubCycler"), "Unknown PLC status %d", plc->plc_status); /* kill it next time it's called */ plc->plc_status = PLC_ABORT; /* move to next plc */ html->html.plc_buffer = plc->next_plc; break; } /* continue processing if we have any plc's left */ if(html->html.plc_buffer != NULL) return TIdleKeep; return TIdleRemove; } #endif /* PLC_WORKPROCS */ /***** * Name: _XmHTMLPLCCycler * Return Type: void * Description: main PLC cycling engine. * In: * call_data: data registed for this function. * proc_id: unused; * Returns: * nothing. * Note: * When PLC_WORKPROCS was defined during compilation, this routine starts * up a subcycler which will do the real PLC processing using work procedures. * The default is to use a dynamic polling interval using timeouts (causing * this function to become a XtTimerCallbackProc). * This function is called only *once* from within the main XmHTML code: * when new text has been set into the widget. *****/ /*ARGSUSED*/ #ifdef WITH_GTK gint _XmHTMLPLCCycler(gpointer call_data) #else void _XmHTMLPLCCycler(TPointer call_data, TIntervalId *proc_id) #endif { XmHTMLWidget html = XmHTML (call_data); PLC *plc; int nplcs, i; /***** * Return if we haven't got any PLC's installed or if progressive * image loading has been suspended. *****/ if((plc = html->html.plc_buffer) == NULL || html->html.plc_suspended) #ifdef WITH_MOTIF return; #else return TIdleRemove; #endif /* make sure we aren't grabbing anything (total server lockup!) */ Toolkit_Pointer_Ungrab (XtDisplay((Widget)html), Toolkit_CurrentTime); nplcs = html->html.num_plcs; #ifdef PLC_WORKPROCS _XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCycler, starting subCycler\n")); # ifdef WITH_GTK html->html.plc_proc_id = gtk_idle_add(_PLCSubCycler, html); # else html->html.plc_proc_id = (XtWorkProcId) XtAppAddWorkProc(XtWidgetToApplicationContext((Widget)html), (XtWorkProc)_PLCSubCycler, (XtPointer)html); # endif #else _XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCycler for %s\n", plc->url)); html->html.plc_proc_id = TNullTimeout; switch(plc->plc_status) { case PLC_SUSPEND: /***** * _PLCDataRequest failed on previous call, skip it this time * to give the connection a bit more time and set plc_status so * it will get activated the next time this plc is called. *****/ plc->plc_status = PLC_ACTIVE; /* move to next plc */ html->html.plc_buffer = plc->next_plc; /***** * To prevent all to much slowdown (this routine is called by * a timer), we cycle thru the full list of installed plc's to see * if we have an active one. If we do, we fall thru this case and * activate it. While checking, we also activate any suspended plc's * so they will get called the next time this routine is activated. *****/ for(i = 0; i < nplcs - 1; i++) { plc = html->html.plc_buffer; if(plc->plc_status == PLC_ACTIVE) break; else /* activate this plc */ plc->plc_status = PLC_ACTIVE; html->html.plc_buffer = plc->next_plc; } /* all plc's suspended, break out */ if(plc->plc_status != PLC_ACTIVE) break; /* we have found an active plc, fall through */ case PLC_ACTIVE: _PLCRun(plc->self); /***** * Speedup: if this plc has finished, remove it right away, * there's no need to do this on the next call to this routine as * the finalizing and removal of this plc does not involve any * data requests. *****/ if(plc->plc_status == PLC_COMPLETE || plc->plc_status == PLC_ABORT) _PLCRemove(plc->self); else /* move to next plc */ html->html.plc_buffer = plc->next_plc; break; case PLC_COMPLETE: case PLC_ABORT: /* _PLCRemove will advance to the next PLC by itself */ _PLCRemove(plc->self); break; default: _XmHTMLWarning(__WFUNC__(html, "_XmHTMLPLCCycler"), "Unknown PLC status %d", plc->plc_status); /* kill it next time it's called */ plc->plc_status = PLC_ABORT; /* move to next plc */ html->html.plc_buffer = plc->next_plc; break; } /* adjust PLC intervals */ _PLCRecomputeDelays(html); /* and re-instate ourselves if we have any plc's left */ if(html->html.plc_buffer != NULL) #ifdef WITH_GTK return TIdleKeep; else return TIdleRemove; #else XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)html), html->html.plc_delay, (XtTimerCallbackProc)_XmHTMLPLCCycler, (XtPointer)html); /* else do nothing, as Xt removes the timeout after invocation */ #endif #endif /* !PLC_WORKPROCS */ } /***** * Name: _XmHTMLKillPLCCycler * Return Type: void * Description: kills all outstanding PLC procedures. * In: * html: XmXmHTMLWidget id for which to kill all PLC's * Returns: * nothing, but the list of PLC's of the given XmXmHTMLWidget has been cleared * upon return. * Note: * This function is called when the current document is discarded (by * loading a new document or destroying the widget). *****/ void _XmHTMLKillPLCCycler(XmHTMLWidget html) { PLC *plc = html->html.plc_buffer; _XmHTMLDebug(14, ("plc.c: _XmHTMLKillPLCCycler\n")); /* kill any outstanding plc cycler timeouts */ if(html->html.plc_proc_id != TNullTimeout) { html->html.plc_suspended = True; #ifdef PLC_WORKPROCS # ifdef WITH_GTK gtk_idle_remove(html->html.plc_proc_id); # else XtRemoveWorkProc((XtWorkProcId)html->html.plc_proc_id); # endif #else # ifdef WITH_GTK gtk_timeout_remove(html->html.plc_proc_id); # else XtRemoveTimeOut(html->html.plc_proc_id); # endif #endif /* PLC_WORKPROCS */ html->html.plc_proc_id = TNullTimeout; } /* reset PLC timeout to the default value */ html->html.plc_delay = html->html.plc_def_delay; if(plc == NULL) { /* restore defaults */ html->html.num_plcs = 0; html->html.plc_suspended = True; html->html.plc_delay = html->html.plc_def_delay; return; } /* now remove all outstanding plc's */ while(html->html.plc_buffer != NULL) { /* abort all outstanding PLC's */ plc = html->html.plc_buffer; _XmHTMLDebug(14, ("plc.c: _XmHTMLKillPLCCycler, aborting %s\n", plc->url)); plc->plc_status = PLC_ABORT; _PLCRemove(plc); } /* restore defaults */ html->html.num_plcs = 0; html->html.plc_suspended = True; html->html.plc_delay = html->html.plc_def_delay; /* free the plc_gc if it's still here */ if(html->html.plc_gc) { #ifdef WITH_GTK gdk_gc_destroy(html->html.plc_gc); #else XFreeGC(XtDisplay((Widget)html), html->html.plc_gc); #endif html->html.plc_gc = NULL; } } /***** * Object-specific routines. * There are three sets of them, each with three functions: * _PLC_IMG class: image PLC functions; * _PLC_DOC class: document PLC functions; * _PLC_ANY class: unknown PLC functions; * The three functions are: * _Init: function that performs initialization of the object for which * a PLC is to be used. This may involve getting more * object-specific data. When this function finishes, it should * set the obj_set field to True. * _Transfer: function that needs to perform *intermediate* transfer of * object-specific data to the final destination; * _Finalize: function that needs to perform *final* transfer of * object-specific data to the final destination; *****/ /*************** ***** Image PLC object and supporting functions ***************/ /***** * Name: _PLC_IMG_Init * Return Type: void * Description: * In: * plc: current PLC * Returns: * nothing. Upon return, the object field of the current PLC is updated to * reflect the type of image that is to be loaded (includes image-specific * object functions). *****/ static void _PLC_IMG_Init(PLC *plc) { Byte obj_type = plcAnyImage, img_type = IMAGE_UNKNOWN; Byte magic[10]; static Byte png_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10}; _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Init for %s\n", plc->url)); /* get first 10 bytes to determine the image type */ plc->min_in = 10; plc->max_in = PLC_MAX_BUFFER_SIZE; /***** * We *need* to know the image type before we can start doing * anything. *****/ if(!(_PLCDataRequest(plc))) return; memcpy(magic, plc->buffer, 10); /* check image types we known of. Do most (?) logical order */ if(!(strncmp((char*)magic, "GIF87a", 6)) || !(strncmp((char*)magic, "GIF89a", 6))) { obj_type = plcGIF; img_type = IMAGE_GIF; plc->init = _PLC_GIF_Init; plc->destructor = _PLC_GIF_Destructor; plc->obj_funcs[0] = _PLC_GIF_ScanlineProc; plc->object->plc_gif_image.info = (XmImageInfo*)plc->priv_data; } /* compatible gif */ else if(!(strncmp((char*)magic, "GZF87a", 6)) || !(strncmp((char*)magic, "GZF89a", 6))) { #if defined (HAVE_LIBPNG) || defined (HAVE_LIBZ) obj_type = plcGZF; img_type = IMAGE_GZF; plc->init = _PLC_GZF_Init; plc->destructor = _PLC_GZF_Destructor; plc->obj_funcs[0] = _PLC_GZF_ScanlineProc; plc->object->plc_gzf_image.info = (XmImageInfo*)plc->priv_data; #endif /* HAVE_LIBPNG || HAVE_LIBZ */ } else if(magic[0] == 0xff && magic[1] == 0xd8 && magic[2] == 0xff) { #ifdef HAVE_LIBJPEG obj_type = plcJPEG; img_type = IMAGE_JPEG; plc->init = _PLC_JPEG_Init; plc->destructor = _PLC_JPEG_Destructor; plc->obj_funcs[0] = _PLC_JPEG_ScanlineProc; plc->object->plc_jpeg_image.info = (XmImageInfo*)plc->priv_data; #endif /* HAVE_LIBJPEG */ } else if(!(memcmp(magic, png_magic, 8))) { #ifdef HAVE_LIBPNG /*** internal configuration define, don't define this yourself ***/ #ifdef PLC_PNG obj_type = plcPNG; img_type = IMAGE_PNG; plc->init = _PLC_PNG_Init; plc->destructor = _PLC_PNG_Destructor; plc->obj_funcs[0] = _PLC_PNG_ScanlineProc; plc->object->plc_png_image.info = (XmImageInfo*)plc->priv_data; #endif /* PLC_PNG */ #endif /* HAVE_LIBPNG */ } else if(!(strncmp((char*)magic, "/* XPM */", 9))) { obj_type = plcXPM; img_type = IMAGE_XPM; plc->init = _PLC_XPM_Init; plc->destructor = _PLC_XPM_Destructor; plc->obj_funcs[0] = _PLC_XPM_ScanlineProc; plc->object->plc_xpm_image.info = (XmImageInfo*)plc->priv_data; } else if(!(strncmp((char*)magic, "#define", 7)) || (magic[0] == '/' && magic[1] == '*')) { obj_type = plcXBM; img_type = IMAGE_XBM; plc->init = _PLC_XBM_Init; plc->destructor = _PLC_XBM_Destructor; plc->obj_funcs[0] = _PLC_XBM_ScanlineProc; plc->object->plc_xbm_image.info = (XmImageInfo*)plc->priv_data; } /* check if we got an image */ if(obj_type == plcAnyImage) { _XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLC_IMG_Init"), "%s: unsupported by PLC/unknown image type!", plc->url); plc->plc_status = PLC_ABORT; return; } plc->object->plc_any.type = obj_type; plc->object->plc_any_image.info->type = img_type; plc->obj_set = True; return; } /***** * Name: _PLC_IMG_Transfer * Return Type: void * Description: intermediate image transfer function * In: * plc: current PLC. * Returns: * nothing. * Note: * When this routine is called for the first time, it initializes all common * image fields (XImage, pixmap, color arrays, clipmask, etc...). * * The actual image composition is split in six parts: * 1. color counting; the numbers of colors used by the new chunk of data is * counted, and the RGB arrays for these colors are filled; * 2. data pixelization; the new chunk of raw data is mapped to the pixel * values assigned in the previous step; * 3. color allocation; if new colors are to be allocated that's done now; * 4. XImage updating; scanlines represented by the new chunk of data are * added to the existing scanlines already present in the XImage; * 5. Pixmap updating; the newly added scanlines are copied into the * destination drawable (a Pixmap); * 6. Display updating: the updated portion of the pixmap is copied to screen. *****/ static void _PLC_IMG_Transfer(PLC *plc) { PLCImage *any_image = &(plc->object->plc_any_image); XmHTMLImage *image = any_image->image; XmImageInfo *info = any_image->info; XmHTMLWidget html = any_image->owner; int width, height, lo, hi; int col_cnt; /* no of colors in image, accumulated */ int npixels; /* no of allocated pixel values, accumulated */ #ifdef WITH_MOTIF Boolean pixels[MAX_IMAGE_COLORS]; #else int pixels [MAX_IMAGE_COLORS]; #endif register int i; Byte *ptr, *data; int ypos, nlines; #ifndef WITH_GTK Display *dpy = XtDisplay((Widget)html); #endif _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer for %s\n", plc->url)); if(info == NULL) { _XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"), "%s: Internal PLC error\n No XmImageInfo structure bound!", plc->url); plc->plc_status = PLC_ABORT; return; } /* don't do a thing if we haven't received any new data */ if(any_image->prev_pos == any_image->data_pos) { _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer end, no new data to " "process\n")); return; } /* no HTML image stored yet, go pick it up */ if(image == NULL) { TWindow win; Boolean need_redisplay = False; for(image = html->html.images; image != NULL && image->html_image != info; image = image->next); if(image == NULL) { _XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"), "%s: Internal PLC error\n No image registered!", plc->url); plc->plc_status = PLC_ABORT; return; } image->options |= IMG_PROGRESSIVE; any_image->image = image; /* if we are realized we have a window */ if (Toolkit_Is_Realized (html)) win = Toolkit_Widget_Window (html->html.work_area); else win = Toolkit_Default_Root_Window (dpy); /* store original image dimensions */ info->swidth = any_image->width; info->sheight = any_image->height; /***** * See if any dimensions were specified on the element * or if we may not perform scaling. *****/ if(!ImageHasDimensions(image) || !ImageInfoScale(info)) { /* store used image dimensions */ info->width = image->width = any_image->width; info->height = image->height = any_image->height; /***** * keep requested dimensions if we have them. Set to real * image dimensions otherwise. *****/ if(!ImageHasDimensions(image)) { image->swidth = image->width; image->sheight = image->height; } } /* we have dimensions and we may scale */ else { /* store used image dimensions */ info->width = image->width = image->swidth; info->height = image->height = image->sheight; /* check see if we really need to scale */ if(image->swidth != any_image->width || image->sheight != any_image->height) { /* we need to scale */ any_image->is_scaled = True; any_image->scaled_data = (Byte*)malloc(image->swidth * image->sheight); } } /* * Update imageWord dimensions as well. * owner can be NULL if this is the body image. */ if(image->owner != NULL && image->owner->words != NULL && image->owner->words[0].image == image) { /* * If the owning word had it's dimensions wrong (not set before) * we need to recompute the screen layout. */ if(image->owner->words[0].width != image->width || image->owner->words[0].height != image->height) { /* store new image dimensions */ image->owner->words[0].width = image->width; image->owner->words[0].height = image->height; /* redo screen layout as it will be incorrectly by now */ need_redisplay = True; } } /* allocate pixmaps */ #ifdef WITH_GTK if ((any_image->pixmap = gdk_pixmap_new(win, image->width, image->height, html->html.xcc->visual->depth)) == NULL) #else if((any_image->pixmap = XCreatePixmap(dpy, win, image->width, image->height, html->html.xcc->visualInfo->depth)) == None) #endif { _XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"), "%s: failed to create pixmap.", plc->url); plc->plc_status = PLC_ABORT; return; } /***** * pre-tile the pixmap with the body image (if this isn't the body * image of course) *****/ if(html->html.body_image && !ImageIsBackground(html->html.body_image) && !ImageDelayedCreation(html->html.body_image) && BodyImageLoaded(html->html.body_image->html_image)) { #ifndef WITH_GTK XGCValues values; unsigned long valuemask; #endif int tile_width, tile_height, x_dist, y_dist, ntiles_x, ntiles_y; int x_offset, y_offset, tsx, tsy, xs, ys; tile_width = html->html.body_image->width; tile_height = html->html.body_image->height; /* compute correct image offsets */ xs = image->owner->words[0].x - html->html.scroll_x; ys = image->owner->words[0].y - html->html.scroll_y; x_dist = html->html.scroll_x + xs; y_dist = html->html.scroll_y + ys; ntiles_x = (int)(x_dist/tile_width); ntiles_y = (int)(y_dist/tile_height); x_offset = x_dist - ntiles_x * tile_width; y_offset = y_dist - ntiles_y * tile_height; tsx = xs - x_offset; tsy = ys - y_offset; #ifdef WITH_GTK gdk_gc_set_fill(html->html.bg_gc, GDK_TILED); gdk_gc_set_tile(html->html.bg_gc, html->html.body_image->pixmap); gdk_gc_set_ts_origin(html->html.bg_gc, tsx, tsy); gdk_draw_rectangle(any_image->pixmap, html->html.bg_gc, TRUE, 0, 0, image->width, image->height); #else valuemask = GCTile|GCFillStyle|GCTileStipXOrigin|GCTileStipYOrigin; values.fill_style = FillTiled; values.tile = html->html.body_image->pixmap; values.ts_x_origin = tsx; values.ts_y_origin = tsy; XChangeGC(dpy, html->html.bg_gc, valuemask, &values); /* a plain fillrect will redraw the background portion */ XFillRectangle(dpy, any_image->pixmap, html->html.bg_gc, 0, 0, image->width, image->height); #endif } else { #ifdef WITH_GTK GdkColor pix; pix.pixel = html->html.body_bg; gdk_gc_set_foreground(html->html.gc, &pix); gdk_draw_rectangle(any_image->pixmap, html->html.gc, TRUE, 0, 0, image->width, image->height); pix.pixel = html->html.body_fg; gdk_gc_set_foreground(html->html.gc, &pix); #else /* fill it with the current background for now */ XSetForeground(dpy, html->html.gc, html->html.body_bg); XFillRectangle(dpy, any_image->pixmap, html->html.gc, 0, 0, image->width, image->height); XSetForeground(dpy, html->html.gc, html->html.body_fg); #endif } image->pixmap = any_image->pixmap; /* allocate fully transparent clipmask */ if(any_image->transparency == XmIMAGE_TRANSPARENCY_BG) { int clipsize = 0; info->options |= XmIMAGE_CLIPMASK; /* compute amount of data required for this clipmask */ i = image->width; /* make it byte-aligned */ while((i % 8)) i++; /* this many bytes on a row */ i /= 8; /* size of clipmask */ clipsize = i * image->height; /* raw clipmask data */ any_image->clip_data = (Byte*)calloc(clipsize, sizeof(Byte)); /* fully transparent clipmask */ #ifdef WITH_GTK { GdkColor fg, bg; /* XXX: is this the right thing to do? Why is it done like that? */ fg.pixel = 1; bg.pixel = 0; any_image->clipmask = gdk_pixmap_create_from_data(win, any_image->clip_data, image->width, image->height, 1, &fg, &bg); } #else any_image->clipmask = XCreatePixmapFromBitmapData(dpy, win, (char*)any_image->clip_data, image->width, image->height, 1, 0, 1); #endif } /* destination clipmask */ image->clip = any_image->clipmask; /* temporary clipmask data */ info->clip = any_image->clip_data; /* allocate image RGB values */ info->options |= XmIMAGE_RGB_SINGLE; info->reds = (Dimension*)calloc(3 * any_image->cmapsize, sizeof(Dimension)); info->greens = info->reds + any_image->cmapsize; info->blues = info->greens + any_image->cmapsize; /* reset used colors array */ for(i = 0; i < MAX_IMAGE_COLORS; i++) { any_image->used[i] = 0; any_image->xcolors[i] = 0L; } any_image->nused = 1; /* update all copies of this image */ if(image->child) _XmHTMLImageUpdateChilds(image); /* create a working XImage for this plc */ if(any_image->ximage == NULL) { any_image->ximage = _XmHTMLCreateXImage(html, html->html.xcc, image->width, image->height, plc->url); if(any_image->ximage == NULL) { plc->plc_status = PLC_ABORT; return; } } /* redo screen layout as it will be incorrect by now */ if(need_redisplay) XmHTMLRedisplay((TWidget)html); } /***** * Always get used image dimensions. *****/ width = image->width; height = image->height; /***** * Step 1: color usage *****/ /* last known processed data */ ptr = any_image->data + any_image->prev_pos; /* last known index of allocated colors */ col_cnt = any_image->nused; /* store pixel indices */ for(i = any_image->prev_pos; i < any_image->data_pos; i++, ptr++) { if(any_image->used[(int)*ptr] == 0) { any_image->used[(int)*ptr] = col_cnt; col_cnt++; } } col_cnt--; /***** * Now go and fill the RGB arrays. Only do this if the new chunk of data * contains new colors. *****/ if(any_image->nused != col_cnt+1) { memset(pixels, 0, MAX_IMAGE_COLORS*sizeof(Boolean)); for(i = 0; i < MAX_IMAGE_COLORS; i++) { int indx; if(any_image->used[i] != 0) { indx = any_image->used[i] - 1; pixels[indx] = True; info->reds[indx] = any_image->cmap[i].red; info->greens[indx] = any_image->cmap[i].green; info->blues[indx] = any_image->cmap[i].blue; } } } /***** * Step 2a: image scaling (optional) *****/ if(any_image->is_scaled) { Byte *img_data, *ilptr, *ipptr, *elptr, *epptr; int ix, iy, ex, ey, src_w, src_h; /* get ptr to scaled image data */ data = any_image->scaled_data; img_data = any_image->data; /* get real image dimensions */ src_w = any_image->width; src_h = any_image->height; /* initialize scaling */ elptr = epptr = data; /* starting scanline index */ ey = any_image->sc_start/width; /* scaling is done from top to bottom, left to right */ for(; ey < height; ey++, elptr += width) { /* vertical pixel skip */ iy = (src_h * ey) / height; epptr = elptr; ilptr = img_data + (iy * src_w); /* don't overrun */ if(iy*src_w > any_image->data_pos) break; for(ex = 0; ex < width; ex++, epptr++) { /* horizontal pixel skip */ ix = (src_w * ex) / width; ipptr = ilptr + ix; *epptr = *ipptr; } } /* update scaled data end position */ any_image->sc_end = ey*width; } /***** * Step 2b: clipmask creation (optional) * We just redo the entire image. *****/ if(any_image->transparency == XmIMAGE_TRANSPARENCY_BG) { int j, bcnt; Byte *cptr = any_image->clip_data; TWindow win; if(any_image->is_scaled) { ptr = any_image->scaled_data; /* last known processed scanline */ lo = any_image->sc_start/width; /* maximum scanline on this pass */ hi = any_image->sc_end/width; } else { ptr = any_image->data; hi = (any_image->data_pos)/width; lo = (any_image->prev_pos)/width; } /* pick up were we left off. Saves prev_pos conditionals */ for(i = 0; i < lo; i++) { for(j = 0, bcnt = 0; j < width; j++) { if((bcnt % 8) == 7 || j == (width-1)) cptr++; bcnt++; ptr++; } } /* process next amount of data */ for(i = lo; i < hi; i++) { for(j = 0, bcnt = 0; j < width; j ++) { if(*ptr != any_image->bg_pixel) *cptr += bitmap_bits[(bcnt % 8)]; if((bcnt % 8) == 7 || j == (width-1)) cptr++; bcnt++; ptr++; } } /* destroy existing bitmap */ if(any_image->clipmask != TNone) FreePixmap (dpy, any_image->clipmask); /* if we are realized we have a window */ if (Toolkit_Is_Realized (html)) win = Toolkit_Widget_Window (html->html.work_area); else win = Toolkit_Default_Root_Window (dpy); /* create new one */ #ifdef WITH_GTK { GdkColor fg, bg; /* XXX: is this the right thing to do? Why is it done like that? */ fg.pixel = 1; bg.pixel = 0; any_image->clipmask = gdk_pixmap_create_from_data(win, any_image->clip_data, width, height, 1, &fg, &bg); } #else any_image->clipmask = XCreatePixmapFromBitmapData(dpy, win, (char*)any_image->clip_data, width, height, 1, 0, 1); #endif /* save it */ image->clip = any_image->clipmask; /* update child copies */ if(image->child) _XmHTMLImageUpdateChilds(image); } /***** * Step 3: image pixelization. * Replaces each pixel value in the decoded image data with the * indices in our own pixel array. * Needs to be done after clipmask creation as this modifies the image * data. *****/ /* last known processed data */ ptr = any_image->data + any_image->prev_pos; for(i = any_image->prev_pos; i < any_image->data_pos; i++) { *ptr = (Byte)(any_image->used[(int)*ptr] - 1); ptr++; } /***** * Step 4: color allocation. * Only allocate colors if the new chunk of data contains colors that * haven't been allocated yet. *****/ if(any_image->nused != col_cnt+1) { _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, allocating %i additional " "colors (%i total)\n", col_cnt - any_image->nused + 1, col_cnt)); npixels = image->npixels; XCCGetPixelsIncremental(html->html.xcc, info->reds, info->greens, info->blues, any_image->cmapsize, pixels, any_image->xcolors, &npixels); _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, after allocation, npixels " ": %i\n", npixels)); /* update used color count */ any_image->nused = col_cnt+1; image->npixels = npixels; } /***** * Step 5: XImage updating. * We have our colors, now do the ximage. If the image is being scaled, * use the pre-scaled image data, else use the real image data. *****/ if(any_image->is_scaled) { data = any_image->scaled_data + any_image->sc_start; lo = any_image->sc_start; hi = any_image->sc_end; } else { data = any_image->data + any_image->prev_pos; lo = any_image->prev_pos; hi = any_image->data_pos; } _XmHTMLFillXImage(html, any_image->ximage, html->html.xcc, data, any_image->xcolors, &lo, &hi); /***** * XImage updated, make all positions aligned on completed scanline * boundaries. *****/ any_image->prev_pos = (any_image->data_pos/any_image->width) * any_image->width; /* current scanline */ ypos = lo/width; /* no of scanlines added on this pass */ nlines = (hi - lo)/width; /* check if we aren't exceeding height of the image */ my_assert((ypos + nlines <= height)); /***** * Step 6: destination updating. * XImage data processed, copy newly added scanlines to the destination * pixmap. *****/ _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, updating image between " "scanline %i to %i\n", ypos, ypos + nlines)); /* update pixmap */ #ifdef WITH_GTK gdk_draw_image(any_image->pixmap, html->html.plc_gc, any_image->ximage, 0, ypos, 0, ypos, width, nlines); #else XPutImage(dpy, any_image->pixmap, html->html.plc_gc, any_image->ximage, 0, ypos, 0, ypos, width, nlines); #endif /***** * Step 7: display update. The first call updates the master image and * the second call updates any copies of this image. * Safety check: only do it when the image has an owner (e.i., it isn't * the background image). *****/ if(image->owner) { _PLC_IMG_UpdateScreen(html, image, image->owner, ypos, nlines); _PLC_IMG_UpdateScreenCopies(html, image, ypos, nlines); } /* all done */ return; } /***** * Name: _PLC_IMG_Finalize * Return Type: void * Description: Image PLC final transfer function * In: * plc: current PLC * Returns: * nothing. *****/ static void _PLC_IMG_Finalize(PLC *plc) { PLCImage *any_image; XmHTMLWidget html; XmImageInfo *info; XmHTMLImage *image; /* obj_set will be false if this PLC was terminated during init phase */ if(plc == NULL || !plc->obj_set) return; any_image = &(plc->object->plc_any_image); html = any_image->owner; info = any_image->info; image = any_image->image; _XmHTMLDebug(14, ("plc.c: _PLC_IMG_Finalize for %s\n", plc->url)); /* this must *always* be destroyed */ if(any_image->ximage) #ifdef WITH_GTK gdk_image_destroy(any_image->ximage); #else XDestroyImage(any_image->ximage); #endif /* no longer need the scaled data, image has been fully processed */ if(any_image->is_scaled) { free(any_image->scaled_data); any_image->scaled_data = (Byte*)NULL; any_image->is_scaled = False; } /***** * Transfer stuff to the info structure. We *need* to test the * existence of info 'cause it might disappear when this PLC is aborted. *****/ if(info) { info->data = any_image->data; /* decoded image data */ info->clip = any_image->clip_data; /* raw clipmask data */ info->bg = any_image->bg_pixel; /* background pixel index */ info->colorspace = any_image->colorclass; /* image colorclass */ info->transparency = any_image->transparency;/* image transparency */ info->depth = any_image->depth; /* image depth */ info->ncolors = any_image->nused-1; /* no of colors in image */ info->scolors = any_image->ncolors; /* original no of cols */ info->width = any_image->width; /* reset to real width */ info->height = any_image->height; /* reset to real height */ /* this image is no longer being loaded progressively */ info->options &= ~XmIMAGE_PROGRESSIVE; /***** * Adjust image RGB components to only contain the number of colors * used in this image. * Only do it when we have allocated any colors for this image (we * didn't allocate colors if this PLC was aborted prematurely). *****/ if(info->ncolors && info->reds != NULL && info->ncolors != any_image->cmapsize) { /* temporary storage */ Dimension *reds, *greens, *blues; /* save old RGB arrays */ reds = info->reds; greens = info->greens; blues = info->blues; /* allocate new RGB arrays */ info->reds = (Dimension*)calloc(3 * info->ncolors, sizeof(Dimension)); info->greens = info->reds + info->ncolors; info->blues = info->greens + info->ncolors; /* copy old RGB arrays */ info->reds = memcpy(info->reds, reds, info->ncolors*sizeof(Dimension)); info->greens = memcpy(info->greens, greens, info->ncolors*sizeof(Dimension)); info->blues = memcpy(info->blues, blues, info->ncolors*sizeof(Dimension)); /* free old RGB arrays */ free(reds); /* update this as well */ info->scolors = info->ncolors; } } /* update all copies of this image */ if(image != NULL) { /* no longer progressive */ image->options &= ~IMG_PROGRESSIVE; if(image->child != NULL) _XmHTMLImageUpdateChilds(image); } /* free remaining PLC resources */ if(any_image->cmap) free(any_image->cmap); if(any_image->bg_cmap) free(any_image->bg_cmap); if(any_image->buffer) free(any_image->buffer); /***** * last and final check: if this is the body image, clear the display * area so it will get redrawn properly. Don't do it when we haven't * got a GC: we do not yet have a window then. *****/ if(image && ImageIsBackground(image) && html->html.gc != NULL) { Toolkit_Clear_Area (XtDisplay(html->html.work_area), Toolkit_Widget_Window (html->html.work_area), 0, 0, Toolkit_Widget_Dim (html).width, Toolkit_Widget_Dim (html).height, True); } /* make sure we are updated */ #ifdef WITH_GTK gtk_widget_draw(GTK_WIDGET (html), NULL); /* XXX: I don't know if this is what we want to do */ #else XmUpdateDisplay((Widget)html); #endif return; } /***** * Name: _PLC_IMG_UpdateScreen * Return Type: void * Description: Image PLC image->screen transfer function * In: * html: XmXmHTMLWidget id; * image: image data; * elePtr: image owner; * src_y: starting scanline index; * height: number of scanlines to render; * Returns: * nothing. *****/ static void _PLC_IMG_UpdateScreen(XmHTMLWidget html, XmHTMLImage *image, XmHTMLObjectTableElement elePtr, int src_y, Dimension height) { int tmp = image->height; /* set bogus image height */ image->height = height; /* paint it */ _XmHTMLDrawImage(html, elePtr, src_y, False); /* restore real image height */ image->height = tmp; } /***** * Name: _PLC_IMG_UpdateScreenCopies * Return Type: void * Description: updates the screen for each copy of the given image. * In: * html: XmXmHTMLWidget id; * image: parent image; * src_y: starting scanline index; * height: number of scanlines to render; * Returns: * nothing. *****/ static void _PLC_IMG_UpdateScreenCopies(XmHTMLWidget html, XmHTMLImage *image, int src_y, Dimension height) { XmHTMLImage *tmp; /* walk the list of child images and call UpdateScreen for each copy */ for(tmp = image->child; tmp != NULL; tmp = tmp->child) { if(tmp->owner) _PLC_IMG_UpdateScreen(html, tmp, tmp->owner, src_y, height); } } /*************** ***** Document PLC object and supporting functions ***************/ static void _PLC_DOC_Init(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_DOC_Init for %s\n", plc->url)); return; } static void _PLC_DOC_Transfer(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_DOC_Transfer for %s\n", plc->url)); plc->obj_set = True; return; } static void _PLC_DOC_Finalize(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_DOC_Finalize for %s\n", plc->url)); plc->obj_set = True; return; } /*************** ***** Unknown PLC object and supporting functions ***************/ static void _PLC_ANY_Init(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_ANY_Init for %s\n", plc->url)); return; } static void _PLC_ANY_Transfer(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_ANY_Transfer for %s\n", plc->url)); return; } static void _PLC_ANY_Finalize(PLC *plc) { _XmHTMLDebug(14, ("plc.c: _PLC_ANY_Finalize for %s\n", plc->url)); return; } /******** ****** Public Functions ********/ /* suspend progressive image loading */ void XmHTMLImageProgressiveSuspend(TWidget w) { XmHTMLWidget html; PLC *plc; int i; if(w == NULL || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLImageProgressiveSuspend"); return; } html = XmHTML (w); /* nothing to suspend */ if((plc = html->html.plc_buffer) == NULL) return; /* first suspend all active PLC's. Don't mess with other PLC states. */ for(i = 0; i < html->html.num_plcs; plc = plc->next_plc, i++) { if(plc->plc_status == PLC_ACTIVE) plc->plc_status = PLC_SUSPEND; } if(html->html.plc_proc_id) { #ifdef PLC_WORKPROCS # ifdef WITH_GTK gtk_idle_remove(html->html.plc_proc_id); # else XtRemoveWorkProc((XtWorkProcId)html->html.plc_proc_id); # endif #else # ifdef WITH_GTK gtk_timeout_remove(html->html.plc_proc_id); # else XtRemoveTimeOut(html->html.plc_proc_id); # endif #endif html->html.plc_proc_id = TNullTimeout; } /* set global PLC suspension flag */ html->html.plc_suspended = True; } /* reactivate progressive image loading */ void XmHTMLImageProgressiveContinue(TWidget w) { XmHTMLWidget html; PLC *plc; int i; if(w == NULL || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLImageProgressiveContinue"); return; } html = XmHTML (w); /* nothing to do */ if((plc = html->html.plc_buffer) == NULL) return; /* first activate all suspended PLC's. Don't mess with other PLC states. */ for(i = 0; i < html->html.num_plcs; plc = plc->next_plc, i++) { if(plc->plc_status == PLC_SUSPEND) plc->plc_status = PLC_ACTIVE; } /* reactivate cycler */ html->html.plc_suspended = False; #ifdef WITH_GTK _XmHTMLPLCCycler((TPointer)html); #else _XmHTMLPLCCycler((TPointer)html, NULL); #endif } /* terminate progressive image loading */ void XmHTMLImageProgressiveKill(TWidget w) { XmHTMLWidget html; if(w == NULL || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLImageProgressiveKill"); return; } html = XmHTML (w); if(html->html.plc_buffer == NULL) return; /* kill the bastards! */ html->html.plc_suspended = True; _XmHTMLKillPLCCycler(html); }