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