1 /*****
2 * plc.c : XmHTML Progressive Loader Context interfacing routines.
3 *
4 * This file Version	$Revision: 1.6 $
5 *
6 * Creation date:		Thu Jun 12 16:46:34 GMT+0100 1997
7 * Last modification: 	$Date: 1998/04/27 07:02:39 $
8 * By:					$Author: newt $
9 * Current State:		$State: Exp $
10 *
11 * Author:				newt
12 *
13 * Copyright (C) 1994-1997 by Ripley Software Development
14 * All Rights Reserved
15 *
16 * This file is part of the XmHTML Widget Library.
17 *
18 * This library is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU Library General Public
20 * License as published by the Free Software Foundation; either
21 * version 2 of the License, or (at your option) any later version.
22 *
23 * This library is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26 * Library General Public License for more details.
27 *
28 * You should have received a copy of the GNU Library General Public
29 * License along with this library; if not, write to the Free
30 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 *
32 *****/
33 /*****
34 * ChangeLog
35 * $Log: plc.c,v $
36 * Revision 1.6  1998/04/27 07:02:39  newt
37 * tka stuff
38 *
39 * Revision 1.5  1998/04/04 06:28:25  newt
40 * XmHTML Beta 1.1.3
41 *
42 * Revision 1.4  1997/10/23 00:25:14  newt
43 * XmHTML Beta 1.1.0 release
44 *
45 * Revision 1.3  1997/08/31 17:37:58  newt
46 * image dimensions bugfix & XCreatePixmapFromBitmap cast
47 *
48 * Revision 1.2  1997/08/30 01:25:52  newt
49 * All progressively loaded images now share a single GC.
50 * Added support for on-the-fly scaling.
51 * The XImage code is now shared with normal image loading.
52 * Fixed transparency stuff.
53 * Screen updating now shares code from paint.c: _XmHTMLDrawImage.
54 *
55 * Revision 1.1  1997/08/01 12:51:48  newt
56 * Initial Revision
57 *
58 *****/
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62 
63 #include <stdlib.h>
64 #include <stdio.h>
65 
66 #if defined (HAVE_LIBPNG) || defined(HAVE_LIBZ)
67 #include <zlib.h>
68 #endif
69 
70 #if defined(HAVE_LIBJPEG)
71 #include <jpeglib.h>
72 #endif
73 
74 #include <png.h> /* must be included before Xft because it would create build problems if it were included afterwards. ( it is indirectly included by plc.h ) */
75 #ifdef HAVE_XFT
76 #include <X11/Xft/Xft.h>
77 #endif
78 #include <X11/Xlib.h>
79 
80 
81 #include "toolkit.h"
82 #include XmHTMLPrivateHeader
83 
84 #ifdef HAVE_XCCP_H
85 #include "XCCP.h"
86 #endif
87 #include "plc.h"
88 
89 /*** External Function Prototype Declarations ***/
90 
91 /*** Public Variable Declarations ***/
92 
93 /*** Private Datatype Declarations ****/
94 
95 /*** Private Function Prototype Declarations ****/
96 static void _PLCInsert(PLC *plc);
97 static void _PLCRemove(PLC *plc);
98 static void _PLCRun(PLC *plc);
99 static void _PLCEndData(PLC *plc);
100 static void _PLCRecomputeDelays(XmHTMLWidget html);
101 
102 #ifdef PLC_WORKPROCS
103 static Boolean _PLCSubCycler(XtPointer call_data);
104 #else
105 static void _PLCRecomputeDelays(XmHTMLWidget html);
106 #endif	/* PLC_WORKPROCS */
107 
108 /*****
109 * PLCProc transfer, finalize and low-level init functions for images.
110 *****/
111 static void _PLC_IMG_Init(PLC *plc);
112 static void _PLC_IMG_Transfer(PLC *plc);
113 static void _PLC_IMG_Finalize(PLC *plc);
114 
115 static void _PLC_IMG_UpdateScreen(XmHTMLWidget html, XmHTMLImage *image,
116 	XmHTMLObjectTableElement elePtr, int src_y, Dimension height);
117 static void _PLC_IMG_UpdateScreenCopies(XmHTMLWidget html, XmHTMLImage *image,
118 	int src_y, Dimension height);
119 
120 /*****
121 * PLCProc transfer, finalize and low-level init functions for documents.
122 *****/
123 static void _PLC_DOC_Init(PLC *plc);
124 static void _PLC_DOC_Transfer(PLC *plc);
125 static void _PLC_DOC_Finalize(PLC *plc);
126 
127 /*****
128 * PLCProc transfer, finalize and low-level init functions for unknown objects.
129 *****/
130 static void _PLC_ANY_Init(PLC *plc);
131 static void _PLC_ANY_Transfer(PLC *plc);
132 static void _PLC_ANY_Finalize(PLC *plc);
133 
134 /*** Private Variable Declarations ***/
135 
136 /***************
137 ***** PLC Support functions
138 ***************/
139 
140 /*****
141 * Name:			_PLCDataRequest
142 * Return Type:	Boolean
143 * Description:	makes a get_data() request for the current PLC
144 * In:
145 *	plc:		current PLC
146 * Returns:
147 *	True when request was served, False if not. plc_status is also updated
148 *	to reflect actual request return code.
149 *****/
150 Boolean
_PLCDataRequest(PLC * plc)151 _PLCDataRequest(PLC *plc)
152 {
153 	int status;
154 	static XmHTMLPLCStream plc_context;
155 
156 	if(plc == NULL)
157 		return(False);
158 
159 	_XmHTMLDebug(14, ("plc.c: _PLCDataRequest called for %s\n", plc->url));
160 
161 	/* *very* usefull sanity */
162 	if(plc->max_in == 0 || plc->max_in < plc->min_in)
163 		plc->max_in = plc->input_size;
164 
165 	/*****
166 	* next_in is the current position in the destination buffer,
167 	* so we need to make sure that next and max_in do not exceed input
168 	* buffer size
169 	*****/
170 	if(plc->left + plc->max_in > plc->buf_size)
171 		plc->max_in = plc->buf_size - plc->left;
172 
173 	/* yet another sanity */
174 	if(plc->max_in && plc->min_in >= plc->max_in)
175 		plc->min_in = 0;
176 
177 	/* fill stream buffer */
178 	plc_context.total_in  = plc->total_in;	/* bytes received so far */
179 	plc_context.min_out   = plc->min_in;	/* minimum no of bytes requested */
180 	plc_context.max_out   = plc->max_in;	/* maximum no of bytes requested */
181 	plc_context.user_data = plc->user_data;	/* user_data for this PLC */
182 
183 	_XmHTMLDebug(14, ("plc.c: _PLCDataRequest, requesting anywhere between %i "
184 		"and %i bytes\n", plc->min_in, plc->max_in));
185 
186 	/* get data from the external data stream */
187 	if((status = plc->sf.get_data(&plc_context, plc->input_buffer)) > 0)
188 	{
189 		/* bad copy, issue warning but proceed. */
190 		if(status < plc->min_in)
191 		{
192 			_XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLCGetData"),
193 				XMHTML_MSG_95, status, plc->min_in, "minimally");
194 		}
195 		if(status > plc->max_in)
196 		{
197 			_XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLCGetData"),
198 				XMHTML_MSG_95, status, plc->max_in, "maximally");
199 			status = plc->max_in;
200 		}
201 
202 		_XmHTMLDebug(14, ("plc.c: _PLCDataRequest, got %i bytes.\n", status));
203 
204 		/* more than min_in bytes returned, activate plc */
205 		plc->plc_status = PLC_ACTIVE;
206 
207 		/* update received byte count */
208 		plc->total_in += status;
209 
210 		/*****
211 		* move data left to the beginning of the buffer (thereby discarding
212 		* already processed data)
213 		*****/
214 		if(plc->left)
215 			plc->buffer = (Byte*)memmove(plc->buffer,
216 				plc->buffer + (plc->size - plc->left), plc->left);
217 
218 		/* append newly received data */
219 		(void)memcpy(plc->buffer + plc->left, plc->input_buffer, status);
220 
221 		/* this many bytes are valid in the buffer */
222 		plc->size = plc->left + status;
223 		/* reset current ptr position */
224 		plc->next_in = plc->buffer;
225 
226 		/* this many bytes left for reading in the buffer */
227 		plc->left += status;
228 
229 		return(True);
230 	}
231 
232 	/* check return value in most logical (?) order */
233 	if(status == STREAM_RESIZE)
234 	{
235 		/* we have been requested to resize the buffers */
236 		if(plc_context.max_out <= 0)
237 		{
238 			/* this *is* definitly an error */
239 			_XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner,
240 				"_PLCDataRequest"), XMHTML_MSG_96);
241 			return(False);
242 		}
243 
244 		/* resize input buffer */
245 		plc->input_buffer = (Byte*)realloc(plc->input_buffer,
246 								plc_context.max_out * sizeof(Byte));
247 		plc->input_size   = plc_context.max_out;
248 		plc->buf_size     = plc_context.max_out;
249 		plc->max_in       = plc_context.max_out;
250 
251 		/*****
252 		* Always backtrack if we have data left in the current buffer.
253 		* We make it ourselves easy here and let the user worry about it.
254 		*****/
255 		if(plc->left)
256 		{
257 			plc->total_in -= plc->left;
258 			plc->left      = 0;
259 			plc->next_in   = NULL;
260 			plc->size      = 0;
261 		}
262 		/* resize current data buffer */
263 		plc->buffer = (Byte*)realloc(plc->buffer, plc->buf_size * sizeof(Byte));
264 
265 		/* and call ourselves again with the new buffers in place */
266 		return(_PLCDataRequest(plc->self));
267 	}
268 
269 	if(status == STREAM_SUSPEND)
270 	{
271 		/* not enough data available, suspend this plc */
272 		_XmHTMLDebug(14, ("plc.c: _PLCDataRequest, suspending this PLC\n"));
273 		plc->plc_status = PLC_SUSPEND;
274 		plc->plc_data_status = STREAM_SUSPEND;
275 	}
276 	else if(status == STREAM_END)
277 	{
278 		/* all data has been received, terminate plc */
279 		_XmHTMLDebug(14, ("plc.c: _PLCDataRequest, terminating PLC\n"));
280 		plc->plc_status = PLC_COMPLETE;
281 		plc->plc_data_status = STREAM_END;
282 	}
283 	else
284 	{
285 		/* plc has been aborted */
286 		_XmHTMLDebug(14, ("plc.c: _PLCDataRequest, aborting PLC\n"));
287 		plc->plc_status = PLC_ABORT;
288 		plc->plc_data_status = STREAM_ABORT;
289 	}
290 	return(False);
291 }
292 
293 /*****
294 * Name: 		_PLCEndData
295 * Return Type: 	void
296 * Description:	calls the end_data() function to signal this PLC is
297 *				terminating.
298 * In:
299 *	plc:		current PLC
300 * Returns:
301 *
302 *****/
303 static void
_PLCEndData(PLC * plc)304 _PLCEndData(PLC *plc)
305 {
306 	static XmHTMLPLCStream plc_context;
307 	PLCImage *any_image = &(plc->object->plc_any_image);
308 	XmImageInfo *image;
309 
310 	/* potential memory leak if XmNprogressiveEndProc resource is empty */
311 	if(plc->sf.end_data == NULL)
312 	{
313 		_XmHTMLWarning(__WFUNC__(any_image->owner, "_PLCEndData"),
314 			XMHTML_MSG_97);
315 		return;
316 	}
317 
318 	_XmHTMLDebug(14, ("plc.c: _PLCEndData called for %s\n", plc->url));
319 
320 	plc_context.total_in  = plc->total_in;	/* bytes received so far */
321 	plc_context.min_out   = 0;				/* meaningless */
322 	plc_context.max_out   = 0;				/* meaningless */
323 	plc_context.user_data = plc->user_data;	/* user_data for this PLC */
324 
325 	if(plc->object->type == plcAny || plc->object->type == plcDocument)
326 	{
327 		plc->sf.end_data(&plc_context, NULL, XmNONE,
328 			(plc->plc_status == PLC_COMPLETE));
329 		return;
330 	}
331 	image = any_image->info;
332 	plc->sf.end_data(&plc_context, (XtPointer)image, XmPLC_IMAGE,
333 		(plc->plc_status == PLC_COMPLETE));
334 }
335 
336 /*****
337 * Name: 		_PLCReadOK
338 * Return Type: 	size_t
339 * Description: 	copy len bytes to buf from an ImageBuffer
340 * In:
341 *	*plc:		current PLC
342 *	buf:		data destination
343 *	len:		no of bytes to copy
344 * Returns:
345 *	actual no of bytes read or 0 on failure or end of buffer.
346 *	This function will make a data request if the current buffer runs out
347 *	of data.
348 *****/
349 size_t
_PLCReadOK(PLC * plc,Byte * buf,int len)350 _PLCReadOK(PLC *plc, Byte *buf, int len)
351 {
352 	/* plc->left is the number of bytes left in the input buffer. */
353 	if(len <= plc->left)
354 	{
355 		buf = (Byte*)memcpy(buf, plc->next_in, len);
356 		/* new position in buffer */
357 		plc->next_in += len;
358 		/* this many bytes still available */
359 		plc->left -= len;
360 		return(len);
361 	}
362 	/* not enough data available, make a request */
363 	plc->min_in = len - plc->left;
364 	plc->max_in = PLC_MAX_BUFFER_SIZE;
365 
366 	if(!(_PLCDataRequest(plc)))
367 		return(0);	/* suspended, aborted or end of data */
368 
369 	/* read again */
370 	return(_PLCReadOK(plc, buf, len));
371 }
372 
373 /*****
374 * Name: 		_PLCGetDataBlock
375 * Return Type: 	int
376 * Description: 	gets the next amount of data from the input buffer
377 * In:
378 *	plc:		current PLC
379 *	buf:		storage buffer, filled upon return.
380 * Returns:
381 *	no of bytes copied into buf or 0 when no more data.
382 *****/
383 size_t
_PLCGetDataBlock(PLC * plc,Byte * buf)384 _PLCGetDataBlock(PLC *plc, Byte *buf)
385 {
386 	Byte count = 0;
387 
388 	if(!_PLCReadOK(plc, &count, 1))
389 		return(0);
390 
391 	if(((int)count != 0) && (!_PLCReadOK(plc, buf, (int)count)))
392 		return(0);
393 
394 	return((size_t)count);
395 }
396 
397 /***************
398 ***** PLC usage functions
399 ***************/
400 
401 /*****
402 * Name: 		_XmHTMLPLCCreate
403 * Return Type: 	PLC*
404 * Description: 	creates a PLC for the given widget
405 * In:
406 *	html:		XmHTMLWidget id;
407 *	url:		object for which this PLC is created;
408 *	priv..:		private data to be registered for this PLC.
409 *	type:		type of PLC to be created
410 * Returns:
411 *	a newly created PLC.
412 * Note:
413 *	Type indicates what type of object should be created. It can be
414 *	XmNONE, XmPLC_IMAGE or XmPLC_DOCUMENT.
415 *	The priv_data argument is not the same as the user_data field. It allows
416 *	an application to attach internal data to a PLC.
417 ****/
418 PLC*
_XmHTMLPLCCreate(XmHTMLWidget html,XtPointer priv_data,String url,Byte type)419 _XmHTMLPLCCreate(XmHTMLWidget html, XtPointer priv_data, String url, Byte type)
420 {
421 	static PLC *plc;
422 
423 	_XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCreate for %s\n", url));
424 
425 	plc = (PLC*)malloc(sizeof(PLC));
426 	memset(plc, 0, sizeof(PLC));
427 
428 	plc->url      = strdup(url);
429 	plc->buffer   = (Byte*)malloc(PLC_MAX_BUFFER_SIZE);
430 	plc->buf_size = (int)PLC_MAX_BUFFER_SIZE;
431 	plc->size     = (int)0;
432 	plc->left     = (int)0;
433 	plc->next_in  = (Byte*)NULL;
434 
435 	plc->input_buffer = (Byte*)malloc(PLC_MAX_BUFFER_SIZE);
436 	plc->input_size   = (int)PLC_MAX_BUFFER_SIZE;
437 	plc->total_in = (int)0;
438 	plc->max_in   = (int)PLC_MAX_BUFFER_SIZE;
439 	plc->min_in   = (int)0;
440 
441 	plc->object   = (PLCObject*)calloc(1, sizeof(PLCObject));
442 	plc->object->plc_any.owner = html;
443 
444 	/* a privatly ownded GC for XPutImage to use */
445 	if(html->html.plc_gc == NULL)
446 	{
447 		XGCValues xgc;
448 
449 		xgc.function = GXcopy;
450 		xgc.plane_mask = AllPlanes;
451 
452 		html->html.plc_gc = XCreateGC(XtDisplay((Widget)html),
453 			(XtIsRealized((Widget)html) ? XtWindow(html->html.work_area) :
454 				DefaultRootWindow(XtDisplay((Widget)html))),
455 				GCFunction | GCPlaneMask , &xgc);
456 	}
457 
458 	plc->plc_status      = PLC_ACTIVE;
459 	plc->plc_data_status = STREAM_OK;
460 
461 	plc->priv_data   = priv_data;
462 
463 	/* must be updated by caller */
464 	plc->user_data   = (XtPointer)NULL;
465 
466 	/* can be overriden by caller */
467 	plc->sf.get_data = html->html.get_data;
468 	plc->sf.end_data = html->html.end_data;
469 
470 	/* object specific transfer, finalize and low-level init functions */
471 	if(type == XmPLC_IMAGE)
472 	{
473 		plc->object->type = plcAnyImage;
474 		plc->transfer     = (PLCProc)_PLC_IMG_Transfer;
475 		plc->finalize     = (PLCProc)_PLC_IMG_Finalize;
476 		plc->sf.c_new     = (PLCProc)_PLC_IMG_Init;
477 		plc->obj_set      = False;
478 	}
479 	else if(type == XmPLC_DOCUMENT)
480 	{
481 		plc->object->type = plcDocument;
482 		plc->transfer     = (PLCProc)_PLC_DOC_Transfer;
483 		plc->finalize     = (PLCProc)_PLC_DOC_Finalize;
484 		plc->sf.c_new     = (PLCProc)_PLC_DOC_Init;
485 		plc->obj_set      = False;
486 	}
487 	else /* type is unknown */
488 	{
489 		plc->object->type = plcAny;
490 		plc->transfer     = (PLCProc)_PLC_ANY_Transfer;
491 		plc->finalize     = (PLCProc)_PLC_ANY_Finalize;
492 		plc->sf.c_new     = (PLCProc)_PLC_ANY_Init;
493 		plc->obj_set      = False;
494 	}
495 	/* these *must* be set when the plc->sf.c_new() function is called */
496 	plc->init        = (PLCProc)NULL;
497 	plc->destructor  = (PLCProc)NULL;
498 
499 	plc->initialized = False;
500 
501 	/*****
502 	* These must be set by the caller, and can be set by the
503 	* init method (which happens to be the case for images).
504 	*****/
505 	plc->obj_funcs[0] = (PLCProc)NULL;
506 	plc->obj_funcs[1] = (PLCProc)NULL;
507 	plc->obj_funcs[2] = (PLCProc)NULL;
508 	plc->curr_obj_func      = 0;
509 	plc->obj_funcs_complete = False;
510 
511 	plc->next_plc = NULL;
512 	plc->prev_plc = NULL;
513 
514 	plc->self = plc;
515 
516 	/* insert it */
517 	_PLCInsert(plc);
518 
519 	return(plc);
520 }
521 
522 /*****
523 * Name: 		_PLCInsert
524 * Return Type:	void
525 * Description:	insert a PLC in the PLC ringbuffer
526 * In:
527 *	plc:		current PLC
528 * Returns:
529 *	nothing.
530 *****/
531 static void
_PLCInsert(PLC * plc)532 _PLCInsert(PLC *plc)
533 {
534 	PLC *tmp;
535 	XmHTMLWidget html = plc->object->plc_any.owner;
536 
537 	_XmHTMLDebug(14, ("plc.c: _XmHTMLPLCInsert for %s\n", plc->url));
538 
539 	/* first element, let it point to itself */
540 	if(html->html.plc_buffer == NULL)
541 	{
542 		plc->next_plc = plc->prev_plc = plc;
543 		html->html.plc_buffer = plc;
544 		html->html.num_plcs++;
545 		return;
546 	}
547 
548 	/*
549 	* We already have elements in the plc buffer.
550 	* The new plc is inserted as the next element of the current PLC so it
551 	* will get activated immediatly.
552 	*/
553 	tmp = html->html.plc_buffer->next_plc;
554 	tmp->prev_plc = plc;
555 	plc->next_plc = tmp;
556 	plc->prev_plc = html->html.plc_buffer;
557 	html->html.plc_buffer->next_plc = plc;
558 
559 	/* keep up running PLC count as well */
560 	html->html.num_plcs++;
561 }
562 
563 /*****
564 * Name: 		_PLCRemove
565 * Return Type:	void
566 * Description:	removes a PLC from the PLC ringbuffer and calls the object
567 *				destructor method.
568 * In:
569 *	plc:		current PLC
570 * Returns:
571 *	nothing.
572 *****/
573 static void
_PLCRemove(PLC * plc)574 _PLCRemove(PLC *plc)
575 {
576 	PLC *next, *prev;
577 	XmHTMLWidget html = plc->object->plc_any.owner;
578 
579 	_XmHTMLDebug(14, ("plc.c: _PLCRemove for %s\n", plc->url));
580 
581 	/* call finalize method if this plc was ended prematurely */
582 	if(plc->obj_funcs_complete == False)
583 		plc->finalize(plc->self);
584 
585 	/*****
586 	* call the end_data() function to signal the user that this PLC is
587 	* about to be destroyed.
588 	*****/
589 	_PLCEndData(plc->self);
590 
591 	/* call destructor method */
592 	plc->destructor(plc->self);
593 
594 	/* now remove it */
595 	next = plc->next_plc;
596 	prev = plc->prev_plc;
597 
598 	/* this is the last PLC in the plc ringbuffer */
599 	if(next == plc->self || prev == plc->self)
600 	{
601 		/* kill the main plc cycler */
602 		html->html.plc_buffer = NULL;
603 		_XmHTMLKillPLCCycler(html);
604 	}
605 	else
606 	{
607 		next->prev_plc = prev;
608 		prev->next_plc = next;
609 
610 		/* if this is the current plc, advance the ring buffer */
611 		if(html->html.plc_buffer == plc->self)
612 			html->html.plc_buffer = next;
613 	}
614 
615 	/*****
616 	* If no more PLC's are left in the buffer, call the end_data() function
617 	* to signal the user that we are done loading progressively.
618 	*****/
619 	if(html->html.plc_buffer == NULL || html->html.num_plcs == 1)
620 	{
621 		if(plc->sf.end_data != NULL)
622 			plc->sf.end_data(NULL, NULL, XmPLC_FINISHED, True);
623 	}
624 
625 	/* and destroy the PLC itself */
626 	free(plc->url);
627 	free(plc->object);			/* all object data */
628 	free(plc->buffer);			/* current data buffer */
629 	free(plc->input_buffer);	/* current input buffer */
630 	free(plc);
631 /*	plc = NULL;*/
632 
633 	/* keep up running PLC count as well */
634 	if(html->html.num_plcs)
635 		html->html.num_plcs--;
636 
637 	if(html->html.num_plcs == 0 && html->html.plc_buffer != NULL)
638 		_XmHTMLWarning(__WFUNC__(html, "_PLCRemove"), XMHTML_MSG_98);
639 }
640 
641 #ifndef PLC_WORKPROCS
642 /*****
643 * Name: 		_PLCRecomputeDelays
644 * Return Type: 	void
645 * Description: 	computes a new PLC polling interval.
646 * In:
647 *	html:		XmHTMLWidget id;
648 * Returns:
649 *	nothing, but the PLC polling interval is updated upon return.
650 * Note1:
651 *	Adjust delay using connection effectiveness (pload below).
652 *	We use a simple equation: x = -y + 50, where y is the connection
653 *	effectiveness and x is the required delay adjustment. This is a simple
654 *	ramp with start point at (50,0) and ending point at (-50,100), which
655 *	cuts the current delay in two with a pload of 100% (all PLC's active)
656 *	and doubles it with a pload of 0% (all PLC's suspended).
657 * Note2:
658 *	This function is only called when PLC cycling is done using timeouts.
659 *****/
660 static void
_PLCRecomputeDelays(XmHTMLWidget html)661 _PLCRecomputeDelays(XmHTMLWidget html)
662 {
663 	int delay, min_delay, max_delay, nplcs, i, nactive, pload, pinc;
664 	PLC *plc;
665 
666 	if((nplcs = html->html.num_plcs) == 0)
667 	{
668 		html->html.plc_delay = html->html.plc_def_delay;
669 		return;
670 	}
671 
672 	delay     = html->html.plc_delay;
673 	min_delay = html->html.plc_min_delay;
674 	max_delay = html->html.plc_max_delay;
675 	plc       = html->html.plc_buffer;
676 
677 	/* make a guess at the effectiveness of the user's connection */
678 	for(i = 0, nactive = 0; i < nplcs; i++, plc = plc->next_plc)
679 		if(plc->plc_status == PLC_ACTIVE)
680 			nactive++;
681 
682 	/*****
683 	* compute new polling interval using the equation mentioned above.
684 	*****/
685 	pload = (nactive/(float)nplcs)*100;
686 	pinc = delay * (-pload+50)/100;
687 	delay += pinc;
688 
689 	if(delay < min_delay)
690 		delay = min_delay;
691 	if(delay > max_delay)
692 		delay = max_delay;
693 
694 	html->html.plc_delay = delay;
695 }
696 #endif /* !PLC_WORKPROCS */
697 
698 /*****
699 * Name: 		_PLCRun
700 * Return Type:	void
701 * Description:	activate a PLC
702 * In:
703 *	plc:		current PLC
704 * Returns:
705 *	nothing.
706 * Note:
707 *	This is the actual PLC cycler. It will activate routines as necessary.
708 *****/
709 static void
_PLCRun(PLC * plc)710 _PLCRun(PLC *plc)
711 {
712 	XmHTMLWidget html;
713 
714 	_XmHTMLDebug(14, ("plc.c: _PLCRun for %s\n", plc->url));
715 
716 	/* see if the object has been set */
717 	if(plc->obj_set == False)
718 	{
719 		plc->sf.c_new(plc->self);
720 		return;
721 	}
722 
723 	/* check if we have been suspended in between calls */
724 	html = plc->object->plc_any.owner;
725 
726 	if(html->html.plc_suspended)
727 	{
728 		_XmHTMLDebug(14, ("plc.c: _PLCRun, plc suspension, returning.\n"));
729 		return;
730 	}
731 
732 	/* see if we have been initialized */
733 	if(plc->initialized == False)
734 	{
735 		/* call object initializer */
736 		plc->init(plc->self);
737 		return;
738 	}
739 
740 	/* we have been initialized, call the current obj function */
741 	plc->obj_funcs[plc->curr_obj_func](plc->self);
742 
743 	/*****
744 	* If the plc_status is PLC_ACTIVE or PLC_COMPLETE when the above
745 	* function returns, call the object transfer function as well.
746 	*****/
747 	if(plc->plc_status == PLC_ACTIVE || plc->plc_status == PLC_COMPLETE)
748 		plc->transfer(plc->self);
749 
750 	/*****
751 	* if the object functions are finished, call the finalize method
752 	* as well.
753 	*****/
754 	if(plc->obj_funcs_complete == True)
755 	{
756 		plc->finalize(plc->self);
757 		plc->plc_status = PLC_COMPLETE;
758 	}
759 }
760 
761 #ifdef PLC_WORKPROCS
762 /*****
763 * Name: 		_PLCSubCycler
764 * Return Type: 	Boolean
765 * Description: 	PLC cycling engine when work procedures are to be used.
766 * In:
767 *	call_data:	data registered for this function.
768 * Returns:
769 *	False when this function should be called again by X (as long as
770 *	there are PLC's in the list), and True when processing is complete.
771 *	This function is actually of type XtWorkProc.
772 *****/
773 static Boolean
_PLCSubCycler(XtPointer call_data)774 _PLCSubCycler(XtPointer call_data)
775 {
776 	XmHTMLWidget html = (XmHTMLWidget)call_data;
777 	PLC *plc = html->html.plc_buffer;
778 
779 	/* return if we haven't got any PLC's installed */
780 	if(plc == NULL || html->html.plc_suspended)
781 		return(True);
782 
783 	_XmHTMLDebug(14, ("plc.c: _PLCSubCycler for %s\n", plc->url));
784 
785 	html->html.plc_proc_id = None;
786 
787 	/*****
788 	* As this is called thru a work procedure, we don't have to concern
789 	* ourselves with the speedups in the timeout variant. It will be called
790 	* *much* sooner.
791 	*****/
792 	switch(plc->plc_status)
793 	{
794 		case PLC_SUSPEND:
795 			/*****
796 			* _PLCDataRequest failed on previous call, skip it this time
797 			* to give the connection a bit more time and set plc_status so
798 			* it will get activated the next time this plc is called.
799 			*****/
800 			plc->plc_status = PLC_ACTIVE;
801 			/* move to next plc */
802 			html->html.plc_buffer = plc->next_plc;
803 			break;
804 
805 		case PLC_ACTIVE:
806 			_PLCRun(plc->self);
807 			/* move to next plc */
808 			html->html.plc_buffer = plc->next_plc;
809 			break;
810 
811 		case PLC_COMPLETE:
812 		case PLC_ABORT:
813 			/* _PLCRemove will advance to the next PLC by itself */
814 			_PLCRemove(plc->self);
815 			break;
816 
817 		default:
818 			_XmHTMLWarning(__WFUNC__(html, "_PLCSubCycler"),
819 				XMHTML_MSG_99, plc->plc_status);
820 			/* kill it next time it's called */
821 			plc->plc_status = PLC_ABORT;
822 			/* move to next plc */
823 			html->html.plc_buffer = plc->next_plc;
824 			break;
825 	}
826 
827 	/* continue processing if we have any plc's left */
828 	if(html->html.plc_buffer != NULL)
829 		return(False);
830 
831 	return(True);
832 }
833 #endif	/* PLC_WORKPROCS */
834 
835 /*****
836 * Name: 		_XmHTMLPLCCyler
837 * Return Type: 	void
838 * Description: 	main PLC cycling engine.
839 * In:
840 *	call_data:	data registed for this function.
841 *	proc_id:	unused;
842 * Returns:
843 *	nothing.
844 * Note:
845 *	When PLC_WORKPROCS was defined during compilation, this routine starts
846 *	up a subcycler which will do the real PLC processing using work procedures.
847 *	The default is to use a dynamic polling interval using timeouts (causing
848 *	this function to become a XtTimerCallbackProc).
849 *	This function is called only *once* from within the main XmHTML code:
850 *	when new text has been set into the widget.
851 *****/
852 /*ARGSUSED*/
853 void
_XmHTMLPLCCycler(XtPointer call_data,XtIntervalId * proc_id)854 _XmHTMLPLCCycler(XtPointer call_data, XtIntervalId *proc_id)
855 {
856 	XmHTMLWidget html = (XmHTMLWidget)call_data;
857 	PLC *plc;
858 	int nplcs, i;
859 
860 	/*****
861 	* Return if we haven't got any PLC's installed or if progressive
862 	* image loading has been suspended.
863 	*****/
864 	if((plc = html->html.plc_buffer) == NULL || html->html.plc_suspended)
865 		return;
866 
867 	/* make sure we aren't grabbing anything (total server lockup!) */
868 	XUngrabPointer(XtDisplay((Widget)html), CurrentTime);
869 
870 	nplcs = html->html.num_plcs;
871 
872 #ifdef PLC_WORKPROCS
873 	_XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCycler, starting subCycler\n"));
874 
875 	html->html.plc_proc_id = (XtWorkProcId)
876 		XtAppAddWorkProc(XtWidgetToApplicationContext((Widget)html),
877 			(XtWorkProc)_PLCSubCycler, (XtPointer)html);
878 #else
879 	_XmHTMLDebug(14, ("plc.c: _XmHTMLPLCCycler for %s\n", plc->url));
880 
881 	html->html.plc_proc_id = None;
882 
883 	switch(plc->plc_status)
884 	{
885 		case PLC_SUSPEND:
886 			/*****
887 			* _PLCDataRequest failed on previous call, skip it this time
888 			* to give the connection a bit more time and set plc_status so
889 			* it will get activated the next time this plc is called.
890 			*****/
891 			plc->plc_status = PLC_ACTIVE;
892 
893 			/* move to next plc */
894 			html->html.plc_buffer = plc->next_plc;
895 
896 			/*****
897 			* To prevent all to much slowdown (this routine is called by
898 			* a timer), we cycle thru the full list of installed plc's to see
899 			* if we have an active one. If we do, we fall thru this case and
900 			* activate it. While checking, we also activate any suspended plc's
901 			* so they will get called the next time this routine is activated.
902 			*****/
903 			for(i = 0; i < nplcs - 1; i++)
904 			{
905 				plc = html->html.plc_buffer;
906 				if(plc->plc_status == PLC_ACTIVE)
907 					break;
908 				else	/* activate this plc */
909 					plc->plc_status = PLC_ACTIVE;
910 				html->html.plc_buffer = plc->next_plc;
911 			}
912 			/* all plc's suspended, break out */
913 			if(plc->plc_status != PLC_ACTIVE)
914 				break;
915 
916 			/* we have found an active plc, fall through */
917 
918 		case PLC_ACTIVE:
919 			_PLCRun(plc->self);
920 
921 			/*****
922 			* Speedup: if this plc has finished, remove it right away,
923 			* there's no need to do this on the next call to this routine as
924 			* the finalizing and removal of this plc does not involve any
925 			* data requests.
926 			*****/
927 			if(plc->plc_status == PLC_COMPLETE || plc->plc_status == PLC_ABORT)
928 				_PLCRemove(plc->self);
929 			else	/* move to next plc */
930 				html->html.plc_buffer = plc->next_plc;
931 			break;
932 
933 		case PLC_COMPLETE:
934 		case PLC_ABORT:
935 			/* _PLCRemove will advance to the next PLC by itself */
936 			_PLCRemove(plc->self);
937 			break;
938 
939 		default:
940 			_XmHTMLWarning(__WFUNC__(html, "_XmHTMLPLCCycler"),
941 				XMHTML_MSG_99, plc->plc_status);
942 			/* kill it next time it's called */
943 			plc->plc_status = PLC_ABORT;
944 			/* move to next plc */
945 			html->html.plc_buffer = plc->next_plc;
946 			break;
947 	}
948 
949 	/* adjust PLC intervals */
950 	_PLCRecomputeDelays(html);
951 
952 	/* and re-instate ourselves if we have any plc's left */
953 	if(html->html.plc_buffer != NULL)
954 	{
955 		XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)html),
956 			html->html.plc_delay, (XtTimerCallbackProc)_XmHTMLPLCCycler,
957 			(XtPointer)html);
958 	}
959 #endif /* !PLC_WORKPROCS */
960 }
961 
962 /*****
963 * Name: 		_XmHTMLKillPLCCycler
964 * Return Type: 	void
965 * Description: 	kills all outstanding PLC procedures.
966 * In:
967 *	html:		XmHTMLWidget id for which to kill all PLC's
968 * Returns:
969 *	nothing, but the list of PLC's of the given XmHTMLWidget has been cleared
970 *	upon return.
971 * Note:
972 *	This function is called when the current document is discarded (by
973 *	loading a new document or destroying the widget).
974 *****/
975 void
_XmHTMLKillPLCCycler(XmHTMLWidget html)976 _XmHTMLKillPLCCycler(XmHTMLWidget html)
977 {
978 	PLC *plc = html->html.plc_buffer;
979 
980 	_XmHTMLDebug(14, ("plc.c: _XmHTMLKillPLCCycler\n"));
981 
982 	/* kill any outstanding plc cycler timeouts */
983 	if(html->html.plc_proc_id != None)
984 	{
985 		html->html.plc_suspended = True;
986 
987 #ifdef PLC_WORKPROCS
988 		XtRemoveWorkProc((XtWorkProcId)html->html.plc_proc_id);
989 #else
990 		XtRemoveTimeOut(html->html.plc_proc_id);
991 #endif	/* PLC_WORKPROCS */
992 		html->html.plc_proc_id = None;
993 	}
994 
995 	/* reset PLC timeout to the default value */
996 	html->html.plc_delay = html->html.plc_def_delay;
997 
998 	if(plc == NULL)
999 	{
1000 		/* restore defaults */
1001 		html->html.num_plcs      = 0;
1002 		html->html.plc_suspended = True;
1003 		html->html.plc_delay     = html->html.plc_def_delay;
1004 		return;
1005 	}
1006 
1007 	/* now remove all outstanding plc's */
1008 	while(html->html.plc_buffer != NULL)
1009 	{
1010 		/* abort all outstanding PLC's */
1011 		plc = html->html.plc_buffer;
1012 
1013 		_XmHTMLDebug(14, ("plc.c: _XmHTMLKillPLCCycler, aborting %s\n",
1014 			plc->url));
1015 
1016 		plc->plc_status = PLC_ABORT;
1017 		_PLCRemove(plc);
1018 	}
1019 	/* restore defaults */
1020 	html->html.num_plcs      = 0;
1021 	html->html.plc_suspended = True;
1022 	html->html.plc_delay     = html->html.plc_def_delay;
1023 
1024 	/* free the plc_gc if it's still here */
1025 	if(html->html.plc_gc)
1026 	{
1027 		XFreeGC(XtDisplay((Widget)html), html->html.plc_gc);
1028 		html->html.plc_gc = (GC)NULL;
1029 	}
1030 }
1031 
1032 /*****
1033 * Object-specific routines.
1034 * There are three sets of them, each with three functions:
1035 *	_PLC_IMG class: image PLC functions;
1036 *	_PLC_DOC class: document PLC functions;
1037 *	_PLC_ANY class: unknown PLC functions;
1038 * The three functions are:
1039 *	_Init:     function that performs initialization of the object for which
1040 *              a PLC is to be used. This may involve getting more
1041 *              object-specific data. When this function finishes, it should
1042 *              set the obj_set field to True.
1043 *	_Transfer: function that needs to perform *intermediate* transfer of
1044 *              object-specific data to the final destination;
1045 *	_Finalize: function that needs to perform *final* transfer of
1046 *              object-specific data to the final destination;
1047 *****/
1048 
1049 /***************
1050 ***** Image PLC object and supporting functions
1051 ***************/
1052 
1053 /*****
1054 * Name: 		_PLC_IMG_Init
1055 * Return Type: 	void
1056 * Description:
1057 * In:
1058 *	plc:		current PLC
1059 * Returns:
1060 *	nothing. Upon return, the object field of the current PLC is updated to
1061 *	reflect the type of image that is to be loaded (includes image-specific
1062 *	object functions).
1063 *****/
1064 static void
_PLC_IMG_Init(PLC * plc)1065 _PLC_IMG_Init(PLC *plc)
1066 {
1067 	Byte obj_type = plcAnyImage, img_type = IMAGE_UNKNOWN;
1068 	Byte magic[10];
1069 	static Byte png_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
1070 
1071 	_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Init for %s\n", plc->url));
1072 
1073 	/* get first 10 bytes to determine the image type */
1074 	plc->min_in = 10;
1075 	plc->max_in = PLC_MAX_BUFFER_SIZE;
1076 
1077 	/*****
1078 	* We *need* to know the image type before we can start doing
1079 	* anything.
1080 	*****/
1081 	if(!(_PLCDataRequest(plc)))
1082 		return;
1083 
1084 	memcpy(magic, plc->buffer, 10);
1085 
1086 	/* check image types we known of. Do most (?) logical order */
1087 	if(!(strncmp((char*)magic, "GIF87a", 6)) ||
1088 		!(strncmp((char*)magic, "GIF89a", 6)))
1089 	{
1090 		obj_type = plcGIF;
1091 		img_type = IMAGE_GIF;
1092 		plc->init         = _PLC_GIF_Init;
1093 		plc->destructor   = _PLC_GIF_Destructor;
1094 		plc->obj_funcs[0] = _PLC_GIF_ScanlineProc;
1095 		plc->object->plc_gif_image.info = (XmImageInfo*)plc->priv_data;
1096 	}
1097 	/* compatible gif */
1098 	else if(!(strncmp((char*)magic, "GZF87a", 6)) ||
1099 		!(strncmp((char*)magic, "GZF89a", 6)))
1100 	{
1101 #if defined (HAVE_LIBPNG) || defined (HAVE_LIBZ)
1102 		obj_type = plcGZF;
1103 		img_type = IMAGE_GZF;
1104 		plc->init         = _PLC_GZF_Init;
1105 		plc->destructor   = _PLC_GZF_Destructor;
1106 		plc->obj_funcs[0] = _PLC_GZF_ScanlineProc;
1107 		plc->object->plc_gzf_image.info = (XmImageInfo*)plc->priv_data;
1108 #endif /* HAVE_LIBPNG || HAVE_LIBZ */
1109 	}
1110 	else if(magic[0] == 0xff && magic[1] == 0xd8 && magic[2] == 0xff)
1111 	{
1112 #ifdef HAVE_LIBJPEG
1113 		obj_type = plcJPEG;
1114 		img_type = IMAGE_JPEG;
1115 		plc->init         = _PLC_JPEG_Init;
1116 		plc->destructor   = _PLC_JPEG_Destructor;
1117 		plc->obj_funcs[0] = _PLC_JPEG_ScanlineProc;
1118 		plc->object->plc_jpeg_image.info = (XmImageInfo*)plc->priv_data;
1119 #endif /* HAVE_LIBJPEG */
1120 	}
1121 	else if(!(memcmp(magic, png_magic, 8)))
1122 	{
1123 #ifdef HAVE_LIBPNG
1124 
1125 /*** internal configuration define, don't define this yourself ***/
1126 #ifdef PLC_PNG
1127 		obj_type = plcPNG;
1128 		img_type = IMAGE_PNG;
1129 		plc->init         = _PLC_PNG_Init;
1130 		plc->destructor   = _PLC_PNG_Destructor;
1131 		plc->obj_funcs[0] = _PLC_PNG_ScanlineProc;
1132 		plc->object->plc_png_image.info = (XmImageInfo*)plc->priv_data;
1133 #endif /* PLC_PNG */
1134 
1135 #endif /* HAVE_LIBPNG */
1136 	}
1137 #ifdef HAVE_XPM
1138 	else if(!(strncmp((char*)magic, "/* XPM */", 9)))
1139 	{
1140 		obj_type = plcXPM;
1141 		img_type = IMAGE_XPM;
1142 		plc->init         = _PLC_XPM_Init;
1143 		plc->destructor   = _PLC_XPM_Destructor;
1144 		plc->obj_funcs[0] = _PLC_XPM_ScanlineProc;
1145 		plc->object->plc_xpm_image.info = (XmImageInfo*)plc->priv_data;
1146 	}
1147 #endif
1148 	else if(!(strncmp((char*)magic, "#define", 7)) ||
1149 		(magic[0] == '/' && magic[1] == '*'))
1150 	{
1151 		obj_type = plcXBM;
1152 		img_type = IMAGE_XBM;
1153 		plc->init         = _PLC_XBM_Init;
1154 		plc->destructor   = _PLC_XBM_Destructor;
1155 		plc->obj_funcs[0] = _PLC_XBM_ScanlineProc;
1156 		plc->object->plc_xbm_image.info = (XmImageInfo*)plc->priv_data;
1157 	}
1158 
1159 	/* check if we got an image */
1160 	if(obj_type == plcAnyImage)
1161 	{
1162 		_XmHTMLWarning(__WFUNC__(plc->object->plc_any.owner, "_PLC_IMG_Init"),
1163 			XMHTML_MSG_100, plc->url);
1164 		plc->plc_status = PLC_ABORT;
1165 		return;
1166 	}
1167 	plc->object->plc_any.type = obj_type;
1168 	plc->object->plc_any_image.info->type = img_type;
1169 	plc->obj_set = True;
1170 	return;
1171 }
1172 
1173 /*****
1174 * Name: 		_PLC_IMG_Transfer
1175 * Return Type: 	void
1176 * Description: 	intermediate image transfer function
1177 * In:
1178 *	plc:		current PLC.
1179 * Returns:
1180 *	nothing.
1181 * Note:
1182 *	When this routine is called for the first time, it initializes all common
1183 *	image fields (XImage, pixmap, color arrays, clipmask, etc...).
1184 *
1185 *	The actual image composition is split in six parts:
1186 *	1. color counting; the numbers of colors used by the new chunk of data is
1187 *	   counted, and the RGB arrays for these colors are filled;
1188 *	2. data pixelization; the new chunk of raw data is mapped to the pixel
1189 *	   values assigned in the previous step;
1190 *	3. color allocation; if new colors are to be allocated that's done now;
1191 *	4. XImage updating; scanlines represented by the new chunk of data are
1192 *	   added to the existing scanlines already present in the XImage;
1193 *	5. Pixmap updating; the newly added scanlines are copied into the
1194 *	   destination drawable (a Pixmap);
1195 *	6. Display updating: the updated portion of the pixmap is copied to screen.
1196 *****/
1197 static void
_PLC_IMG_Transfer(PLC * plc)1198 _PLC_IMG_Transfer(PLC *plc)
1199 {
1200 	PLCImage *any_image = &(plc->object->plc_any_image);
1201 	XmHTMLImage *image  = any_image->image;
1202 	XmImageInfo *info   = any_image->info;
1203 	XmHTMLWidget html   = any_image->owner;
1204 	int width, height, lo, hi;
1205 	int col_cnt;	/* no of colors in image, accumulated */
1206 	int npixels;	/* no of allocated pixel values, accumulated */
1207 	Boolean pixels[XmHTML_MAX_IMAGE_COLORS];
1208 	register int i;
1209 	Byte *ptr, *data;
1210 	int ypos, nlines;
1211 	Display *dpy  = XtDisplay((Widget)html);
1212 
1213 	_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer for %s\n", plc->url));
1214 
1215 	if(info == NULL)
1216 	{
1217 		_XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"),
1218 			XMHTML_MSG_101, plc->url);
1219 		plc->plc_status = PLC_ABORT;
1220 		return;
1221 	}
1222 
1223 	/* don't do a thing if we haven't received any new data */
1224 	if(any_image->prev_pos == any_image->data_pos)
1225 	{
1226 		_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer end, no new data to "
1227 			"process\n"));
1228 		return;
1229 	}
1230 
1231 	/* no HTML image stored yet, go pick it up */
1232 	if(image == NULL)
1233 	{
1234 		Window win;
1235 		Boolean need_redisplay = False;
1236 
1237 		for(image = html->html.images; image != NULL &&
1238 			image->html_image != info; image = image->next);
1239 		if(image == NULL)
1240 		{
1241 			_XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"),
1242 				XMHTML_MSG_102, plc->url);
1243 			plc->plc_status = PLC_ABORT;
1244 			return;
1245 		}
1246 		image->options |= IMG_PROGRESSIVE;
1247 
1248 		any_image->image = image;
1249 
1250 		/* if we are realized we have a window */
1251 		if(XtIsRealized((Widget)html))
1252 			win = XtWindow(html->html.work_area);
1253 		else
1254 			win = DefaultRootWindow(dpy);
1255 
1256 		/* store original image dimensions */
1257 		info->swidth  = any_image->width;
1258 		info->sheight = any_image->height;
1259 
1260 		/*****
1261 		* See if any dimensions were specified on the <IMG> element
1262 		* or if we may not perform scaling.
1263 		*****/
1264 		if(!ImageHasDimensions(image) || !ImageInfoScale(info))
1265 		{
1266 			/* store used image dimensions */
1267 			info->width  = image->width  = any_image->width;
1268 			info->height = image->height = any_image->height;
1269 
1270 			/*****
1271 			* keep requested dimensions if we have them. Set to real
1272 			* image dimensions otherwise.
1273 			*****/
1274 			if(!ImageHasDimensions(image))
1275 			{
1276 				image->swidth  = image->width;
1277 				image->sheight = image->height;
1278 			}
1279 		}
1280 		/* we have dimensions and we may scale */
1281 		else
1282 		{
1283 			/* store used image dimensions */
1284 			info->width  = image->width  = image->swidth;
1285 			info->height = image->height = image->sheight;
1286 
1287 			/* check see if we really need to scale */
1288 			if(image->swidth != any_image->width ||
1289 				image->sheight != any_image->height)
1290 			{
1291 				/* we need to scale */
1292 				any_image->is_scaled = True;
1293 				any_image->scaled_data =
1294 					(Byte*)malloc(image->swidth * image->sheight);
1295 			}
1296 		}
1297 
1298 		/*
1299 		* Update imageWord dimensions as well.
1300 		* owner can be NULL if this is the body image.
1301 		*/
1302 		if(image->owner != NULL && image->owner->words != NULL &&
1303 			image->owner->words[0].image == image)
1304 		{
1305 			/*
1306 			* If the owning word had it's dimensions wrong (not set before)
1307 			* we need to recompute the screen layout.
1308 			*/
1309 			if(image->owner->words[0].width != image->width ||
1310 				image->owner->words[0].height != image->height)
1311 			{
1312 
1313 				/* store new image dimensions */
1314 				image->owner->words[0].width = image->width;
1315 				image->owner->words[0].height = image->height;
1316 
1317 				/* redo screen layout as it will be incorrectly by now */
1318 				need_redisplay = True;
1319 			}
1320 		}
1321 
1322 		/* allocate pixmaps */
1323 		if((any_image->pixmap = XCreatePixmap(dpy, win, image->width,
1324 			image->height, html->html.xcc->visualInfo->depth)) == None)
1325 		{
1326 			_XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_Transfer"),
1327 				XMHTML_MSG_66, plc->url);
1328 			plc->plc_status = PLC_ABORT;
1329 			return;
1330 		}
1331 		/*****
1332 		* pre-tile the pixmap with the body image (if this isn't the body
1333 		* image of course)
1334 		*****/
1335 		if(html->html.body_image && !ImageIsBackground(html->html.body_image)
1336 			&& !ImageDelayedCreation(html->html.body_image) &&
1337 			BodyImageLoaded(html->html.body_image->html_image))
1338 		{
1339 			XGCValues values;
1340 			unsigned long valuemask;
1341 			int tile_width, tile_height, x_dist, y_dist, ntiles_x, ntiles_y;
1342 			int x_offset, y_offset, tsx, tsy, xs, ys;
1343 
1344 			tile_width  = html->html.body_image->width;
1345 			tile_height = html->html.body_image->height;
1346 
1347 			/* compute correct image offsets */
1348 			xs = image->owner->words[0].x - html->html.scroll_x;
1349 			ys = image->owner->words[0].y - html->html.scroll_y;
1350 
1351 			x_dist = html->html.scroll_x + xs;
1352 			y_dist = html->html.scroll_y + ys;
1353 
1354 			ntiles_x = (int)(x_dist/tile_width);
1355 			ntiles_y = (int)(y_dist/tile_height);
1356 
1357 			x_offset = x_dist - ntiles_x * tile_width;
1358 			y_offset = y_dist - ntiles_y * tile_height;
1359 
1360 			tsx = xs - x_offset;
1361 			tsy = ys - y_offset;
1362 
1363 			valuemask = GCTile|GCFillStyle|GCTileStipXOrigin|GCTileStipYOrigin;
1364 			values.fill_style = FillTiled;
1365 			values.tile = html->html.body_image->pixmap;
1366 			values.ts_x_origin = tsx;
1367 			values.ts_y_origin = tsy;
1368 
1369 			XChangeGC(dpy, html->html.bg_gc, valuemask, &values);
1370 
1371 			/* a plain fillrect will redraw the background portion */
1372 			XFillRectangle(dpy, any_image->pixmap, html->html.bg_gc, 0, 0,
1373 				image->width, image->height);
1374 		}
1375 		else
1376 		{
1377 			/* fill it with the current background for now */
1378 			XSetForeground(dpy, html->html.gc, html->html.body_bg);
1379 			XFillRectangle(dpy, any_image->pixmap, html->html.gc, 0, 0,
1380 				image->width, image->height);
1381 			XSetForeground(dpy, html->html.gc, html->html.body_fg);
1382 		}
1383 
1384 		image->pixmap = any_image->pixmap;
1385 
1386 		/* allocate fully transparent clipmask */
1387 		if(any_image->transparency == XmIMAGE_TRANSPARENCY_BG)
1388 		{
1389 			int clipsize = 0;
1390 			info->options |= XmIMAGE_CLIPMASK;
1391 
1392 			/* compute amount of data required for this clipmask */
1393 			i = image->width;
1394 
1395 			/* make it byte-aligned */
1396 			while((i % 8))
1397 				i++;
1398 
1399 			/* this many bytes on a row */
1400 			i /= 8;
1401 
1402 			/* size of clipmask */
1403 			clipsize = i * image->height;
1404 
1405 			/* raw clipmask data */
1406 			any_image->clip_data = (Byte*)calloc(clipsize, sizeof(Byte));
1407 			/* fully transparent clipmask */
1408 			any_image->clipmask = XCreatePixmapFromBitmapData(dpy, win,
1409 				(char*)any_image->clip_data, image->width, image->height,
1410 				1, 0, 1);
1411 		}
1412 		/* destination clipmask */
1413 		image->clip = any_image->clipmask;
1414 		/* temporary clipmask data */
1415 		info->clip  = any_image->clip_data;
1416 
1417 		/* allocate image RGB values */
1418 		info->options |= XmIMAGE_RGB_SINGLE;
1419 		info->reds   = (Dimension*)calloc(3 * any_image->cmapsize,
1420 						sizeof(Dimension));
1421 		info->greens = info->reds   + any_image->cmapsize;
1422 		info->blues  = info->greens + any_image->cmapsize;
1423 
1424 		/* reset used colors array */
1425 		for(i = 0; i < XmHTML_MAX_IMAGE_COLORS; i++)
1426 		{
1427 			any_image->used[i] = 0;
1428 			any_image->xcolors[i] = 0L;
1429 		}
1430 
1431 		any_image->nused = 1;
1432 
1433 		/* update all copies of this image */
1434 		if(image->child)
1435 			_XmHTMLImageUpdateChilds(image);
1436 
1437 		/* create a working XImage for this plc */
1438 		if(any_image->ximage == NULL)
1439 		{
1440 			any_image->ximage = _XmHTMLCreateXImage(html, html->html.xcc,
1441 				image->width, image->height, plc->url);
1442 
1443 			if(any_image->ximage == NULL)
1444 			{
1445 				plc->plc_status = PLC_ABORT;
1446 				return;
1447 			}
1448 		}
1449 
1450 		/* redo screen layout as it will be incorrect by now */
1451 		if(need_redisplay)
1452 			XmHTMLRedisplay((Widget)html);
1453 	}
1454 
1455 	/*****
1456 	* Always get used image dimensions.
1457 	*****/
1458 	width  = image->width;
1459 	height = image->height;
1460 
1461 	/*****
1462 	* Step 1: color usage
1463 	*****/
1464 
1465 	/* last known processed data */
1466 	ptr = any_image->data + any_image->prev_pos;
1467 	/* last known index of allocated colors */
1468 	col_cnt = any_image->nused;
1469 
1470 	/* store pixel indices */
1471 	for(i = any_image->prev_pos; i < any_image->data_pos; i++, ptr++)
1472 	{
1473 		if(any_image->used[(int)*ptr] == 0)
1474 		{
1475 			any_image->used[(int)*ptr] = col_cnt;
1476 			col_cnt++;
1477 		}
1478 	}
1479 	col_cnt--;
1480 
1481 	/*****
1482 	* Now go and fill the RGB arrays. Only do this if the new chunk of data
1483 	* contains new colors.
1484 	*****/
1485 	if(any_image->nused != col_cnt+1)
1486 	{
1487 		memset(pixels, 0, XmHTML_MAX_IMAGE_COLORS*sizeof(Boolean));
1488 		for(i = 0; i < XmHTML_MAX_IMAGE_COLORS; i++)
1489 		{
1490 			int indx;
1491 
1492 			if(any_image->used[i] != 0)
1493 			{
1494 				indx = any_image->used[i] - 1;
1495 				pixels[indx] = True;
1496 				info->reds[indx]   = any_image->cmap[i].red;
1497 				info->greens[indx] = any_image->cmap[i].green;
1498 				info->blues[indx]  = any_image->cmap[i].blue;
1499 			}
1500 		}
1501 	}
1502 
1503 	/*****
1504 	* Step 2a: image scaling (optional)
1505 	*****/
1506 	if(any_image->is_scaled)
1507 	{
1508 		Byte *img_data, *ilptr, *ipptr, *elptr, *epptr;
1509 		int ix, iy, ex, ey, src_w, src_h;
1510 
1511 		/* get ptr to scaled image data */
1512 		data = any_image->scaled_data;
1513 		img_data = any_image->data;
1514 
1515 		/* get real image dimensions */
1516 		src_w = any_image->width;
1517 		src_h = any_image->height;
1518 
1519 		/* initialize scaling */
1520 		elptr = epptr = data;
1521 
1522 		/* starting scanline index */
1523 		ey = any_image->sc_start/width;
1524 
1525 		/* scaling is done from top to bottom, left to right */
1526 		for(; ey < height; ey++, elptr += width)
1527 		{
1528 			/* vertical pixel skip */
1529 			iy = (src_h * ey) / height;
1530 			epptr = elptr;
1531 			ilptr = img_data + (iy * src_w);
1532 			/* don't overrun */
1533 			if(iy*src_w > any_image->data_pos)
1534 				break;
1535 			for(ex = 0; ex < width; ex++, epptr++)
1536 			{
1537 				/* horizontal pixel skip */
1538 				ix = (src_w * ex) / width;
1539 				ipptr = ilptr + ix;
1540 				*epptr = *ipptr;
1541 			}
1542 		}
1543 		/* update scaled data end position */
1544 		any_image->sc_end = ey*width;
1545 	}
1546 
1547 	/*****
1548 	* Step 2b: clipmask creation (optional)
1549 	* We just redo the entire image.
1550 	*****/
1551 	if(any_image->transparency == XmIMAGE_TRANSPARENCY_BG)
1552 	{
1553 		int j, bcnt;
1554 		Byte *cptr = any_image->clip_data;
1555 		Window win;
1556 
1557 		if(any_image->is_scaled)
1558 		{
1559 			ptr = any_image->scaled_data;
1560 			/* last known processed scanline */
1561 			lo  = any_image->sc_start/width;
1562 			/* maximum scanline on this pass */
1563 			hi  = any_image->sc_end/width;
1564 		}
1565 		else
1566 		{
1567 			ptr = any_image->data;
1568  			hi = (any_image->data_pos)/width;
1569 			lo = (any_image->prev_pos)/width;
1570 		}
1571 
1572 		/* pick up were we left off. Saves prev_pos conditionals */
1573 		for(i = 0; i < lo; i++)
1574 		{
1575 			for(j = 0, bcnt = 0; j < width; j++)
1576 			{
1577 				if((bcnt % 8) == 7 || j == (width-1))
1578 					cptr++;
1579 				bcnt++;
1580 				ptr++;
1581 			}
1582 		}
1583 
1584 		/* process next amount of data */
1585 		for(i = lo; i < hi; i++)
1586 		{
1587 			for(j = 0, bcnt = 0; j < width; j ++)
1588 			{
1589 				if(*ptr != any_image->bg_pixel)
1590 					*cptr += bitmap_bits[(bcnt % 8)];
1591 				if((bcnt % 8) == 7 || j == (width-1))
1592 					cptr++;
1593 				bcnt++;
1594 				ptr++;
1595 			}
1596 		}
1597 		/* destroy existing bitmap */
1598 		if(any_image->clipmask != None)
1599 			XFreePixmap(dpy, any_image->clipmask);
1600 
1601 		/* if we are realized we have a window */
1602 		if(XtIsRealized((Widget)html))
1603 			win = XtWindow(html->html.work_area);
1604 		else
1605 			win = DefaultRootWindow(dpy);
1606 
1607 		/* create new one */
1608 		any_image->clipmask = XCreatePixmapFromBitmapData(dpy, win,
1609 				(char*)any_image->clip_data, width, height, 1, 0, 1);
1610 		/* save it */
1611 		image->clip = any_image->clipmask;
1612 
1613 		/* update child copies */
1614 		if(image->child)
1615 			_XmHTMLImageUpdateChilds(image);
1616 	}
1617 
1618 	/*****
1619 	* Step 3: image pixelization.
1620 	* Replaces each pixel value in the decoded image data with the
1621 	* indices in our own pixel array.
1622 	* Needs to be done after clipmask creation as this modifies the image
1623 	* data.
1624 	*****/
1625 
1626 	/* last known processed data */
1627 	ptr = any_image->data + any_image->prev_pos;
1628 	for(i = any_image->prev_pos; i < any_image->data_pos; i++)
1629 	{
1630 		*ptr = (Byte)(any_image->used[(int)*ptr] - 1);
1631 		ptr++;
1632 	}
1633 
1634 	/*****
1635 	* Step 4: color allocation.
1636 	* Only allocate colors if the new chunk of data contains colors that
1637 	* haven't been allocated yet.
1638 	*****/
1639 	if(any_image->nused != col_cnt+1)
1640 	{
1641 		_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, allocating %i additional "
1642 			"colors (%i total)\n", col_cnt - any_image->nused + 1, col_cnt));
1643 
1644 		npixels = image->npixels;
1645 		XCCGetPixelsIncremental(html->html.xcc, info->reds, info->greens,
1646 			info->blues, any_image->cmapsize, pixels, any_image->xcolors,
1647 			&npixels);
1648 
1649 		_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, after allocation, npixels "
1650 			": %i\n", npixels));
1651 
1652 		/* update used color count */
1653 		any_image->nused = col_cnt+1;
1654 		image->npixels = npixels;
1655 	}
1656 
1657 	/*****
1658 	* Step 5: XImage updating.
1659 	* We have our colors, now do the ximage. If the image is being scaled,
1660 	* use the pre-scaled image data, else use the real image data.
1661 	*****/
1662 	if(any_image->is_scaled)
1663 	{
1664 		data = any_image->scaled_data + any_image->sc_start;
1665 		lo = any_image->sc_start;
1666 		hi = any_image->sc_end;
1667 	}
1668 	else
1669 	{
1670 		data = any_image->data + any_image->prev_pos;
1671 		lo = any_image->prev_pos;
1672 		hi = any_image->data_pos;
1673 	}
1674 
1675 	_XmHTMLFillXImage(html, any_image->ximage, html->html.xcc, data,
1676 		any_image->xcolors, &lo, &hi);
1677 
1678 	/*****
1679 	* XImage updated, make all positions aligned on completed scanline
1680 	* boundaries.
1681 	*****/
1682 	any_image->prev_pos = (any_image->data_pos/any_image->width) *
1683 		any_image->width;
1684 
1685 	/* current scanline */
1686 	ypos = lo/width;
1687 
1688 	/* no of scanlines added on this pass */
1689 	nlines = (hi - lo)/width;
1690 
1691 	/* check if we aren't exceeding height of the image */
1692 	my_assert((ypos + nlines <= height));
1693 
1694 	/*****
1695 	* Step 6: destination updating.
1696 	* XImage data processed, copy newly added scanlines to the destination
1697 	* pixmap.
1698 	*****/
1699 	_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Transfer, updating image between "
1700 		"scanline %i to %i\n", ypos, ypos + nlines));
1701 
1702 	/* update pixmap */
1703 	XPutImage(dpy, any_image->pixmap, html->html.plc_gc, any_image->ximage,
1704 		 0, ypos, 0, ypos, width, nlines);
1705 
1706 	/*****
1707 	* Step 7: display update. The first call updates the master image and
1708 	*         the second call updates any copies of this image.
1709 	* Safety check: only do it when the image has an owner (e.i., it isn't
1710 	* the background image).
1711 	*****/
1712 	if(image->owner)
1713 	{
1714 		_PLC_IMG_UpdateScreen(html, image, image->owner, ypos, nlines);
1715 		_PLC_IMG_UpdateScreenCopies(html, image, ypos, nlines);
1716 	}
1717 
1718 	/* all done */
1719 	return;
1720 }
1721 
1722 /*****
1723 * Name: 		_PLC_IMG_Finalize
1724 * Return Type:	void
1725 * Description:	Image PLC final transfer function
1726 * In:
1727 *	plc:		current PLC
1728 * Returns:
1729 *	nothing.
1730 *****/
1731 static void
_PLC_IMG_Finalize(PLC * plc)1732 _PLC_IMG_Finalize(PLC *plc)
1733 {
1734 	PLCImage *any_image;
1735 	XmHTMLWidget html;
1736 	XmImageInfo *info;
1737 	XmHTMLImage *image;
1738 
1739 	/* obj_set will be false if this PLC was terminated during init phase */
1740 	if(plc == NULL || !plc->obj_set)
1741 		return;
1742 
1743 	any_image = &(plc->object->plc_any_image);
1744 	html   = any_image->owner;
1745 	info   = any_image->info;
1746 	image  = any_image->image;
1747 
1748 	_XmHTMLDebug(14, ("plc.c: _PLC_IMG_Finalize for %s\n", plc->url));
1749 
1750 	/* this must *always* be destroyed */
1751 	if(any_image->ximage)
1752 		XDestroyImage(any_image->ximage);
1753 
1754 	/* no longer need the scaled data, image has been fully processed */
1755 	if(any_image->is_scaled)
1756 	{
1757 		free(any_image->scaled_data);
1758 		any_image->scaled_data = (Byte*)NULL;
1759 		any_image->is_scaled = False;
1760 	}
1761 
1762 	/*****
1763 	* Transfer stuff to the info structure. We *need* to test the
1764 	* existence of info 'cause it might disappear when this PLC is aborted.
1765 	*****/
1766 	if(info)
1767 	{
1768 		info->data         = any_image->data;		/* decoded image data */
1769 		info->clip         = any_image->clip_data;	/* raw clipmask data */
1770 		info->bg           = any_image->bg_pixel;	/* background pixel index */
1771 		info->colorspace   = any_image->colorclass;	/* image colorclass */
1772 		info->transparency = any_image->transparency;/* image transparency */
1773 		info->depth        = any_image->depth;		/* image depth */
1774 		info->ncolors      = any_image->nused-1;	/* no of colors in image */
1775 		info->scolors      = any_image->ncolors;	/* original no of cols */
1776 		info->width        = any_image->width;		/* reset to real width */
1777 		info->height       = any_image->height;		/* reset to real height */
1778 
1779 		/* this image is no longer being loaded progressively */
1780 		info->options &= ~XmIMAGE_PROGRESSIVE;
1781 
1782 		/*****
1783 		* Adjust image RGB components to only contain the number of colors
1784 		* used in this image.
1785 		* Only do it when we have allocated any colors for this image (we
1786 		* didn't allocate colors if this PLC was aborted prematurely).
1787 		*****/
1788 		if(info->ncolors && info->reds != NULL &&
1789 			info->ncolors != any_image->cmapsize)
1790 		{
1791 			/* temporary storage */
1792 			Dimension *reds, *greens, *blues;
1793 
1794 			/* save old RGB arrays */
1795 			reds   = info->reds;
1796 			greens = info->greens;
1797 			blues  = info->blues;
1798 
1799 			/* allocate new RGB arrays */
1800 			info->reds   = (Dimension*)calloc(3 * info->ncolors,
1801 								sizeof(Dimension));
1802 			info->greens = info->reds   + info->ncolors;
1803 			info->blues  = info->greens + info->ncolors;
1804 
1805 			/* copy old RGB arrays */
1806 			info->reds   = (Dimension*)memcpy(info->reds, reds,
1807 								info->ncolors*sizeof(Dimension));
1808 			info->greens = (Dimension*)memcpy(info->greens, greens,
1809 								info->ncolors*sizeof(Dimension));
1810 			info->blues  = (Dimension*)memcpy(info->blues, blues,
1811 								info->ncolors*sizeof(Dimension));
1812 
1813 			/* free old RGB arrays */
1814 			free(reds);
1815 
1816 			/* update this as well */
1817 			info->scolors = info->ncolors;
1818 		}
1819 	}
1820 
1821 	/* update all copies of this image */
1822 	if(image != NULL)
1823 	{
1824 		/* no longer progressive */
1825 		image->options &= ~IMG_PROGRESSIVE;
1826 		if(image->child != NULL)
1827 			_XmHTMLImageUpdateChilds(image);
1828 	}
1829 
1830 	/* free remaining PLC resources */
1831 	if(any_image->cmap)
1832 		free(any_image->cmap);
1833 	if(any_image->bg_cmap)
1834 		free(any_image->bg_cmap);
1835 	if(any_image->buffer)
1836 		free(any_image->buffer);
1837 
1838 	/*****
1839 	* last and final check: if this is the body image, clear the display
1840 	* area so it will get redrawn properly. Don't do it when we haven't
1841 	* got a GC: we do not yet have a window then.
1842 	*****/
1843 	if(image && ImageIsBackground(image) && html->html.gc != NULL)
1844 	{
1845 		XClearArea(XtDisplay(html->html.work_area),
1846 			XtWindow(html->html.work_area), 0, 0, html->core.width,
1847 			html->core.height, True);
1848 	}
1849 	/* make sure we are updated */
1850 	XmUpdateDisplay((Widget)html);
1851 	return;
1852 }
1853 
1854 /*****
1855 * Name: 		_PLC_IMG_UpdateScreen
1856 * Return Type: 	void
1857 * Description: 	Image PLC image->screen transfer function
1858 * In:
1859 *	html:		XmHTMLWidget id;
1860 *	image:		image data;
1861 *	elePtr:		image owner;
1862 *	src_y:		starting scanline index;
1863 *	height:		number of scanlines to render;
1864 * Returns:
1865 *	nothing.
1866 *****/
1867 static void
_PLC_IMG_UpdateScreen(XmHTMLWidget html,XmHTMLImage * image,XmHTMLObjectTableElement elePtr,int src_y,Dimension height)1868 _PLC_IMG_UpdateScreen(XmHTMLWidget html, XmHTMLImage *image,
1869 	XmHTMLObjectTableElement elePtr, int src_y, Dimension height)
1870 {
1871 	int tmp = image->height;
1872 
1873 	/* set bogus image height */
1874 	image->height = height;
1875 	/* paint it */
1876 	_XmHTMLDrawImage(html, elePtr, src_y, False);
1877 	/* restore real image height */
1878 	image->height = tmp;
1879 }
1880 
1881 /*****
1882 * Name:			_PLC_IMG_UpdateScreenCopies
1883 * Return Type: 	void
1884 * Description: 	updates the screen for each copy of the given image.
1885 * In:
1886 *	html:		XmHTMLWidget id;
1887 *	image:		parent image;
1888 *	src_y:		starting scanline index;
1889 *	height:		number of scanlines to render;
1890 * Returns:
1891 *	nothing.
1892 *****/
1893 static void
_PLC_IMG_UpdateScreenCopies(XmHTMLWidget html,XmHTMLImage * image,int src_y,Dimension height)1894 _PLC_IMG_UpdateScreenCopies(XmHTMLWidget html, XmHTMLImage *image, int src_y,
1895 	Dimension height)
1896 {
1897 	XmHTMLImage *tmp;
1898 
1899 	/* walk the list of child images and call UpdateScreen for each copy */
1900 	for(tmp = image->child; tmp != NULL; tmp = tmp->child)
1901 	{
1902 		if(tmp->owner)
1903 			_PLC_IMG_UpdateScreen(html, tmp, tmp->owner, src_y, height);
1904 	}
1905 }
1906 
1907 /***************
1908 ***** Document PLC object and supporting functions
1909 ***************/
1910 
1911 static void
_PLC_DOC_Init(PLC * plc)1912 _PLC_DOC_Init(PLC *plc)
1913 {
1914 	_XmHTMLDebug(14, ("plc.c: _PLC_DOC_Init for %s\n", plc->url));
1915 	return;
1916 }
1917 
1918 static void
_PLC_DOC_Transfer(PLC * plc)1919 _PLC_DOC_Transfer(PLC *plc)
1920 {
1921 	_XmHTMLDebug(14, ("plc.c: _PLC_DOC_Transfer for %s\n", plc->url));
1922 	plc->obj_set = True;
1923 	return;
1924 }
1925 
1926 static void
_PLC_DOC_Finalize(PLC * plc)1927 _PLC_DOC_Finalize(PLC *plc)
1928 {
1929 	_XmHTMLDebug(14, ("plc.c: _PLC_DOC_Finalize for %s\n", plc->url));
1930 	plc->obj_set = True;
1931 	return;
1932 }
1933 
1934 /***************
1935 ***** Unknown PLC object and supporting functions
1936 ***************/
1937 static void
_PLC_ANY_Init(PLC * plc)1938 _PLC_ANY_Init(PLC *plc)
1939 {
1940 	_XmHTMLDebug(14, ("plc.c: _PLC_ANY_Init for %s\n", plc->url));
1941 	return;
1942 }
1943 
1944 static void
_PLC_ANY_Transfer(PLC * plc)1945 _PLC_ANY_Transfer(PLC *plc)
1946 {
1947 	_XmHTMLDebug(14, ("plc.c: _PLC_ANY_Transfer for %s\n", plc->url));
1948 	return;
1949 }
1950 
1951 static void
_PLC_ANY_Finalize(PLC * plc)1952 _PLC_ANY_Finalize(PLC *plc)
1953 {
1954 	_XmHTMLDebug(14, ("plc.c: _PLC_ANY_Finalize for %s\n", plc->url));
1955 	return;
1956 }
1957 
1958 /********
1959 ****** Public Functions
1960 ********/
1961 
1962 /* suspend progressive image loading */
1963 void
XmHTMLImageProgressiveSuspend(Widget w)1964 XmHTMLImageProgressiveSuspend(Widget w)
1965 {
1966 	XmHTMLWidget html;
1967 	PLC *plc;
1968 	int i;
1969 
1970 	if(w == NULL || !XmIsHTML(w))
1971 	{
1972 		_XmHTMLBadParent(w, "ImageProgressiveSuspend");
1973 		return;
1974 	}
1975 	html = (XmHTMLWidget)w;
1976 
1977 	/* nothing to suspend */
1978 	if((plc = html->html.plc_buffer) == NULL)
1979 		return;
1980 
1981 	/* first suspend all active PLC's. Don't mess with other PLC states. */
1982 	for(i = 0; i < html->html.num_plcs; plc = plc->next_plc, i++)
1983 	{
1984 		if(plc->plc_status == PLC_ACTIVE)
1985 			plc->plc_status = PLC_SUSPEND;
1986 	}
1987 
1988 	if(html->html.plc_proc_id)
1989 	{
1990 #ifdef PLC_WORKPROCS
1991 		XtRemoveWorkProc((XtWorkProcId)html->html.plc_proc_id);
1992 #else
1993 		XtRemoveTimeOut(html->html.plc_proc_id);
1994 #endif
1995 		html->html.plc_proc_id = None;
1996 	}
1997 	/* set global PLC suspension flag */
1998 	html->html.plc_suspended = True;
1999 }
2000 
2001 /* reactivate progressive image loading */
2002 void
XmHTMLImageProgressiveContinue(Widget w)2003 XmHTMLImageProgressiveContinue(Widget w)
2004 {
2005 	XmHTMLWidget html;
2006 	PLC *plc;
2007 	int i;
2008 
2009 	if(w == NULL || !XmIsHTML(w))
2010 	{
2011 		_XmHTMLBadParent(w, "ImageProgressiveContinue");
2012 		return;
2013 	}
2014 	html = (XmHTMLWidget)w;
2015 
2016 	/* nothing to do */
2017 	if((plc = html->html.plc_buffer) == NULL)
2018 		return;
2019 
2020 	/* first activate all suspended PLC's. Don't mess with other PLC states. */
2021 	for(i = 0; i < html->html.num_plcs; plc = plc->next_plc, i++)
2022 	{
2023 		if(plc->plc_status == PLC_SUSPEND)
2024 			plc->plc_status = PLC_ACTIVE;
2025 	}
2026 
2027 	/* reactivate cycler */
2028 	html->html.plc_suspended = False;
2029 	_XmHTMLPLCCycler((XtPointer)html, NULL);
2030 }
2031 
2032 /* terminate progressive image loading */
2033 void
XmHTMLImageProgressiveKill(Widget w)2034 XmHTMLImageProgressiveKill(Widget w)
2035 {
2036 	XmHTMLWidget html;
2037 
2038 	if(w == NULL || !XmIsHTML(w))
2039 	{
2040 		_XmHTMLBadParent(w, "ImageProgressiveKill");
2041 		return;
2042 	}
2043 	html = (XmHTMLWidget)w;
2044 
2045 	if(html->html.plc_buffer == NULL)
2046 		return;
2047 
2048 	/* kill the bastards! */
2049 	html->html.plc_suspended = True;
2050 	_XmHTMLKillPLCCycler(html);
2051 }
2052 
2053 /*****
2054 * Name: 		_XmHTMLPLCCheckIntervals
2055 * Return Type: 	void
2056 * Description: 	validates the delays for the PLC Cycler.
2057 * In:
2058 *	html:		XmHTMLWidget id
2059 * Returns:
2060 *	nothing, but the PLC delay values can have changed when this function
2061 *	returns.
2062 *****/
2063 void
_XmHTMLPLCCheckIntervals(XmHTMLWidget html)2064 _XmHTMLPLCCheckIntervals(XmHTMLWidget html)
2065 {
2066 	int delay, min_delay, max_delay, new_delay;
2067 	Boolean delay_reset = False;
2068 
2069 	delay = HTML_ATTR(plc_delay);
2070 	min_delay = HTML_ATTR(plc_min_delay);
2071 	max_delay = HTML_ATTR(plc_max_delay);
2072 
2073 	if(min_delay <= 0)
2074 	{
2075 		_XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), XMHTML_MSG_16,
2076 			"Minimum", min_delay, PLC_MIN_DELAY);
2077 		min_delay = PLC_MIN_DELAY;
2078 	}
2079 
2080 	if(delay < min_delay)
2081 	{
2082 		if(min_delay < PLC_DEFAULT_DELAY)
2083 			new_delay = PLC_DEFAULT_DELAY;
2084 		else
2085 			new_delay = min_delay * 50;
2086 
2087 		_XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), XMHTML_MSG_16,
2088 			"Initial", delay, new_delay);
2089 		delay = new_delay;
2090 		delay_reset = True;
2091 	}
2092 
2093 	if(max_delay <= min_delay)
2094 	{
2095 		new_delay = min_delay <= PLC_MAX_DELAY ?
2096 					PLC_MAX_DELAY : min_delay * 100;
2097 
2098 		_XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), XMHTML_MSG_17,
2099 			max_delay, "Minimum", min_delay, new_delay);
2100 		max_delay = new_delay;
2101 	}
2102 
2103 	/* can't do anything with this, reset to default values */
2104 	if(max_delay <= delay && !delay_reset)
2105 	{
2106 		_XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), XMHTML_MSG_17,
2107 			max_delay, "Initial", min_delay, PLC_MAX_DELAY);
2108 
2109 		delay     = PLC_DEFAULT_DELAY;
2110 		min_delay = PLC_MIN_DELAY;
2111 		max_delay = PLC_MAX_DELAY;
2112 	}
2113 
2114 	HTML_ATTR(plc_delay) = HTML_ATTR(plc_def_delay) = delay;
2115 	HTML_ATTR(plc_min_delay) = min_delay;
2116 	HTML_ATTR(plc_max_delay) = max_delay;
2117 }
2118