maug
Quick and dirty C mini-augmentation library.
retrohtr.h
Go to the documentation of this file.
1
2#ifndef RETROHTR_H
3#define RETROHTR_H
4
11#define RETROHTR_TREE_FLAG_GUI_ACTIVE 1
12
13#define RETROHTR_NODE_FLAG_DIRTY 2
14
15#ifndef RETROHTR_RENDER_NODES_INIT_SZ
16# define RETROHTR_RENDER_NODES_INIT_SZ 10
17#endif /* !RETROHTR_RENDER_NODES_INIT_SZ */
18
19#ifndef RETROHTR_TRACE_LVL
20# define RETROHTR_TRACE_LVL 0
21#endif /* !RETROHTR_TRACE_LVL */
22
23#define RETROHTR_EDGE_UNKNOWN 0
24#define RETROHTR_EDGE_LEFT 1
25#define RETROHTR_EDGE_TOP 2
26#define RETROHTR_EDGE_INSIDE 4
27
29 uint8_t flags;
30 /* TODO: Maybe get rid of these and replace them with MCSS_STYLE node? */
39 uint8_t pos;
40 uint8_t pos_flags;
41 uint8_t edge;
44#ifdef RETROGXC_PRESENT
45 ssize_t font_idx;
46#else
47 MAUG_MHANDLE font_h;
48#endif /* RETROGXC_PRESENT */
49 ssize_t tag;
51 ssize_t parent;
53 ssize_t first_child;
55 ssize_t next_sibling;
56 struct RETROFLAT_BITMAP bitmap;
57};
58
60 uint8_t flags;
61 MAUG_MHANDLE nodes_h;
65 size_t nodes_sz;
68 struct RETROGUI gui;
69};
70
71/* TODO: Function names should be verb_noun! */
72
73#define retrohtr_node( tree, idx ) \
74 (0 <= (ssize_t)idx ? &((tree)->nodes[idx]) : NULL)
75
76#define retrohtr_node_parent( tree, idx ) \
77 (0 <= idx && 0 <= (tree)->nodes[idx].parent ? \
78 &((tree)->nodes[(tree)->nodes[idx].parent]) : NULL)
79
80#define retrohtr_tree_lock( tree ) \
81 if( NULL == (tree)->nodes ) { \
82 maug_mlock( (tree)->nodes_h, (tree)->nodes ); \
83 maug_cleanup_if_null_alloc( struct RETROHTR_RENDER_NODE*, (tree)->nodes ); \
84 }
85
86#define retrohtr_tree_unlock( tree ) \
87 if( NULL != (tree)->nodes ) { \
88 maug_munlock( (tree)->nodes_h, (tree)->nodes ); \
89 }
90
91#define retrohtr_tree_is_locked( tree ) (NULL != (tree)->nodes)
92
93/* TODO: Make these offset by element scroll on screen. */
94
95#define retrohtr_node_screen_x( tree, node_idx ) \
96 ((tree)->nodes[node_idx].x)
97
98#define retrohtr_node_screen_y( tree, node_idx ) \
99 ((tree)->nodes[node_idx].y)
100
101void retrohtr_merge_prop(
102 int p_id,
103 const char* prop_name,
104 size_t prop_sz,
105 size_t tag_type,
106 void* parent_prop, uint8_t* parent_prop_flags,
107 void* effect_prop, uint8_t* effect_prop_flags,
108 void* tag_prop, uint8_t* tag_prop_flags );
109
110void retrohtr_merge_styles(
111 struct MCSS_STYLE* effect_style,
112 struct MCSS_STYLE* parent_style,
113 struct MCSS_STYLE* tag_style,
114 size_t tag_type );
115
116MERROR_RETVAL retrohtr_tree_create(
117 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
120 ssize_t tag_idx, ssize_t node_idx, size_t d );
121
134 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
135 struct MCSS_STYLE* parent_style, struct MCSS_STYLE* effect_style,
136 ssize_t tag_idx );
137
138MERROR_RETVAL retrohtr_tree_size(
139 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
140 struct MCSS_STYLE* prev_sibling_style,
141 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d );
142
143MERROR_RETVAL retrohtr_tree_pos(
144 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
145 struct MCSS_STYLE* prev_sibling_style,
146 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d );
147
148MERROR_RETVAL retrohtr_tree_draw(
149 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
150 ssize_t node_idx, size_t d );
151
152retrogui_idc_t retrohtr_tree_poll_ctls(
153 struct RETROHTR_RENDER_TREE* tree,
154 RETROFLAT_IN_KEY* input,
155 struct RETROFLAT_INPUT* input_evt );
156
157MERROR_RETVAL retrohtr_tree_dump(
158 struct RETROHTR_RENDER_TREE* tree, struct MHTML_PARSER* parser,
159 ssize_t iter, size_t d );
160
161void retrohtr_tree_free( struct RETROHTR_RENDER_TREE* tree );
162
163MERROR_RETVAL retrohtr_tree_init( struct RETROHTR_RENDER_TREE* tree );
164
165#ifdef RETROHTR_C
166
167void retrohtr_merge_prop(
168 int p_id,
169 const char* prop_name,
170 size_t prop_sz,
171 size_t tag_type,
172 void* parent_prop, uint8_t* parent_prop_flags,
173 void* effect_prop, uint8_t* effect_prop_flags,
174 void* tag_prop, uint8_t* tag_prop_flags
175) {
176 if(
177 (
178 /* Only do inheritence for some special cases.
179 * e.g. We don't want to inherit width/height/X/etc! */
180 mcss_prop_is_heritable( p_id )
181 ) && (NULL != parent_prop && (
182 (
183 NULL != tag_prop &&
184 /* Parent is important and new property isn't. */
185 mcss_prop_is_active_flag( *parent_prop, IMPORTANT ) &&
186 /* TODO: Is not active OR important? */
187 !mcss_prop_is_important( *tag_prop ) &&
188 !mcss_prop_is_important( *effect_prop )
189 ) || (
190 NULL != tag_prop &&
191 /* New property is not active. */
192 !mcss_prop_is_active( *tag_prop ) &&
193 !mcss_prop_is_active( *effect_prop )
194 ) || (
195 /* No competition. */
196 NULL == tag_prop &&
197 !mcss_prop_is_active( *effect_prop )
198 )
199 ))
200 ) {
201 /* Inherit parent property. */
202 if( MCSS_PROP_BACKGROUND_COLOR == p_id ) {
203#if RETROHTR_TRACE_LVL > 0
204 debug_printf( RETROHTR_TRACE_LVL, "background color was %s",
205 0 <= *(RETROFLAT_COLOR*)effect_prop ?
206 gc_retroflat_color_names[*(RETROFLAT_COLOR*)effect_prop] : "NULL" );
207#endif /* RETROHTR_TRACE_LVL */
208 } else if( MCSS_PROP_COLOR == p_id ) {
209#if RETROHTR_TRACE_LVL > 0
210 debug_printf( RETROHTR_TRACE_LVL, "color was %s",
211 0 <= *(RETROFLAT_COLOR*)effect_prop ?
212 gc_retroflat_color_names[*(RETROFLAT_COLOR*)effect_prop] : "NULL" );
213#endif /* RETROHTR_TRACE_LVL */
214 }
215#if RETROHTR_TRACE_LVL > 0
216 debug_printf( RETROHTR_TRACE_LVL,
217 "%s using parent %s: " SSIZE_T_FMT,
218 gc_mhtml_tag_names[tag_type], prop_name, *(ssize_t*)parent_prop );
219#endif /* RETROHTR_TRACE_LVL */
220 memcpy( effect_prop, parent_prop, prop_sz );
221 *effect_prop_flags = *parent_prop_flags;
222 if( MCSS_PROP_BACKGROUND_COLOR == p_id ) {
223#if RETROHTR_TRACE_LVL > 0
224 debug_printf( RETROHTR_TRACE_LVL, "background color %s",
225 0 <= *(RETROFLAT_COLOR*)effect_prop ?
226 gc_retroflat_color_names[*(RETROFLAT_COLOR*)effect_prop] : "NULL" );
227#endif /* RETROHTR_TRACE_LVL */
228 } else if( MCSS_PROP_COLOR == p_id ) {
229#if RETROHTR_TRACE_LVL > 0
230 debug_printf( RETROHTR_TRACE_LVL, "color %s",
231 0 <= *(RETROFLAT_COLOR*)effect_prop ?
232 gc_retroflat_color_names[*(RETROFLAT_COLOR*)effect_prop] : "NULL" );
233#endif /* RETROHTR_TRACE_LVL */
234 }
235 } else if(
236 NULL != tag_prop &&
237 mcss_prop_is_active( *tag_prop )
238 ) {
239 /* Use new property. */
240#if RETROHTR_TRACE_LVL > 0
241 debug_printf( RETROHTR_TRACE_LVL, "%s using style %s: " SSIZE_T_FMT,
242 gc_mhtml_tag_names[tag_type], prop_name,
243 *(ssize_t*)tag_prop );
244 if( MCSS_PROP_COLOR == p_id ) {
245 debug_printf( RETROHTR_TRACE_LVL, "color %s",
246 0 <= *(RETROFLAT_COLOR*)effect_prop ?
247 gc_retroflat_color_names[*(RETROFLAT_COLOR*)effect_prop] : "NULL" );
248 }
249#endif /* RETROHTR_TRACE_LVL */
250 memcpy( effect_prop, tag_prop, prop_sz );
251 *effect_prop_flags = *tag_prop_flags;
252 }
253}
254
255/* === */
256
257void retrohtr_merge_styles(
258 struct MCSS_STYLE* effect_style,
259 struct MCSS_STYLE* parent_style,
260 struct MCSS_STYLE* tag_style,
261 size_t tag_type
262) {
263 /* At least one of these styles probably comes from the locked vector of the
264 * CSS styles parser, so... keep that in mind?
265 */
266
267 if(
268 MCSS_STYLE_FLAG_ACTIVE !=
269 (MCSS_STYLE_FLAG_ACTIVE & effect_style->flags)
270 ) {
271 mcss_style_init( effect_style );
272 }
273
274 /* Perform inheritence of special cases. */
275
276 #define MCSS_PROP_TABLE_MERGE( p_id, prop_n, prop_t, prop_p, def ) \
277 retrohtr_merge_prop( p_id, \
278 #prop_n, \
279 sizeof( prop_t ), \
280 tag_type, \
281 NULL != parent_style ? &(parent_style->prop_n) : NULL, \
282 NULL != parent_style ? &(parent_style->prop_n ## _flags) : 0, \
283 NULL != effect_style ? &(effect_style->prop_n) : NULL, \
284 NULL != effect_style ? &(effect_style->prop_n ## _flags) : 0, \
285 NULL != tag_style ? &(tag_style->prop_n) : NULL, \
286 NULL != tag_style ? &(tag_style->prop_n ## _flags) : 0 );
287
288 MCSS_PROP_TABLE( MCSS_PROP_TABLE_MERGE )
289
290 /* Apply defaults for display. */
291
292 if(
293 MCSS_PROP_FLAG_ACTIVE !=
294 (MCSS_PROP_FLAG_ACTIVE & effect_style->DISPLAY_flags)
295 ) {
296 /* Set the display property based on the tag's default. */
297
298 #define MHTML_TAG_TABLE_DISP( tag_id, tag_name, fields, disp ) \
299 } else if( tag_id == tag_type ) { \
300 effect_style->DISPLAY = MCSS_DISPLAY_ ## disp; \
301 debug_printf( RETROHTR_TRACE_LVL, "%s defaulting to %s DISPLAY", \
302 gc_mhtml_tag_names[tag_type], \
303 gc_mcss_display_names[effect_style->DISPLAY] );
304
305 if( 0 ) {
306 MHTML_TAG_TABLE( MHTML_TAG_TABLE_DISP )
307 }
308
309 }
310
311 return;
312}
313
314/* === */
315
316ssize_t retrohtr_get_next_free_node( struct RETROHTR_RENDER_TREE* tree ) {
317 MERROR_RETVAL retval = MERROR_OK;
318 uint8_t auto_unlocked = 0;
319 ssize_t retidx = -1;
320 MAUG_MHANDLE new_nodes_h = (MAUG_MHANDLE)NULL;
321
322 if( NULL != tree->nodes ) {
323#if RETROHTR_TRACE_LVL > 0
324 debug_printf( RETROHTR_TRACE_LVL, "auto-unlocking nodes..." );
325#endif /* RETROHTR_TRACE_LVL */
326 maug_munlock( tree->nodes_h, tree->nodes );
327 auto_unlocked = 1;
328 }
329
330 assert( 0 < tree->nodes_sz_max );
331 assert( NULL == tree->nodes );
332 assert( (MAUG_MHANDLE)NULL != tree->nodes_h );
333 if( tree->nodes_sz_max <= tree->nodes_sz + 1 ) {
334 /* We've run out of nodes, so double the available number. */
335 maug_mrealloc_test( new_nodes_h, tree->nodes_h, tree->nodes_sz_max * 2,
336 sizeof( struct RETROHTR_RENDER_NODE ) );
337 tree->nodes_sz_max *= 2;
338 }
339
340 /* Assume handle is unlocked. */
341 assert( NULL == tree->nodes );
342 maug_mlock( tree->nodes_h, tree->nodes );
343 if( NULL == tree->nodes ) {
344 error_printf( "unable to lock nodes!" );
345 goto cleanup;
346 }
347
348 /* Zero out the last node, add it to the list, and return its index. */
349#if RETROHTR_TRACE_LVL > 0
350 debug_printf( RETROHTR_TRACE_LVL,
351 "zeroing node " SIZE_T_FMT " (of " SIZE_T_FMT ")...",
352 tree->nodes_sz, tree->nodes_sz_max );
353#endif /* RETROHTR_TRACE_LVL */
354 maug_mzero( &(tree->nodes[tree->nodes_sz]),
355 sizeof( struct RETROHTR_RENDER_NODE ) );
356 retidx = tree->nodes_sz;
357 tree->nodes_sz++;
358
359 /* Compensate for cleanup below. */
360 maug_munlock( tree->nodes_h, tree->nodes );
361
362cleanup:
363
364 if( auto_unlocked ) {
365#if RETROHTR_TRACE_LVL > 0
366 debug_printf( RETROHTR_TRACE_LVL, "auto-locking nodes..." );
367#endif /* RETROHTR_TRACE_LVL */
368 maug_mlock( tree->nodes_h, tree->nodes );
369 }
370
371 if( MERROR_OK != retval ) {
372 retidx = merror_retval_to_sz( retval );
373 }
374
375 return retidx;
376}
377
378/* === */
379
380ssize_t retrohtr_add_node_child(
381 struct RETROHTR_RENDER_TREE* tree, ssize_t node_parent_idx
382) {
383 ssize_t node_new_idx = -1,
384 node_sibling_idx = -1;
385
386 node_new_idx = retrohtr_get_next_free_node( tree );
387 if( 0 > node_new_idx ) {
388 goto cleanup;
389 }
390
391#ifdef RETROGXC_PRESENT
392 retrohtr_node( tree, node_new_idx )->font_idx = -1;
393#endif /* RETROGXC_PRESENT */
394 retrohtr_node( tree, node_new_idx )->parent = node_parent_idx;
395 retrohtr_node( tree, node_new_idx )->first_child = -1;
396 retrohtr_node( tree, node_new_idx )->next_sibling = -1;
397
398 if( 0 > node_parent_idx ) {
399 debug_printf(
400 1, "adding root node under " SSIZE_T_FMT "...", node_parent_idx );
401 goto cleanup;
402 } else {
403 debug_printf(
404 1, "adding node " SSIZE_T_FMT " under " SSIZE_T_FMT,
405 node_new_idx, node_parent_idx );
406 }
407
408 /* Add new child under current node. */
409 if( 0 > retrohtr_node( tree, node_parent_idx )->first_child ) {
410#if RETROHTR_TRACE_LVL > 0
411 debug_printf( RETROHTR_TRACE_LVL, "adding first child..." );
412#endif /* RETROHTR_TRACE_LVL */
413 assert( -1 == retrohtr_node( tree, node_parent_idx )->first_child );
414 retrohtr_node( tree, node_parent_idx )->first_child = node_new_idx;
415 } else {
416 assert( NULL != retrohtr_node( tree, node_parent_idx ) );
417 node_sibling_idx = retrohtr_node( tree, node_parent_idx )->first_child;
418 assert( NULL != retrohtr_node( tree, node_sibling_idx ) );
419 while( 0 <= retrohtr_node( tree, node_sibling_idx )->next_sibling ) {
420 node_sibling_idx =
421 retrohtr_node( tree, node_sibling_idx )->next_sibling;
422 }
423 retrohtr_node( tree, node_sibling_idx )->next_sibling = node_new_idx;
424 }
425
426cleanup:
427
428 return node_new_idx;
429}
430
431/* === */
432
433MERROR_RETVAL retrohtr_tree_create(
434 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
437 ssize_t tag_idx, ssize_t node_idx, size_t d
438) {
439 ssize_t node_new_idx = -1;
440 ssize_t tag_iter_idx = -1;
441 MERROR_RETVAL retval = MERROR_OK;
442 union MHTML_TAG* p_tag_iter = NULL;
443 ssize_t tag_next_idx = 0;
444
445#if RETROHTR_TRACE_LVL > 0
446 debug_printf( RETROHTR_TRACE_LVL,
447 "creating render node for tag: " SSIZE_T_FMT, tag_idx );
448#endif /* RETROHTR_TRACE_LVL */
449
450 mdata_vector_lock( &(parser->tags) );
451
452 if( 0 > tag_idx ) {
453 goto cleanup;
454 }
455 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
456 if( NULL == p_tag_iter ) {
457 goto cleanup;
458 }
459
460 /* Make sure we have a single root node. */
461 if( 0 > node_idx ) {
462 assert( MHTML_TAG_TYPE_BODY == p_tag_iter->base.type );
463
464 node_new_idx = retrohtr_add_node_child( tree, node_idx );
465 if( 0 > node_new_idx ) {
466 goto cleanup;
467 }
468#if RETROHTR_TRACE_LVL > 0
469 debug_printf( RETROHTR_TRACE_LVL,
470 "created initial root node: " SIZE_T_FMT, node_new_idx );
471#endif /* RETROHTR_TRACE_LVL */
472
473 node_idx = node_new_idx;
474
475 /* The common root is the body tag. */
476 retrohtr_node( tree, node_idx )->tag = tag_idx;
477
478 retrohtr_node( tree, node_idx )->x = x;
479 retrohtr_node( tree, node_idx )->y = y;
480 retrohtr_node( tree, node_idx )->w = w;
481 retrohtr_node( tree, node_idx )->h = h;
482 }
483
484 tag_iter_idx = p_tag_iter->base.first_child;
485 while( 0 <= tag_iter_idx ) {
486 node_new_idx = retrohtr_add_node_child( tree, node_idx );
487 p_tag_iter = mdata_vector_get(
488 &(parser->tags), tag_iter_idx, union MHTML_TAG );
489 assert( NULL != p_tag_iter );
490 if( 0 > node_new_idx ) {
491 goto cleanup;
492 }
493
494 retrohtr_node( tree, node_new_idx )->tag = tag_iter_idx;
495
496#if RETROHTR_TRACE_LVL > 0
497 debug_printf( RETROHTR_TRACE_LVL,
498 "rendering node " SSIZE_T_FMT " (%s) under node " SSIZE_T_FMT,
499 node_new_idx,
500 gc_mhtml_tag_names[p_tag_iter->base.type],
501 node_idx );
502#endif /* RETROHTR_TRACE_LVL */
503
504 /* Tag-specific rendering preparations. */
505 if( MHTML_TAG_TYPE_IMG == p_tag_iter->base.type ) {
506 /* Load the image for rendering later. */
507 retval = retroflat_load_bitmap(
508 p_tag_iter->IMG.src,
509 &(retrohtr_node( tree, node_new_idx )->bitmap),
511 if( MERROR_OK == retval ) {
512#if RETROHTR_TRACE_LVL > 0
513 debug_printf( RETROHTR_TRACE_LVL, "loaded img: %s",
514 p_tag_iter->IMG.src );
515#endif /* RETROHTR_TRACE_LVL */
516 } else {
517 error_printf( "could not load img: %s", p_tag_iter->IMG.src );
518 }
519 }
520
521 tag_next_idx = p_tag_iter->base.next_sibling;
522
523 mdata_vector_unlock( &(parser->tags) );
524
525 retval = retrohtr_tree_create( parser, tree, x, y, w, h,
526 tag_iter_idx, node_new_idx, d + 1 );
527 maug_cleanup_if_not_ok();
528
529 mdata_vector_lock( &(parser->tags) );
530
531 tag_iter_idx = tag_next_idx;
532 }
533
534cleanup:
535
536 if( mdata_vector_is_locked( &(parser->tags) ) ) {
537 mdata_vector_unlock( &(parser->tags) );
538 }
539
540 return retval;
541}
542
543/* === */
544
546 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
547 struct MCSS_STYLE* parent_style, struct MCSS_STYLE* effect_style,
548 ssize_t tag_idx
549) {
550 MERROR_RETVAL retval = MERROR_OK;
551 ssize_t tag_style_idx = -1;
552 size_t tag_type = 0,
553 i = 0;
554 struct MCSS_STYLE* style = NULL;
555 union MHTML_TAG* p_tag_iter = NULL;
556
557#if RETROHTR_TRACE_LVL > 0
558 debug_printf( RETROHTR_TRACE_LVL,
559 "applying styles for tag: " SSIZE_T_FMT, tag_idx );
560#endif /* RETROHTR_TRACE_LVL */
561
562 assert( !mdata_vector_is_locked( &(parser->tags) ) );
563 mdata_vector_lock( &(parser->styler.styles) );
564 mdata_vector_lock( &(parser->tags) );
565
566 maug_mzero( effect_style, sizeof( struct MCSS_STYLE ) );
567
568 if( 0 >= tag_idx ) {
569 goto cleanup;
570 }
571 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
572 if( NULL == p_tag_iter ) {
573 goto cleanup;
574 }
575
576 tag_type = p_tag_iter->base.type;
577
578 /* Merge style based on HTML element class. */
579 if( 0 < p_tag_iter->base.classes_sz ) {
580 for( i = 0 ; mdata_vector_ct( &(parser->styler.styles) ) > i ; i++ ) {
581 style = mdata_vector_get(
582 &(parser->styler.styles), i, struct MCSS_STYLE );
583
584 if(
585 NULL != style &&
586 0 == maug_strncmp(
587 p_tag_iter->base.classes,
588 style->class,
589 p_tag_iter->base.classes_sz
590 )
591 ) {
592#if RETROHTR_TRACE_LVL > 0
593 debug_printf( RETROHTR_TRACE_LVL, "found style for tag class: %s",
594 style->class );
595#endif /* RETROHTR_TRACE_LVL */
596
597 retrohtr_merge_styles(
598 effect_style, parent_style, style, tag_type );
599 }
600 }
601 }
602
603 /* Merge style based on HTML element ID. */
604 if( 0 < p_tag_iter->base.id_sz ) {
605 for( i = 0 ; mdata_vector_ct( &(parser->styler.styles) ) > i ; i++ ) {
606 style = mdata_vector_get(
607 &(parser->styler.styles), i, struct MCSS_STYLE );
608
609 if(
610 NULL != style &&
611 0 == maug_strncmp(
612 p_tag_iter->base.id,
613 style->id,
614 p_tag_iter->base.id_sz
615 )
616 ) {
617#if RETROHTR_TRACE_LVL > 0
618 debug_printf( RETROHTR_TRACE_LVL, "found style for tag ID: %s",
619 style->id );
620#endif /* RETROHTR_TRACE_LVL */
621
622 retrohtr_merge_styles(
623 effect_style, parent_style, style, tag_type );
624 }
625 }
626 }
627
628 /* Grab element-specific style last. */
629 tag_style_idx = p_tag_iter->base.style;
630
631cleanup:
632
633 /* TODO: Separate this out of cleanup phase. */
634
635 /* This might be NULL! */
636 style = mdata_vector_get(
637 &(parser->styler.styles), tag_style_idx, struct MCSS_STYLE );
638
639 /* Make sure we have a root style. */
640 retrohtr_merge_styles( effect_style, parent_style, style, tag_type );
641
642 mdata_vector_unlock( &(parser->tags) );
643 mdata_vector_unlock( &(parser->styler.styles) );
644
645 return retval;
646}
647
648/* === */
649
650static MERROR_RETVAL retrohtr_load_font(
651 struct MCSS_PARSER* styler,
652#ifdef RETROGXC_PRESENT
653 ssize_t* font_idx_p,
654#else
655 MAUG_MHANDLE* font_h_p,
656#endif /* RETROGXC_PRESENT */
657 struct MCSS_STYLE* effect_style
658) {
659 MERROR_RETVAL retval = MERROR_OK;
660
661#ifdef RETROGXC_PRESENT
662 if( 0 <= *font_idx_p ) {
663 error_printf( "tried to load font but font already loaded, idx: "
664 SSIZE_T_FMT, *font_idx_p );
665#else
666 if( (MAUG_MHANDLE)NULL != *font_h_p ) {
667 error_printf( "tried to load font but font already loaded, p: %p",
668 *font_h_p );
669#endif /* RETROGXC_PRESENT */
670 goto cleanup;
671 }
672
673 mdata_strpool_lock( &(styler->strpool) );
674
675#if RETROHTR_TRACE_LVL > 0
676 debug_printf( RETROHTR_TRACE_LVL,
677 "loading font: %s (" SSIZE_T_FMT ")",
678 mdata_strpool_get( &(styler->strpool), effect_style->FONT_FAMILY ),
679 effect_style->FONT_FAMILY );
680#endif /* RETROHTR_TRACE_LVL */
681
682 if( MDATA_STRPOOL_IDX_ERROR == effect_style->FONT_FAMILY ) {
683 error_printf( "style has no font associated!" );
684 /* TODO: Load fallback font? */
685 retval = MERROR_GUI;
686 goto cleanup;
687 }
688
689 /* Load the font into the cache. */
690#ifdef RETROGXC_PRESENT
691 *font_idx_p =
692 retrogxc_load_font(
693 mdata_strpool_get( &(styler->strpool), effect_style->FONT_FAMILY ),
694 0, 33, 93 );
695#else
696 retval = retrofont_load(
697 mdata_strpool_get( &(styler->strpool), effect_style->FONT_FAMILY ),
698 font_h_p, 0, 33, 93 );
699#endif /* RETROGXC_PRESENT */
700
701cleanup:
702
703 mdata_strpool_unlock( &(styler->strpool) );
704
705 return retval;
706}
707
708/* === */
709
710MERROR_RETVAL retrohtr_tree_gui(
711 struct RETROHTR_RENDER_TREE* tree, struct MCSS_PARSER* styler,
712 struct MCSS_STYLE* effect_style
713) {
714 MERROR_RETVAL retval = MERROR_OK;
715
716 /* Create a GUI handler just for this tree. */
717 if(
718 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
719 (RETROHTR_TREE_FLAG_GUI_ACTIVE & tree->flags)
720 ) {
721#if RETROHTR_TRACE_LVL > 0
722 debug_printf( RETROHTR_TRACE_LVL, "tree GUI already active!" );
723#endif /* RETROHTR_TRACE_LVL */
724 goto cleanup;
725 }
726
727 /* This means all GUI items will use the font from the first node
728 * loaded with a GUI item!
729 */
730 retval = retrogui_init( &(tree->gui) );
731 maug_cleanup_if_not_ok();
732
733 retval = retrohtr_load_font(
734 styler,
735#ifdef RETROGXC_PRESENT
736 &(tree->gui.font_idx),
737#else
738 &(tree->gui.font_h),
739#endif /* RETROGXC_PRESENT */
740 effect_style );
741 maug_cleanup_if_not_ok();
742
743 tree->flags |= RETROHTR_TREE_FLAG_GUI_ACTIVE;
744
745#if RETROHTR_TRACE_LVL > 0
746 debug_printf( RETROHTR_TRACE_LVL, "tree GUI initialized!" );
747#endif /* RETROHTR_TRACE_LVL */
748
749cleanup:
750 return retval;
751}
752
753/* === */
754
755MERROR_RETVAL retrohtr_tree_size(
756 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
757 struct MCSS_STYLE* prev_sibling_style,
758 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d
759) {
760 struct MCSS_STYLE effect_style;
761 struct MCSS_STYLE child_prev_sibling_style;
762 struct MCSS_STYLE child_style;
763 ssize_t child_iter_idx = -1;
764 ssize_t tag_idx = -1;
765 ssize_t node_iter_idx = -1;
766 size_t this_line_w = 0;
767 size_t this_line_h = 0;
768 MERROR_RETVAL retval = MERROR_OK;
769 union RETROGUI_CTL ctl;
770 union MHTML_TAG* p_tag_iter = NULL;
771 union MHTML_TAG* p_tag_node = NULL;
772
773 if( NULL == retrohtr_node( tree, node_idx ) ) {
774 goto cleanup;
775 }
776
777 tag_idx = retrohtr_node( tree, node_idx )->tag;
778
779 retval = retrohtr_apply_styles(
780 parser, tree, parent_style, &effect_style, tag_idx );
781 maug_cleanup_if_not_ok();
782
783 assert( !mdata_vector_is_locked( &(parser->tags) ) );
784 mdata_vector_lock( &(parser->tags) );
785
786 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
787 assert( NULL != p_tag_iter );
788
789 /* position */
790
791 if( mcss_prop_is_active( effect_style.POSITION ) ) {
792#if RETROHTR_TRACE_LVL > 0
793 debug_printf( RETROHTR_TRACE_LVL,
794 "node " SSIZE_T_FMT ": applying %s positioning",
795 node_idx, gc_mcss_position_names[effect_style.POSITION] );
796#endif /* RETROHTR_TRACE_LVL */
797 /* TODO: MCSS_POS_NOTE: We'd like to get rid of this so all positioning
798 * is done through CSS... unfortunately, we only track the current
799 * and previous effective styles while working that out later, so
800 * we need to pin this to the element directly so we can rule it
801 * out of the box model e.g. when determining x/y coords of its
802 * neighbors.
803 */
804 retrohtr_node( tree, node_idx )->pos = effect_style.POSITION;
805 retrohtr_node( tree, node_idx )->pos_flags = effect_style.POSITION_flags;
806 }
807
808 /* Grab fixed dimensions before content-based calculations of children, so
809 * we know if there are constraints. If these aren't set, then we'll size
810 * based on childrens' sizes after we determine childrens' sizes below.
811 */
812
813 if( mcss_prop_is_active_NOT_flag( effect_style.WIDTH, AUTO ) ) {
814 retrohtr_node( tree, node_idx )->w = effect_style.WIDTH;
815 }
816
817 if( mcss_prop_is_active_NOT_flag( effect_style.HEIGHT, AUTO ) ) {
818 retrohtr_node( tree, node_idx )->h = effect_style.HEIGHT;
819 }
820
821 /* Figure out how big the contents of this node are. */
822
823 /* Font is heritable, so load it for all nodes even if we don't use it. */
824 retval = retrohtr_load_font(
825 &(parser->styler),
826#ifdef RETROGXC_PRESENT
827 &(retrohtr_node( tree, node_idx )->font_idx),
828#else
829 &(retrohtr_node( tree, node_idx )->font_h),
830#endif /* RETROGXC_PRESENT */
831 &effect_style );
832 maug_cleanup_if_not_ok();
833
834 if( 0 <= tag_idx && MHTML_TAG_TYPE_TEXT == p_tag_iter->base.type ) {
835 /* Get text size to use in calculations below. */
836
837 mdata_strpool_lock( &(parser->strpool) );
838
839#ifdef RETROGXC_PRESENT
840 retrogxc_string_sz(
841#else
842 retrofont_string_sz(
843#endif /* RETROGXC_PRESENT */
844 NULL,
845 mdata_strpool_get( &(parser->strpool), p_tag_iter->TEXT.content_idx ),
846 p_tag_iter->TEXT.content_sz,
847#ifdef RETROGXC_PRESENT
848 retrohtr_node( tree, node_idx )->font_idx,
849#else
850 retrohtr_node( tree, node_idx )->font_h,
851#endif /* RETROGXC_PRESENT */
852 /* Constrain node text size to parent size. */
853 retrohtr_node_parent( tree, node_idx )->w,
854 retrohtr_node_parent( tree, node_idx )->h,
855 &(retrohtr_node( tree, node_idx )->w),
856 &(retrohtr_node( tree, node_idx )->h), 0 );
857
858#if RETROHTR_TRACE_LVL > 0
859 debug_printf( RETROHTR_TRACE_LVL, "TEXT w: " SIZE_T_FMT,
860 retrohtr_node( tree, node_idx )->w );
861#endif /* RETROHTR_TRACE_LVL */
862
863 mdata_strpool_unlock( &(parser->strpool) );
864
865 } else if(
866 0 <= tag_idx &&
867 MHTML_TAG_TYPE_INPUT == p_tag_iter->base.type
868 ) {
869 /* Push the control (for the client renderer to redraw later). */
870
871 retval = retrohtr_tree_gui( tree, &(parser->styler), &effect_style );
872
873 if(
874 /* Use the same ID for the node and control it creates. */
875 MERROR_OK != retrogui_init_ctl(
876 &ctl, RETROGUI_CTL_TYPE_BUTTON, node_idx )
877 ) {
878 error_printf( "could not initialize control!" );
879 goto cleanup;
880 }
881
882 p_tag_node = mdata_vector_get(
883 &(parser->tags),
884 retrohtr_node( tree, node_idx )->tag, union MHTML_TAG );
885
886 ctl.base.x = retrohtr_node( tree, node_idx )->x;
887 ctl.base.y = retrohtr_node( tree, node_idx )->y;
888 ctl.base.w = 0;
889 ctl.base.h = 0;
890 ctl.BUTTON.label = p_tag_node->INPUT.value;
891
892 /* Grab determined size back from control. */
893 retrohtr_node( tree, node_idx )->w = ctl.base.w;
894 retrohtr_node( tree, node_idx )->h = ctl.base.h;
895
896#if RETROHTR_TRACE_LVL > 0
897 debug_printf( RETROHTR_TRACE_LVL, "initialized control for INPUT..." );
898#endif /* RETROHTR_TRACE_LVL */
899
900 retrogui_push_ctl( &(tree->gui), &ctl );
901
902 } else if( 0 <= tag_idx && MHTML_TAG_TYPE_IMG == p_tag_iter->base.type ) {
903
904 if( retroflat_bitmap_ok( &(retrohtr_node( tree, node_idx )->bitmap) ) ) {
905 retrohtr_node( tree, node_idx )->w =
906 retroflat_bitmap_w( &(retrohtr_node( tree, node_idx )->bitmap) );
907 retrohtr_node( tree, node_idx )->h =
908 retroflat_bitmap_h( &(retrohtr_node( tree, node_idx )->bitmap) );
909 }
910
911#if RETROHTR_TRACE_LVL > 0
912 debug_printf( RETROHTR_TRACE_LVL, "TEXT w: " SIZE_T_FMT,
913 retrohtr_node( tree, node_idx )->w );
914#endif /* RETROHTR_TRACE_LVL */
915
916 } else {
917 /* Get sizing of child nodes. */
918
919 maug_mzero( &child_prev_sibling_style, sizeof( struct MCSS_STYLE ) );
920 node_iter_idx = retrohtr_node( tree, node_idx )->first_child;
921 mdata_vector_unlock( &(parser->tags) );
922 while( 0 <= node_iter_idx ) {
923 retrohtr_tree_size(
924 parser, tree, &child_prev_sibling_style, &effect_style,
925 node_iter_idx, d + 1 );
926
927 node_iter_idx = retrohtr_node( tree, node_iter_idx )->next_sibling;
928 }
929 }
930
931 if( mdata_vector_is_locked( &(parser->tags) ) ) {
932 mdata_vector_unlock( &(parser->tags) );
933 }
934
935 /* If our width is still zero, then size based on children. */
936 if( 0 == retrohtr_node( tree, node_idx )->w ) {
937 if(
938 MCSS_DISPLAY_BLOCK == effect_style.DISPLAY &&
939 0 <= retrohtr_node( tree, node_idx )->parent
940 ) {
941 /* Use parent width. */
942 /* TODO: Subtract parent padding! */
943 retrohtr_node( tree, node_idx )->w =
944 retrohtr_node_parent( tree, node_idx )->w;
945 }
946
947 /* Cycle through children and use greatest width. */
948 child_iter_idx = retrohtr_node( tree, node_idx )->first_child;
949 while( 0 <= child_iter_idx ) {
950 assert( !mdata_vector_is_locked( &(parser->tags) ) );
951 retval = retrohtr_apply_styles(
952 parser, tree, &effect_style, &child_style,
953 retrohtr_node( tree, child_iter_idx )->tag );
954 maug_cleanup_if_not_ok();
955
956 /* Skip ABSOLUTE nodes. */
957 if( MCSS_POSITION_ABSOLUTE == child_style.POSITION ) {
958 child_iter_idx =
959 retrohtr_node( tree, child_iter_idx )->next_sibling;
960 continue;
961 }
962
963 if( MCSS_DISPLAY_BLOCK == child_style.DISPLAY ) {
964 /* Reset the line width counter for coming BLOCK node. */
965 this_line_w = 0;
966
967 if(
968 retrohtr_node( tree, child_iter_idx )->w >
969 retrohtr_node( tree, node_idx )->w
970 ) {
971 /* This BLOCK node is the longest yet! */
972 retrohtr_node( tree, node_idx )->w =
973 retrohtr_node( tree, child_iter_idx )->w;
974 }
975 } else {
976 /* Add inline node to this node line's width. */
977 this_line_w += retrohtr_node( tree, child_iter_idx )->w;
978
979 if( this_line_w > retrohtr_node( tree, node_idx )->w ) {
980 /* The line of nodes we've been adding up is the longest yet! */
981 retrohtr_node( tree, node_idx )->w = this_line_w;
982 }
983 }
984 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
985 }
986 }
987
988 /* If our height is still zero, then size based on children. */
989 if( 0 == retrohtr_node( tree, node_idx )->h ) {
990 /* Cycle through children and add heights. */
991 child_iter_idx = retrohtr_node( tree, node_idx )->first_child;
992 while( 0 <= child_iter_idx ) {
993 assert( !mdata_vector_is_locked( &(parser->tags) ) );
995 parser, tree, &effect_style, &child_style,
996 retrohtr_node( tree, child_iter_idx )->tag );
997
998 /* Skip ABSOLUTE nodes. */
999 if( MCSS_POSITION_ABSOLUTE == child_style.POSITION ) {
1000 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
1001 continue;
1002 }
1003
1004 if( MCSS_DISPLAY_BLOCK == child_style.DISPLAY ) {
1005 /* Add the last line to the running height. */
1006 retrohtr_node( tree, node_idx )->h += this_line_h;
1007
1008 /* Start a new running line height with this BLOCK node. */
1009 this_line_h = retrohtr_node( tree, child_iter_idx )->h;
1010 } else {
1011 /* Make sure this line is at least as tall as this INLINE node. */
1012 if( this_line_h < retrohtr_node( tree, child_iter_idx )->h ) {
1013 this_line_h = retrohtr_node( tree, child_iter_idx )->h;
1014 }
1015 }
1016
1017 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
1018 }
1019
1020 /* Add the last line height the node height. */
1021 retrohtr_node( tree, node_idx )->h += this_line_h;
1022 this_line_h = 0;
1023 }
1024
1025 /* Apply additional modifiers (padding, etc) after children have all been
1026 * calculated.
1027 */
1028
1029 /* Try specific left padding first, then try general padding. */
1030 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_LEFT, AUTO ) ) {
1031 retrohtr_node( tree, node_idx )->w += effect_style.PADDING_LEFT;
1032 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
1033 retrohtr_node( tree, node_idx )->w += effect_style.PADDING;
1034 }
1035
1036 /* Try specific right padding first, then try general padding. */
1037 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_RIGHT, AUTO ) ) {
1038 retrohtr_node( tree, node_idx )->w += effect_style.PADDING_RIGHT;
1039 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
1040 retrohtr_node( tree, node_idx )->w += effect_style.PADDING;
1041 }
1042
1043 /* Try specific top padding first, then try general padding. */
1044 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_TOP, AUTO ) ) {
1045 retrohtr_node( tree, node_idx )->h += effect_style.PADDING_TOP;
1046 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
1047 retrohtr_node( tree, node_idx )->h += effect_style.PADDING;
1048 }
1049
1050 /* Try specific bottom padding first, then try general padding. */
1051 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_BOTTOM, AUTO ) ) {
1052 retrohtr_node( tree, node_idx )->h += effect_style.PADDING_BOTTOM;
1053 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
1054 retrohtr_node( tree, node_idx )->h += effect_style.PADDING;
1055 }
1056
1057#if RETROHTR_TRACE_LVL > 0
1058 debug_printf( RETROHTR_TRACE_LVL,
1059 "setting node " SIZE_T_FMT " dirty...", node_idx );
1060#endif /* RETROHTR_TRACE_LVL */
1061 retrohtr_node( tree, node_idx )->flags |= RETROHTR_NODE_FLAG_DIRTY;
1062
1063cleanup:
1064
1065 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1066 mdata_vector_unlock( &(parser->tags) );
1067 }
1068
1069 /* We're done with the prev_sibling_style for this iter, so prepare it for
1070 * the next called by the parent!
1071 */
1072 if( NULL != prev_sibling_style ) {
1073 maug_mcpy(
1074 prev_sibling_style, &effect_style,
1075 sizeof( struct MCSS_STYLE ) );
1076 }
1077
1078 return retval;
1079}
1080
1081/* TODO: See MCSS_POS_NOTE. */
1083#define retrohtr_break_on_active_pos( iter_idx ) \
1084 if( mcss_prop_is_active( retrohtr_node( tree, iter_idx )->pos ) ) { \
1085 break; \
1086 }
1087
1088static ssize_t retrohtr_find_prev_sibling_in_box_model(
1089 struct RETROHTR_RENDER_TREE* tree,
1090 ssize_t node_idx
1091) {
1092 ssize_t sibling_iter_idx = -1;
1093 ssize_t sibling_found_idx = -1;
1094
1095 if( 0 > retrohtr_node( tree, node_idx )->parent ) {
1096 /* Can't determine sibling! */
1097 goto cleanup;
1098 }
1099
1100 sibling_iter_idx = retrohtr_node_parent( tree, node_idx )->first_child;
1101
1102 if( sibling_iter_idx == node_idx ) {
1103 /* No previous siblings! */
1104 goto cleanup;
1105 }
1106
1107 while( 0 <= sibling_iter_idx && node_idx != sibling_iter_idx ) {
1108 if(
1109 /* TODO: See MCSS_POS_NOTE. This is what we were talking about. */
1110 MCSS_POSITION_ABSOLUTE != retrohtr_node( tree, sibling_iter_idx )->pos
1111 ) {
1112 sibling_found_idx = sibling_iter_idx;
1113 }
1114
1115 /* TODO: Reset on <br />? */
1116
1117 sibling_iter_idx = retrohtr_node( tree, sibling_iter_idx )->next_sibling;
1118 }
1119
1120cleanup:
1121 return sibling_found_idx;
1122}
1123
1124/* === */
1125
1126static MERROR_RETVAL retrohtr_mark_edge_child_nodes(
1127 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
1128 ssize_t node_parent_idx
1129) {
1130 ssize_t node_sibling_idx = -1;
1131 MERROR_RETVAL retval = MERROR_OK;
1132 struct MCSS_STYLE effect_style;
1133 size_t col_idx = 0; /* How many nodes right (X)? */
1134 size_t row_idx = 0; /* How many nodes down (Y)? */
1135 union MHTML_TAG* p_tag_iter = NULL;
1136
1137 node_sibling_idx = retrohtr_node( tree, node_parent_idx )->first_child;
1138 while( 0 <= node_sibling_idx ) {
1139 maug_mzero( &effect_style, sizeof( struct MCSS_STYLE ) );
1141 parser, tree, NULL, &effect_style,
1142 retrohtr_node( tree, node_sibling_idx )->tag );
1143
1144 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
1145 /* Absolute nodes are never on the edge. */
1146 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_INSIDE;
1147
1148 } else if( MCSS_DISPLAY_INLINE == effect_style.DISPLAY ) {
1149 /* Inline, or something that follows previous column. */
1150 if( 0 == col_idx ) {
1151 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_LEFT;
1152 }
1153 if( 0 == row_idx ) {
1154 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_TOP;
1155 }
1156 if( 0 < row_idx && 0 < col_idx ) {
1157 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_INSIDE;
1158 }
1159 col_idx++;
1160
1161 } else {
1162 /* Block element will be on the next line, so take that into account
1163 * when deciding the edge below.
1164 */
1165 row_idx++;
1166 col_idx = 0;
1167
1168 /* Block, or something else in a new row. */
1169 if( 0 == row_idx ) {
1170 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_TOP;
1171 }
1172
1173 /* Assume block is always on a new line. */
1174 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_LEFT;
1175 }
1176
1177 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1178 mdata_vector_lock( &(parser->tags) );
1179
1180 p_tag_iter = mdata_vector_get( &(parser->tags),
1181 retrohtr_node( tree, node_sibling_idx )->tag,
1182 union MHTML_TAG );
1183 assert( NULL != p_tag_iter );
1184
1185 debug_printf( 1, "marking node " SIZE_T_FMT " (%s) edge: %u",
1186 node_sibling_idx,
1187 gc_mhtml_tag_names[p_tag_iter->base.type],
1188 retrohtr_node( tree, node_sibling_idx )->edge );
1189
1190 mdata_vector_unlock( &(parser->tags) );
1191
1192 node_sibling_idx =
1193 retrohtr_node( tree, node_sibling_idx )->next_sibling;
1194 }
1195
1196cleanup:
1197
1198 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1199 mdata_vector_unlock( &(parser->tags) );
1200 }
1201
1202 return retval;
1203}
1204
1205/* === */
1206
1207MERROR_RETVAL retrohtr_tree_pos(
1208 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
1209 struct MCSS_STYLE* prev_sibling_style,
1210 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d
1211) {
1212 struct MCSS_STYLE child_prev_sibling_style;
1213 struct MCSS_STYLE effect_style;
1214 ssize_t child_iter_idx = -1;
1215 ssize_t tag_idx = -1;
1216 ssize_t node_iter_idx = -1;
1217 ssize_t prev_sibling_idx = -1;
1218 MERROR_RETVAL retval = MERROR_OK;
1219 union MHTML_TAG* p_tag_iter = NULL;
1220
1221 if( NULL == retrohtr_node( tree, node_idx ) ) {
1222 goto cleanup;
1223 }
1224
1225 tag_idx = retrohtr_node( tree, node_idx )->tag;
1226
1228 parser, tree, parent_style, &effect_style, tag_idx );
1229
1230 prev_sibling_idx =
1231 retrohtr_find_prev_sibling_in_box_model( tree, node_idx );
1232
1233 /* x */
1234
1235 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
1236 /* This node is positioned absolutely. (Relatively) simple! */
1237
1238 if( mcss_prop_is_active_NOT_flag( effect_style.LEFT, AUTO ) ) {
1239
1240 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1241 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1242 retrohtr_break_on_active_pos( child_iter_idx );
1243 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1244 }
1245
1246 /* Set X to highest non-explicit ancestor. */
1247 retrohtr_node( tree, node_idx )->x =
1248 retrohtr_node( tree, child_iter_idx )->x + effect_style.LEFT;
1249 }
1250 if( mcss_prop_is_active_NOT_flag( effect_style.RIGHT, AUTO ) ) {
1251
1252 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1253 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1254 retrohtr_break_on_active_pos( child_iter_idx );
1255 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1256 }
1257
1258 /* Set X to highest non-explicit ancestor. */
1259 retrohtr_node( tree, node_idx )->x =
1260 retrohtr_node( tree, child_iter_idx )->w -
1261 retrohtr_node( tree, node_idx )->w -
1262 effect_style.RIGHT;
1263 }
1264
1265 } else if(
1266 MCSS_DISPLAY_INLINE == effect_style.DISPLAY &&
1267 MCSS_DISPLAY_INLINE == prev_sibling_style->DISPLAY &&
1268 0 <= prev_sibling_idx
1269 ) {
1270 /* Place to the right of the previous sibling. */
1271 retrohtr_node( tree, node_idx )->x =
1272 retrohtr_node( tree, prev_sibling_idx )->x +
1273 retrohtr_node( tree, prev_sibling_idx )->w;
1274
1275 } else if( 0 <= retrohtr_node( tree, node_idx )->parent ) {
1276 retrohtr_node( tree, node_idx )->x = retrohtr_node_parent( tree, node_idx )->x;
1277 }
1278
1279 /* y */
1280
1281 /* TODO: Add margins of children? */
1282
1283 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
1284 /* This node is positioned absolutely. (Relatively) simple! */
1285
1286 if( mcss_prop_is_active_NOT_flag( effect_style.TOP, AUTO ) ) {
1287
1288 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1289 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1290 retrohtr_break_on_active_pos( child_iter_idx );
1291 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1292 }
1293
1294 /* Set Y to highest non-explicit ancestor. */
1295 retrohtr_node( tree, node_idx )->y =
1296 retrohtr_node( tree, child_iter_idx )->y + effect_style.TOP;
1297 }
1298 if( mcss_prop_is_active_NOT_flag( effect_style.BOTTOM, AUTO ) ) {
1299
1300 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1301 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1302 retrohtr_break_on_active_pos( child_iter_idx );
1303 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1304 }
1305
1306 /* Set Y to highest non-explicit ancestor. */
1307 retrohtr_node( tree, node_idx )->y =
1308 retrohtr_node( tree, child_iter_idx )->h -
1309 retrohtr_node( tree, node_idx )->h -
1310 effect_style.BOTTOM;
1311 }
1312
1313 } else if(
1314 MCSS_DISPLAY_INLINE == effect_style.DISPLAY &&
1315 MCSS_DISPLAY_INLINE == prev_sibling_style->DISPLAY &&
1316 0 <= prev_sibling_idx
1317 ) {
1318 /* Place to the right of the previous sibling. */
1319 retrohtr_node( tree, node_idx )->y = retrohtr_node( tree, prev_sibling_idx )->y;
1320
1321 } else if( 0 <= prev_sibling_idx ) {
1322 /* Place below the previous block sibling. */
1323
1324 /* TODO: We should probably use the tallest element on the prev sibling's
1325 * line, but that seems hard...
1326 */
1327
1328 retrohtr_node( tree, node_idx )->y =
1329 retrohtr_node( tree, prev_sibling_idx )->y +
1330 retrohtr_node( tree, prev_sibling_idx )->h;
1331
1332 } else if( 0 <= retrohtr_node( tree, node_idx )->parent ) {
1333 /* Position relative to other nodes. */
1334
1335 retrohtr_node( tree, node_idx )->y = retrohtr_node_parent( tree, node_idx )->y;
1336 }
1337
1338 /* margin-left, margin-right */
1339
1340 if(
1341 MCSS_POSITION_ABSOLUTE != retrohtr_node( tree, node_idx )->pos &&
1342 0 <= retrohtr_node( tree, node_idx )->parent &&
1343 mcss_prop_is_active_flag( effect_style.MARGIN_LEFT, AUTO ) &&
1344 mcss_prop_is_active_flag( effect_style.MARGIN_RIGHT, AUTO )
1345 ) {
1346 /* Center */
1347 retrohtr_node( tree, node_idx )->x =
1348 retrohtr_node_parent( tree, node_idx )->x +
1349 (retrohtr_node_parent( tree, node_idx )->w >> 1) -
1350 (retrohtr_node( tree, node_idx )->w >> 1);
1351
1352 } else if(
1353 0 <= retrohtr_node( tree, node_idx )->parent &&
1354 mcss_prop_is_active_flag( effect_style.MARGIN_LEFT, AUTO ) &&
1355 mcss_prop_is_active_NOT_flag( effect_style.MARGIN_RIGHT, AUTO )
1356 ) {
1357 /* Justify right. */
1358 /* TODO: Subtract padding below, as well. */
1359 retrohtr_node( tree, node_idx )->x =
1360 retrohtr_node_parent( tree, node_idx )->w -
1361 retrohtr_node( tree, node_idx )->w;
1362
1363 } else if( mcss_prop_is_active( effect_style.MARGIN_LEFT ) ) {
1364 /* Justify left. */
1365 retrohtr_node( tree, node_idx )->x += effect_style.MARGIN_LEFT;
1366 }
1367
1368 /* padding */
1369
1370 /* TODO: Padding is still broken. Needs more involved understanding of
1371 * where elements are in their container.
1372 */
1373
1374 debug_printf( 1, "(d: " SIZE_T_FMT ") node " SIZE_T_FMT " is on edge: %u",
1375 d, node_idx, retrohtr_node( tree, node_idx )->edge );
1376
1377 assert(
1378 0 == node_idx ||
1379 RETROHTR_EDGE_UNKNOWN != retrohtr_node( tree, node_idx )->edge );
1380
1381 if(
1382 RETROHTR_EDGE_LEFT ==
1383 (RETROHTR_EDGE_LEFT & retrohtr_node( tree, node_idx )->edge)
1384 ) {
1385 /* Try specific left padding first, then try general padding. */
1386 if( mcss_prop_is_active_NOT_flag( parent_style->PADDING_LEFT, AUTO ) ) {
1387 retrohtr_node( tree, node_idx )->x += parent_style->PADDING_LEFT;
1388 } else if( mcss_prop_is_active_NOT_flag( parent_style->PADDING, AUTO ) ) {
1389 retrohtr_node( tree, node_idx )->x += parent_style->PADDING;
1390 }
1391 }
1392
1393 if(
1394 RETROHTR_EDGE_TOP ==
1395 (RETROHTR_EDGE_TOP & retrohtr_node( tree, node_idx )->edge) &&
1396 /* Only apply padding to first node in line. The rest will pick it up. */
1397 RETROHTR_EDGE_LEFT ==
1398 (RETROHTR_EDGE_LEFT & retrohtr_node( tree, node_idx )->edge)
1399 ) {
1400 /* Try specific top padding first, then try general padding. */
1401 if( mcss_prop_is_active_NOT_flag( parent_style->PADDING_TOP, AUTO ) ) {
1402 retrohtr_node( tree, node_idx )->y += parent_style->PADDING_TOP;
1403 } else if( mcss_prop_is_active_NOT_flag( parent_style->PADDING, AUTO ) ) {
1404 retrohtr_node( tree, node_idx )->y += parent_style->PADDING;
1405 }
1406 }
1407
1408 /* color */
1409
1410 if( mcss_prop_is_active( effect_style.COLOR ) ) {
1411 retrohtr_node( tree, node_idx )->fg = effect_style.COLOR;
1412 }
1413
1414 if( mcss_prop_is_active( effect_style.BACKGROUND_COLOR ) ) {
1415 retrohtr_node( tree, node_idx )->bg = effect_style.BACKGROUND_COLOR;
1416 }
1417
1418 /* Figure out child positions. */
1419
1420 retrohtr_mark_edge_child_nodes( parser, tree, node_idx );
1421
1422 maug_mzero( &child_prev_sibling_style, sizeof( struct MCSS_STYLE ) );
1423 node_iter_idx = retrohtr_node( tree, node_idx )->first_child;
1424 while( 0 <= node_iter_idx ) {
1425 /* Mark child nodes on the edge so applying padding can be done. */
1426
1427 /* Figure out child node positioning. */
1428 retrohtr_tree_pos(
1429 parser, tree, &child_prev_sibling_style, &effect_style,
1430 node_iter_idx, d + 1 );
1431
1432 node_iter_idx = retrohtr_node( tree, node_iter_idx )->next_sibling;
1433 }
1434
1435 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1436 mdata_vector_lock( &(parser->tags) );
1437 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
1438 assert( NULL != p_tag_iter );
1439
1440 if( MHTML_TAG_TYPE_INPUT == p_tag_iter->base.type ) {
1441 /* Feed the position back to the GUI control created during tree_size. */
1442 retval = retrogui_pos_ctl( &(tree->gui), node_idx,
1443 retrohtr_node_screen_x( tree, node_idx ),
1444 retrohtr_node_screen_y( tree, node_idx ),
1445 retrohtr_node( tree, node_idx )->w,
1446 retrohtr_node( tree, node_idx )->h );
1447 maug_cleanup_if_not_ok();
1448 }
1449
1450#if RETROHTR_TRACE_LVL > 0
1451 debug_printf( RETROHTR_TRACE_LVL,
1452 "setting node " SIZE_T_FMT " dirty...", node_idx );
1453#endif /* RETROHTR_TRACE_LVL */
1454 retrohtr_node( tree, node_idx )->flags |= RETROHTR_NODE_FLAG_DIRTY;
1455
1456cleanup:
1457
1458 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1459 mdata_vector_unlock( &(parser->tags) );
1460 }
1461
1462 /* We're done with the prev_sibling_style for this iter, so prepare it for
1463 * the next called by the parent!
1464 */
1465 if( NULL != prev_sibling_style ) {
1466 maug_mcpy(
1467 prev_sibling_style, &effect_style,
1468 sizeof( struct MCSS_STYLE ) );
1469 }
1470
1471 return retval;
1472}
1473
1474/* === */
1475
1476MERROR_RETVAL retrohtr_tree_draw(
1477 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
1478 ssize_t node_idx, size_t d
1479) {
1480 union MHTML_TAG* p_tag = NULL;
1481 struct RETROHTR_RENDER_NODE* node = NULL;
1482 MERROR_RETVAL retval = MERROR_OK;
1483
1484 node = retrohtr_node( tree, node_idx );
1485
1486 if( NULL == node ) {
1487 return MERROR_OK;
1488 }
1489
1490 /* TODO: Multi-pass, draw absolute pos afterwards. */
1491
1492 if( 0 > node->tag ) {
1493 goto cleanup;
1494 }
1495
1496 if( RETROHTR_NODE_FLAG_DIRTY != (RETROHTR_NODE_FLAG_DIRTY & node->flags) ) {
1497 goto cleanup;
1498 }
1499
1500 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1501 mdata_vector_lock( &(parser->tags) );
1502
1503 p_tag = mdata_vector_get( &(parser->tags), node->tag, union MHTML_TAG );
1504 if( NULL == p_tag ) {
1505 goto cleanup;
1506 }
1507
1508 /* Perform drawing. */
1509 if( MHTML_TAG_TYPE_TEXT == p_tag->base.type ) {
1510
1511 if(
1512 MDATA_STRPOOL_IDX_ERROR == p_tag->TEXT.content_idx ||
1513#ifdef RETROGXC_PRESENT
1514 0 > node->font_idx
1515#else
1516 (MAUG_MHANDLE)NULL == node->font_h
1517#endif /* RETROGXC_PRESENT */
1518 ) {
1519 goto cleanup;
1520 }
1521
1522 mdata_strpool_lock( &(parser->strpool) );
1523
1524#ifdef RETROGXC_PRESENT
1525 retrogxc_string(
1526#else
1528#endif /* RETROGXC_PRESENT */
1529 NULL, node->fg,
1530 mdata_strpool_get( &(parser->strpool), p_tag->TEXT.content_idx ),
1531 p_tag->TEXT.content_sz,
1532#ifdef RETROGXC_PRESENT
1533 node->font_idx,
1534#else
1535 node->font_h,
1536#endif /* RETROGXC_PRESENT */
1537 retrohtr_node_screen_x( tree, node_idx ),
1538 retrohtr_node_screen_y( tree, node_idx ),
1539 node->w, node->h, 0 );
1540
1541 mdata_strpool_unlock( &(parser->strpool) );
1542
1543 } else if( MHTML_TAG_TYPE_BODY == p_tag->base.type ) {
1544
1545#if RETROHTR_TRACE_LVL > 0
1546 debug_printf(
1547 RETROHTR_TRACE_LVL, "drawing BODY node " SIZE_T_FMT "...", node_idx );
1548#endif /* RETROHTR_TRACE_LVL */
1549
1550 /* Draw body BG. */
1551 if( RETROFLAT_COLOR_NULL != node->bg ) {
1552 retroflat_rect(
1553 NULL, node->bg,
1554 retrohtr_node_screen_x( tree, node_idx ),
1555 retrohtr_node_screen_y( tree, node_idx ),
1556 retrohtr_node( tree, node_idx )->w,
1557 retrohtr_node( tree, node_idx )->h,
1558 RETROFLAT_FLAGS_FILL );
1559 }
1560
1561 } else if( MHTML_TAG_TYPE_IMG == p_tag->base.type ) {
1562 /* Blit the image. */
1563
1564 if( !retroflat_bitmap_ok( &(retrohtr_node( tree, node_idx )->bitmap) ) ) {
1565 goto cleanup;
1566 }
1567
1568#if RETROHTR_TRACE_LVL > 0
1569 debug_printf(
1570 RETROHTR_TRACE_LVL, "drawing IMG node " SIZE_T_FMT "...", node_idx );
1571#endif /* RETROHTR_TRACE_LVL */
1572
1574 NULL, &(retrohtr_node( tree, node_idx )->bitmap),
1575 0, 0,
1576 retrohtr_node_screen_x( tree, node_idx ),
1577 retrohtr_node_screen_y( tree, node_idx ),
1578 retroflat_bitmap_w( &(retrohtr_node( tree, node_idx )->bitmap) ),
1579 retroflat_bitmap_h( &(retrohtr_node( tree, node_idx )->bitmap) ),
1581 /* retrohtr_node( tree, node_idx )->w,
1582 retrohtr_node( tree, node_idx )->h */ );
1583
1584 } else if( MHTML_TAG_TYPE_INPUT == p_tag->base.type ) {
1585
1586#if RETROHTR_TRACE_LVL > 0
1587 debug_printf( RETROHTR_TRACE_LVL, "setting tree GUI dirty..." );
1588#endif /* RETROHTR_TRACE_LVL */
1589
1590 tree->gui.flags |= RETROGUI_FLAGS_DIRTY;
1591
1592 } else {
1593 if( RETROFLAT_COLOR_NULL == node->bg ) {
1594 goto cleanup;
1595 }
1596
1597#if RETROHTR_TRACE_LVL > 0
1598 debug_printf(
1599 RETROHTR_TRACE_LVL, "drawing xs node " SIZE_T_FMT "...",
1600 /* gc_mhtml_tag_names[mhtml_tag( parser,
1601 retrohtr_node( tree, node_idx )->tag )->base.type], */
1602 node_idx );
1603#endif /* RETROHTR_TRACE_LVL */
1604
1606 NULL, node->bg,
1607 retrohtr_node_screen_x( tree, node_idx ),
1608 retrohtr_node_screen_y( tree, node_idx ),
1609 node->w, node->h,
1611 }
1612
1613 node->flags &= ~RETROHTR_NODE_FLAG_DIRTY;
1614
1615cleanup:
1616
1617 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1618 mdata_vector_unlock( &(parser->tags) );
1619 }
1620
1621 if( MERROR_OK != retval ) {
1622 error_printf( "failed drawing node: " SIZE_T_FMT, node_idx );
1623 }
1624
1625 /* Keep trying to render children, tho. */
1626
1627 retrohtr_tree_draw( parser, tree, node->first_child, d + 1 );
1628
1629 retrohtr_tree_draw( parser, tree, node->next_sibling, d );
1630
1631 /* If this is the root redraw call, redraw GUI elements. */
1632 if(
1633 0 == d &&
1634 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
1635 (tree->flags & RETROHTR_TREE_FLAG_GUI_ACTIVE)
1636 ) {
1637 retrogui_redraw_ctls( &(tree->gui) );
1638 }
1639
1640 return retval;
1641}
1642
1643/* === */
1644
1645retrogui_idc_t retrohtr_tree_poll_ctls(
1646 struct RETROHTR_RENDER_TREE* tree,
1647 RETROFLAT_IN_KEY* input,
1648 struct RETROFLAT_INPUT* input_evt
1649) {
1650 retrogui_idc_t idc = 0;
1651 MERROR_RETVAL retval = MERROR_OK;
1652
1653 assert( retrohtr_tree_is_locked( tree ) );
1654
1655 if(
1656 RETROHTR_TREE_FLAG_GUI_ACTIVE !=
1657 (RETROHTR_TREE_FLAG_GUI_ACTIVE & tree->flags)
1658 ) {
1659 /* No GUI, so exit without even unlocking. */
1660 return 0;
1661 }
1662
1663 idc = retrogui_poll_ctls( &(tree->gui), input, input_evt );
1664
1665 if( 0 < idc ) {
1666#if RETROHTR_TRACE_LVL > 0
1667 debug_printf(
1668 RETROHTR_TRACE_LVL, "setting node " SIZE_T_FMT " dirty...", idc );
1669#endif /* RETROHTR_TRACE_LVL */
1670 retrohtr_node( tree, idc )->flags |= RETROHTR_NODE_FLAG_DIRTY;
1671 }
1672
1673 if( MERROR_OK != retval ) {
1674 idc = 0;
1675 }
1676
1677 return idc;
1678}
1679
1680/* === */
1681
1682MERROR_RETVAL retrohtr_tree_dump(
1683 struct RETROHTR_RENDER_TREE* tree, struct MHTML_PARSER* parser,
1684 ssize_t node_idx, size_t d
1685) {
1686 size_t i = 0;
1687 char indents[31];
1688 union MHTML_TAG* p_tag_iter = NULL;
1689 MERROR_RETVAL retval = MERROR_OK;
1690
1691 if( 0 > node_idx ) {
1692 return MERROR_OK;
1693 }
1694
1695 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1696 mdata_vector_lock( &(parser->tags) );
1697
1698 p_tag_iter = mdata_vector_get(
1699 &(parser->tags), tree->nodes[node_idx].tag, union MHTML_TAG );
1700 if( NULL == p_tag_iter ) {
1701 goto cleanup;
1702 }
1703
1704 /* Generate the indentation. */
1705 maug_mzero( indents, 30 );
1706 for( i = 0 ; d > i ; i++ ) {
1707 if( maug_strlen( indents ) >= 30 ) {
1708 break;
1709 }
1710 strcat( indents, " " );
1711 }
1712
1713 /* Print the debug line. */
1714 debug_printf(
1715 1,
1716 "%s" SSIZE_T_FMT " (tag %s): x: " SSIZE_T_FMT ", y: " SSIZE_T_FMT
1717 " (" SSIZE_T_FMT " x " SSIZE_T_FMT ") f: "
1718#ifdef RETROGXC_PRESENT
1719 SSIZE_T_FMT,
1720#else
1721 "%p",
1722#endif /* RETROGXC_PRESENT */
1723 indents, node_idx,
1724 0 <= tree->nodes[node_idx].tag ?
1725 gc_mhtml_tag_names[p_tag_iter->base.type] : "ROOT",
1726 tree->nodes[node_idx].x, tree->nodes[node_idx].y,
1727 tree->nodes[node_idx].w, tree->nodes[node_idx].h,
1728#ifdef RETROGXC_PRESENT
1729 tree->nodes[node_idx].font_idx
1730#else
1731 tree->nodes[node_idx].font_h
1732#endif /* RETROGXC_PRESENT */
1733 );
1734
1735 mdata_vector_unlock( &(parser->tags) );
1736
1737 retval = retrohtr_tree_dump(
1738 tree, parser, tree->nodes[node_idx].first_child, d + 1 );
1739 maug_cleanup_if_not_ok();
1740
1741 retval = retrohtr_tree_dump(
1742 tree, parser, tree->nodes[node_idx].next_sibling, d );
1743 maug_cleanup_if_not_ok();
1744
1745cleanup:
1746
1747 return retval;
1748}
1749
1750/* === */
1751
1752void retrohtr_tree_free( struct RETROHTR_RENDER_TREE* tree ) {
1753
1754#if RETROHTR_TRACE_LVL > 0
1755 debug_printf( RETROHTR_TRACE_LVL, "freeing render nodes..." );
1756#endif /* RETROHTR_TRACE_LVL */
1757
1758 /* TODO: Free bitmaps from img! */
1759
1760 /* TODO: Free node->font_h! */
1761
1762 /* Free GUI if present. */
1763 if(
1764 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
1765 (tree->flags & RETROHTR_TREE_FLAG_GUI_ACTIVE)
1766 ) {
1767 retrogui_destroy( &(tree->gui) );
1768 }
1769
1770 /* Unlock nodes before trying to free them. */
1771 retrohtr_tree_unlock( tree );
1772
1773 if( (MAUG_MHANDLE)NULL != tree->nodes_h ) {
1774 maug_mfree( tree->nodes_h );
1775 }
1776}
1777
1778/* === */
1779
1780MERROR_RETVAL retrohtr_tree_init( struct RETROHTR_RENDER_TREE* tree ) {
1781 MERROR_RETVAL retval = MERROR_OK;
1782
1783 maug_mzero( tree, sizeof( struct RETROHTR_RENDER_TREE ) );
1784
1785 /* Perform initial node allocation. */
1786 tree->nodes_sz_max = MHTML_PARSER_TAGS_INIT_SZ;
1787#if RETROHTR_TRACE_LVL > 0
1788 debug_printf( RETROHTR_TRACE_LVL,
1789 "allocating " SIZE_T_FMT " nodes...", tree->nodes_sz_max );
1790#endif /* RETROHTR_TRACE_LVL */
1791 maug_malloc_test(
1792 tree->nodes_h,
1793 tree->nodes_sz_max,
1794 sizeof( struct RETROHTR_RENDER_NODE ) );
1795
1796 /* XXX
1797 r.w_max = retroflat_screen_w();
1798 r.h_max = retroflat_screen_h(); */
1799
1800cleanup:
1801
1802 return retval;
1803}
1804
1805#endif /* RETROHTR_C */
1806
1807#endif /* !RETROHTR_H */
1808
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition: merror.h:28
MERROR_RETVAL retroflat_load_bitmap(const char *filename, struct RETROFLAT_BITMAP *bmp_out, uint8_t flags)
Load a bitmap into the given ::RETROFLAT_BITMAP structure if it is available. Bitmaps are subject to ...
#define RETROFLAT_INSTANCE_NULL
Pass to retroflat_blit_bitmap() instance arg if this is not a sprite (i.e. if it is a background tile...
Definition: retroflt.h:570
MERROR_RETVAL retroflat_blit_bitmap(struct RETROFLAT_BITMAP *target, struct RETROFLAT_BITMAP *src, retroflat_pxxy_t s_x, retroflat_pxxy_t s_y, retroflat_pxxy_t d_x, retroflat_pxxy_t d_y, retroflat_pxxy_t w, retroflat_pxxy_t h, int16_t instance)
Blit the contents of a ::RETROFLAT_BITMAP onto another ::RETROFLAT_BITMAP.
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition: retroflt.h:326
#define RETROFLAT_FLAGS_LITERAL_PATH
Flag for retroflat_load_bitmap() to not use assets path.
Definition: retroflt.h:385
#define RETROFLAT_FLAGS_BITMAP_SILENT
flag for retroflat_load_bitmap() to not show an error dialog if a bitmap fails to load (on supported ...
Definition: retroflt.h:391
void retroflat_rect(struct RETROFLAT_BITMAP *target, const RETROFLAT_COLOR color, retroflat_pxxy_t x, retroflat_pxxy_t y, retroflat_pxxy_t w, retroflat_pxxy_t h, uint8_t flags)
Draw a rectangle onto the target ::RETROFLAT_BITMAP.
#define RETROFLAT_FLAGS_FILL
Flag for retroflat_rect() or retroflat_ellipse(), indicating drawn shape should be filled.
Definition: retroflt.h:374
int16_t retroflat_pxxy_t
Type used for surface pixel coordinates.
Definition: retroflt.h:879
retrogui_idc_t retrogui_poll_ctls(struct RETROGUI *gui, RETROFLAT_IN_KEY *p_input, struct RETROFLAT_INPUT *input_evt)
Poll for the last clicked control and maintain listboxes and menus.
MERROR_RETVAL retrogui_destroy(struct RETROGUI *gui)
Free memory held by a RETROGUI controller internally and clean up any subordinate controls.
MERROR_RETVAL retrogui_init(struct RETROGUI *gui)
Prepare a RETROGUI controller for use.
int16_t retrogui_idc_t
Unique identifying constant number for controls.
Definition: retrogui.h:330
#define mdata_strpool_get(sp, idx)
Get a string by the index of its first character in the strpool.
Definition: mdata.h:334
MERROR_RETVAL retrofont_load(const char *font_name, MAUG_MHANDLE *p_font_h, uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count)
Load a font for drawing.
void retrofont_string(retroflat_blit_t *target, RETROFLAT_COLOR color, const char *str, size_t str_sz, MAUG_MHANDLE font_h, retroflat_pxxy_t x, retroflat_pxxy_t y, retroflat_pxxy_t max_w, retroflat_pxxy_t max_h, uint8_t flags)
Draw a string with the given font.
size_t nodes_sz_max
Current alloc'd number of nodes in RETROHTR_RENDER_NODE::nodes_h.
Definition: retrohtr.h:67
ssize_t parent
Index of container's render node in RETROHTR_RENDER_TREE.
Definition: retrohtr.h:51
ssize_t first_child
Index of first child's render node in RETROHTR_RENDER_TREE.
Definition: retrohtr.h:53
MERROR_RETVAL retrohtr_apply_styles(struct MHTML_PARSER *parser, struct RETROHTR_RENDER_TREE *tree, struct MCSS_STYLE *parent_style, struct MCSS_STYLE *effect_style, ssize_t tag_idx)
Create a style node that is a composite of a parent style and the styles applicable to the classes/ID...
ssize_t next_sibling
Index of next sibling's render node in RETROHTR_RENDER_TREE.
Definition: retrohtr.h:55
size_t nodes_sz
Current active number of nodes in RETROHTR_RENDER_NODE::nodes_h.
Definition: retrohtr.h:65
struct RETROHTR_RENDER_NODE * nodes
Locked pointer to nodes when locked with retrohtr_tree_lock().
Definition: retrohtr.h:63
#define mdata_vector_lock(v)
Lock the vector. This should be done when items from the vector are actively being referenced,...
Definition: mdata.h:372
#define mdata_vector_unlock(v)
Unlock the vector so items may be added and removed.
Definition: mdata.h:405
#define mdata_vector_ct(v)
Number of items of MDATA_VECTOR::item_sz bytes actively stored in this vector.
Definition: mdata.h:448
Definition: mhtml.h:150
Struct passed to retroflat_poll_input() to hold return data.
Definition: retroflt.h:823
Definition: retrogui.h:468
#define RETROGUI_FLAGS_DIRTY
RETROGUI::flags indicating controls should be redrawn.
Definition: retrogui.h:263
MAUG_MHANDLE font_h
Font used to draw any attached RETROGUI_CTL.
Definition: retrogui.h:494
Definition: retrohtr.h:28
Definition: retrohtr.h:59
Definition: mhtml.h:145
Definition: retrogui.h:450