/*************************************************************************** kgraphics.c Copyright (C) 1999,2000..04 Christoph Reichenbach This program may be modified and copied freely according to the terms of the GNU general public license (GPL), as long as the above copyright notice and the licensing information contained herein are preserved. Please refer to www.gnu.org for licensing details. This work is provided AS IS, without warranty of any kind, expressed or implied, including but not limited to the warranties of merchantibility, noninfringement, and fitness for a specific purpose. The author will not be held liable for any damage caused by this work or derivatives of it. By using this source code, you agree to the licensing terms as stated above. Please contact the maintainer for bug reports or inquiries. Current Maintainer: Christoph Reichenbach (CR) [creichen@gmail.com] ***************************************************************************/ #include #include #include #include "sci_graphics.h" #include #undef DEBUG_LSRECT /* Graph subfunctions */ #define K_GRAPH_GET_COLORS_NR 2 #define K_GRAPH_DRAW_LINE 4 #define K_GRAPH_SAVE_BOX 7 #define K_GRAPH_RESTORE_BOX 8 #define K_GRAPH_FILL_BOX_BACKGROUND 9 #define K_GRAPH_FILL_BOX_FOREGROUND 10 #define K_GRAPH_FILL_BOX_ANY 11 #define K_GRAPH_UPDATE_BOX 12 #define K_GRAPH_REDRAW_BOX 13 #define K_GRAPH_ADJUST_PRIORITY 14 /* Control types and flags */ #define K_CONTROL_BUTTON 1 #define K_CONTROL_TEXT 2 #define K_CONTROL_EDIT 3 #define K_CONTROL_ICON 4 #define K_CONTROL_CONTROL 6 #define K_CONTROL_CONTROL_ALIAS 7 #define K_CONTROL_BOX 10 #define ADD_TO_CURRENT_PORT(widget) \ {if (s->port) \ s->port->add(GFXWC(s->port), GFXW(widget)); \ else \ s->picture_port->add(GFXWC(s->visual), GFXW(widget));} #define ADD_TO_CURRENT_PICTURE_PORT(widget) \ {if (s->port) \ s->port->add(GFXWC(s->port), GFXW(widget)); \ else \ s->picture_port->add(GFXWC(s->picture_port), GFXW(widget));} #define ADD_TO_WINDOW_PORT(widget) \ s->wm_port->add(GFXWC(s->wm_port), GFXW(widget)); #define ADD_TO_CURRENT_FG_WIDGETS(widget) \ ADD_TO_CURRENT_PICTURE_PORT(widget) #define ADD_TO_CURRENT_BG_WIDGETS(widget) \ ADD_TO_CURRENT_PICTURE_PORT(widget) #define FULL_REDRAW()\ if (s->visual) \ s->visual->draw(GFXW(s->visual), gfxw_point_zero); \ gfxop_update(s->gfx_state); #define FULL_INSPECTION()\ if (s->visual) \ s->visual->print(GFXW(s->visual), 0); #define GFX_ASSERT(x) { \ int val = !!(x); \ if (val) { \ if (val == GFX_ERROR) \ SCIkwarn(SCIkWARNING, "GFX subsystem returned error on \"" #x "\"!\n"); \ else {\ SCIkwarn(SCIkERROR, "GFX subsystem fatal error condition on \"" #x "\"!\n"); \ vm_handle_fatal_error(s, __LINE__, __FILE__); \ } \ }\ } #define ASSERT(x) { \ int val = !!(x); \ if (!val) { \ SCIkwarn(SCIkERROR, "Fatal error condition on \"" #x "\"!\n"); \ BREAKPOINT(); \ vm_handle_fatal_error(s, __LINE__, __FILE__); \ } \ } static inline int sign_extend_byte(int value) { if (value & 0x80) return value - 256; else return value; } static void assert_primary_widget_lists(state_t *s) { if (!s->dyn_views) { rect_t bounds = s->picture_port->bounds; s->dyn_views = gfxw_new_list(bounds, GFXW_LIST_SORTED); s->dyn_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; ADD_TO_CURRENT_PICTURE_PORT(s->dyn_views); } if (!s->drop_views) { rect_t bounds = s->picture_port->bounds; s->drop_views = gfxw_new_list(bounds, GFXW_LIST_SORTED); s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); } } static void reparentize_primary_widget_lists(state_t *s, gfxw_port_t *newport) { if (!newport) newport = s->picture_port; if (s->dyn_views) { gfxw_remove_widget_from_container(s->dyn_views->parent, GFXW(s->dyn_views)); newport->add(GFXWC(newport), GFXW(s->dyn_views)); } } int _find_view_priority(state_t *s, int y) { /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1) ++y; */ if (s->pic_priority_table) { /* SCI01 priority table set? */ int j; for (j = 0; j < 15; j++) if (y < s->pic_priority_table[j+1]) return j; return 14; /* Maximum */ } else { if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES) return SCI0_VIEW_PRIORITY_14_ZONES(y); else return SCI0_VIEW_PRIORITY(y) == 15 ? 14 : SCI0_VIEW_PRIORITY(y); } } inline int _find_priority_band(state_t *s, int nr) { if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 14)) { if (nr == 15) return 0xffff; else { SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr); } return 0; } if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 15)) { SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr); return 0; } if (s->pic_priority_table) /* SCI01 priority table set? */ return s->pic_priority_table[nr]; else { int retval; if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES) retval = SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr); else retval = SCI0_PRIORITY_BAND_FIRST(nr); /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1) --retval; */ return retval; } } reg_t graph_save_box(state_t *s, rect_t area) { reg_t handle = kalloc(s, "graph_save_box()", sizeof(gfxw_snapshot_t *)); gfxw_snapshot_t **ptr = (gfxw_snapshot_t **) kmem(s, handle); *ptr = gfxw_make_snapshot(s->visual, area); return handle; } void graph_restore_box(state_t *s, reg_t handle) { gfxw_snapshot_t **ptr; int port_nr = s->port->ID; if (!handle.segment) { SCIkwarn(SCIkWARNING, "Attempt to restore box with zero handle\n"); return; } ptr = (gfxw_snapshot_t **) kmem(s, handle); if (!ptr) { SCIkwarn(SCIkWARNING, "Attempt to restore invalid handle %04x\n", handle); return; } while (port_nr > 2 && !(s->port->flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS) &&(gfxw_widget_matches_snapshot(*ptr, GFXW(s->port)))) { /* This shouldn't ever happen, actually, since windows (ports w/ ID > 2) should all be immune */ gfxw_port_t *newport = gfxw_find_port(s->visual, port_nr); SCIkwarn(SCIkERROR, "Port %d is not immune against snapshots!\n", s->port->ID); port_nr--; if (newport) s->port = newport; } if (s->dyn_views && gfxw_widget_matches_snapshot(*ptr, GFXW(s->dyn_views->parent))) { gfxw_container_t *parent = s->dyn_views->parent; do { parent = parent->parent; } while (parent && (gfxw_widget_matches_snapshot(*ptr, GFXW(parent)))); if (!parent) { SCIkwarn(SCIkERROR, "Attempted widget mass destruction by a snapshot\n"); BREAKPOINT(); } reparentize_primary_widget_lists(s, (gfxw_port_t *) parent); } if (!ptr) { SCIkwarn(SCIkERROR, "Attempt to restore invalid snaphot with handle %04x!\n", handle); return; } gfxw_restore_snapshot(s->visual, *ptr); free(*ptr); *ptr = NULL; kfree(s, handle); } #if 0 #define KERNEL_COLOR_PALETTE s->gfx_state->pic->visual_map->colors #define KERNEL_COLORS_NR s->gfx_state->pic->visual_map->colors_nr #else #define KERNEL_COLOR_PALETTE s->gfx_state->resstate->static_palette #define KERNEL_COLORS_NR s->gfx_state->resstate->static_palette_entries #endif static gfx_pixmap_color_t white = {GFX_COLOR_INDEX_UNMAPPED, 255, 255, 255}; gfx_pixmap_color_t * get_pic_color(state_t *s, int color) { if (s->resmgr->sci_version < SCI_VERSION_01_VGA) return &(s->ega_colors[color].visual); if (color == 255) return &white; else if (color < KERNEL_COLORS_NR) return &(KERNEL_COLOR_PALETTE[color]); else { SCIkwarn(SCIkERROR, "Color index %d out of bounds for pic %d (%d max)", color, s->gfx_state->pic_nr, KERNEL_COLORS_NR); BREAKPOINT(); return NULL; /* Well, rather, not return. But gcc gets scared here. */ } } static gfx_color_t graph_map_color(state_t *s, int color, int priority, int control) { gfx_color_t retval; if (s->resmgr->sci_version < SCI_VERSION_01_VGA) { retval = s->ega_colors[(color >=0 && color < 16)? color : 0]; gfxop_set_color(s->gfx_state, &retval, (color < 0)? -1 : retval.visual.r, retval.visual.g, retval.visual.b, (color == -1)? 255 : 0, priority, control); } else { retval.visual = *(get_pic_color(s, color)); retval.alpha = 0; retval.priority = priority; retval.control = control; retval.mask = GFX_MASK_VISUAL | ((priority >= 0)? GFX_MASK_PRIORITY : 0) | ((control >= 0)? GFX_MASK_CONTROL : 0); }; return retval; } /* --- */ reg_t kSetCursor_SCI11(state_t *s, int funct_nr, int argc, reg_t *argv) { switch (argc) { case 1 : if (UKPV(0) == 0) { s->save_mouse_pointer_view = s->mouse_pointer_view; s->save_mouse_pointer_loop = s->mouse_pointer_loop; s->save_mouse_pointer_cel = s->mouse_pointer_cel; s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; gfxop_set_pointer_cursor(s->gfx_state, GFXOP_NO_POINTER); } else { s->mouse_pointer_view = s->save_mouse_pointer_view; s->mouse_pointer_loop = s->save_mouse_pointer_loop; s->mouse_pointer_cel = s->save_mouse_pointer_cel; } case 2 : { point_t pt; pt.x = UKPV(0); pt.y = UKPV(1); GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, pt)); break; } case 3 : GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), NULL)); s->mouse_pointer_view = UKPV(0); s->mouse_pointer_loop = UKPV(1); s->mouse_pointer_cel = UKPV(2); break; case 9 : { point_t hotspot = gfx_point(SKPV(3), SKPV(4)); // sciprintf("Setting hotspot at %d/%d\n", hotspot.x, hotspot.y); gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), &hotspot); break; } default : SCIkwarn(SCIkERROR, "kSetCursor: Unhandled case: %d arguments given!\n", argc); break; } return s->r_acc; } reg_t kSetCursor(state_t *s, int funct_nr, int argc, reg_t *argv) { if (s->version >= SCI_VERSION(1,001,000)|| has_kernel_function(s, "MoveCursor")) { return kSetCursor_SCI11(s, funct_nr, argc, argv); } if (SKPV_OR_ALT(1,1)) { s->mouse_pointer_view = SKPV(0); } else s->mouse_pointer_view = GFXOP_NO_POINTER; s->mouse_pointer_loop = s->mouse_pointer_cel = 0; /* Not used with cursor-format pointers */ GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, s->mouse_pointer_view)); if (argc > 2) { point_t newpos = gfx_point(SKPV(2) + s->port->bounds.x, SKPV(3) + s->port->bounds.y); GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos)); } return s->r_acc; } extern int oldx, oldy; reg_t kMoveCursor(state_t *s, int funct_nr, int argc, reg_t *argv) { point_t newpos; static point_t oldpos = {0}; newpos = s->gfx_state->pointer_pos; if (argc == 1) { /* Case ignored on IBM PC */ } else { newpos.x = SKPV(0)+s->port->zone.x; newpos.y = SKPV(1)+s->port->zone.y; if (newpos.x > s->port->zone.x+s->port->zone.xl) newpos.x = s->port->zone.x+s->port->zone.xl; if (newpos.y > s->port->zone.y+s->port->zone.yl) newpos.y = s->port->zone.y+s->port->zone.yl; if (newpos.x < 0) newpos.x=0; if (newpos.y < 0) newpos.y=0; oldpos = newpos; } GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos)); return s->r_acc; } static inline void _ascertain_port_contents(gfxw_port_t *port) { if (!port->contents) port->contents = (gfxw_widget_t *) gfxw_new_list(port->bounds, 0); } reg_t kShow(state_t *s, int funct_nr, int argc, reg_t *argv) { int old_map = s->pic_visible_map; s->pic_visible_map = (gfx_map_mask_t) UKPV_OR_ALT(0, 1); switch (s->pic_visible_map) { case GFX_MASK_VISUAL: case GFX_MASK_PRIORITY: case GFX_MASK_CONTROL: gfxop_set_visible_map(s->gfx_state, s->pic_visible_map); if (old_map != s->pic_visible_map) { if (s->pic_visible_map == GFX_MASK_VISUAL) /* Full widget redraw */ s->visual->draw(GFXW(s->visual), gfx_point(0,0)); gfxop_update(s->gfx_state); sciprintf("Switching visible map to %x\n", s->pic_visible_map); } break; default: SCIkwarn(SCIkWARNING, "Show(%x) selects unknown map\n", s->pic_visible_map); } s->pic_not_valid = 2; return s->r_acc; } reg_t kPicNotValid(state_t *s, int funct_nr, int argc, reg_t *argv) { s->r_acc = make_reg(0, s->pic_not_valid); if (argc) s->pic_not_valid = (byte)UKPV(0); return s->r_acc; } void _k_redraw_box(state_t *s, int x1, int y1, int x2, int y2) { sciprintf("_k_redraw_box(): Unimplemented!\n"); #if 0 int i; view_object_t *list = s->dyn_views; sciprintf("Reanimating views\n", s->dyn_views_nr); for (i=0;idyn_views_nr;i++) { *(list[i].underBitsp) = graph_save_box(s, list[i].nsLeft, list[i].nsTop, list[i].nsRight-list[i].nsLeft, list[i].nsBottom-list[i].nsTop, SCI_MAP_VISUAL | SCI_MAP_PRIORITY); draw_view0(s->pic, s->ports[0], list[i].nsLeft, list[i].nsTop, list[i].priority, list[i].loop, list[i].cel, 0, list[i].view); } graph_update_box(s, x1, y1, x2-x1, y2-y1); for (i=0;idyn_views_nr;i++) { graph_restore_box(s, *(list[i].underBitsp)); list[i].underBits=0; } #endif } void _k_graph_rebuild_port_with_color(state_t *s, gfx_color_t newbgcolor) { gfxw_port_t *port = s->port; gfxw_port_t *newport; newport = sciw_new_window(s, port->zone, port->font_nr, port->color, newbgcolor, s->titlebar_port->font_nr, s->ega_colors[15], s->ega_colors[8], port->title_text, port->port_flags & ~WINDOW_FLAG_TRANSPARENT); if (s->dyn_views) { int found = 0; gfxw_container_t *parent = s->dyn_views->parent; while (parent && !(found |= (GFXW(parent) == GFXW(port)))) parent = parent->parent; s->dyn_views = NULL; } port->parent->add(GFXWC(port->parent), GFXW(newport)); port->widfree(GFXW(port)); } static int activated_icon_bar; static int port_origin_x; static int port_origin_y; reg_t kGraph(state_t *s, int funct_nr, int argc, reg_t *argv) { rect_t area; gfxw_port_t *port = s->port; int redraw_port = 0; area = gfx_rect(SKPV(2), SKPV(1) , SKPV(4), SKPV(3)); area.xl = area.xl - area.x; /* Since the actual coordinates are absolute */ area.yl = area.yl - area.y; switch(SKPV(0)) { case K_GRAPH_GET_COLORS_NR: return make_reg(0, (s->resmgr->sci_version < SCI_VERSION_01_VGA) ? 0x10 : 0x100); break; case K_GRAPH_DRAW_LINE: { gfx_color_t gfxcolor = graph_map_color(s, SKPV(5) & 0xf, SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1)); SCIkdebug(SCIkGRAPHICS, "draw_line((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n", SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(5), SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1), gfxcolor.mask); redraw_port = 1; ADD_TO_CURRENT_BG_WIDGETS(GFXW(gfxw_new_line(gfx_point(SKPV(2), SKPV(1)), gfx_point(SKPV(4), SKPV(3)), gfxcolor, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); } break; case K_GRAPH_SAVE_BOX: area.x += s->port->zone.x + port_origin_x; area.y += s->port->zone.y + port_origin_y; area.xl += -port_origin_x; area.yl += -port_origin_y; return(graph_save_box(s, area)); break; case K_GRAPH_RESTORE_BOX: graph_restore_box(s, argv[1]); break; case K_GRAPH_FILL_BOX_BACKGROUND: _k_graph_rebuild_port_with_color(s, port->bgcolor); port = s->port; redraw_port = 1; break; case K_GRAPH_FILL_BOX_FOREGROUND: _k_graph_rebuild_port_with_color(s, port->color); port = s->port; redraw_port = 1; break; case K_GRAPH_FILL_BOX_ANY: { gfx_color_t color = graph_map_color(s, SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1)); color.mask = (byte)UKPV(5); SCIkdebug(SCIkGRAPHICS, "fill_box_any((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n", SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1), UKPV(5)); ADD_TO_CURRENT_BG_WIDGETS(gfxw_new_box(s->gfx_state, area, color, color, GFX_BOX_SHADE_FLAT)); } break; case K_GRAPH_UPDATE_BOX: { SCIkdebug(SCIkGRAPHICS, "update_box(%d, %d, %d, %d)\n", SKPV(1), SKPV(2), SKPV(3), SKPV(4)); area.x += s->port->zone.x; area.y += s->port->zone.y; gfxop_update_box(s->gfx_state, area); } break; case K_GRAPH_REDRAW_BOX: { SCIkdebug(SCIkGRAPHICS, "redraw_box(%d, %d, %d, %d)\n", SKPV(1), SKPV(2), SKPV(3), SKPV(4)); area.x += s->port->zone.x; area.y += s->port->zone.y; if (s->dyn_views && s->dyn_views->parent == GFXWC(s->port)) s->dyn_views->draw(GFXW(s->dyn_views), gfx_point(0, 0)); gfxop_update_box(s->gfx_state, area); } break; case K_GRAPH_ADJUST_PRIORITY: SCIkdebug(SCIkGRAPHICS, "adjust_priority(%d, %d)\n", SKPV(1), SKPV(2)); s->priority_first = SKPV(1) - 10; s->priority_last = SKPV(2) - 10; break; default: SCIkdebug(SCIkSTUB, "Unhandled Graph() operation %04x\n", SKPV(0)); } if (redraw_port) FULL_REDRAW(); gfxop_update(s->gfx_state); return s->r_acc; } reg_t kTextSize(state_t *s, int funct_nr, int argc, reg_t *argv) { int width, height; char *text = argv[1].segment ? (char *) kernel_dereference_bulk_pointer(s, argv[1], 0) : NULL; reg_t *dest = kernel_dereference_reg_pointer(s, argv[0], 4); int maxwidth = KP_UINT(KP_ALT(3, NULL_REG)); int font_nr = KP_UINT(argv[2]); if (maxwidth < 0) maxwidth = 0; dest[0] = dest[1] = NULL_REG; if (!text || !*text || !dest) { /* Empty text */ dest[2] = dest[3] = make_reg(0, 0); SCIkdebug(SCIkSTRINGS, "GetTextSize: Empty string\n"); return s->r_acc; } GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text, maxwidth? maxwidth : MAX_TEXT_WIDTH_MAGIC_VALUE, &width, &height, 0, NULL, NULL, NULL)); SCIkdebug(SCIkSTRINGS, "GetTextSize '%s' -> %dx%d\n", text, width, height); dest[2] = make_reg(0, height); // dest[3] = make_reg(0, maxwidth? maxwidth : width); dest[3] = make_reg(0, width); return s->r_acc; } int debug_sleeptime_factor = 1; reg_t kWait(state_t *s, int funct_nr, int argc, reg_t *argv) { GTimeVal time; int sleep_time = UKPV(0); sci_get_current_time (&time); s->r_acc = make_reg(0, ((time.tv_usec - s->last_wait_time.tv_usec) * 60 / 1000000) + (time.tv_sec - s->last_wait_time.tv_sec) * 60); memcpy(&(s->last_wait_time), &time, sizeof(GTimeVal)); /* Reset optimization flags: Game is playing along nicely anyway */ s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT | KERNEL_OPT_FLAG_GOT_2NDEVENT); sleep_time *= debug_sleeptime_factor; GFX_ASSERT(gfxop_usleep(s->gfx_state, sleep_time * 1000000 / 60)); return s->r_acc; } reg_t kCoordPri(state_t *s, int funct_nr, int argc, reg_t *argv) { int y = SKPV(0); return make_reg(0, VIEW_PRIORITY(y)); } reg_t kPriCoord(state_t *s, int funct_nr, int argc, reg_t *argv) { int priority = SKPV(0); return make_reg(0, PRIORITY_BAND_FIRST(priority)); } void _k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr, int argc, reg_t *argv) { int view = GET_SEL32V(obj, view); int signal = GET_SEL32V(obj, signal); int loop; int maxloops; if (signal & _K_VIEW_SIG_FLAG_DOESNT_TURN) return; angle %= 360; if (s->version >= SCI_VERSION_FTU_2ND_ANGLES) { if (angle < 45) loop = 3; else if (angle < 136) loop = 0; else if (angle < 225) loop = 2; else if (angle < 316) loop = 1; else loop = 3; } else { if (angle >= 330 || angle <= 30) loop = 3; else if (angle <= 150) loop = 0; else if (angle <= 210) loop = 2; else if (angle < 330) loop = 1; else loop = 0xffff; } maxloops = gfxop_lookup_view_get_loops(s->gfx_state, view); if (maxloops == GFX_ERROR) { SCIkwarn(SCIkERROR, "Invalid view.%03d\n", view); return; } else if ((loop>1)&&(maxloops < 4)) return; PUT_SEL32V(obj, loop, loop); } reg_t kDirLoop(state_t *s, int funct_nr, int argc, reg_t *argv) { _k_dirloop(argv[0], UKPV(1), s, funct_nr, argc, argv); return s->r_acc; } #define GASEOUS_VIEW_MASK_ACTIVE (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR) #define GASEOUS_VIEW_MASK_PASSIVE (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR) abs_rect_t set_base(struct _state *s, reg_t object); inline abs_rect_t get_nsrect(struct _state *s, reg_t object, byte clip); static inline abs_rect_t nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority); static int collides_with(state_t *s, abs_rect_t area, reg_t other_obj, int use_nsrect, int view_mask, int funct_nr, int argc, reg_t *argv) { int other_signal = GET_SEL32V(other_obj, signal); int other_priority = GET_SEL32V(other_obj, priority); int y = GET_SEL32SV(other_obj, y); abs_rect_t other_area; if (use_nsrect) { other_area = get_nsrect(s, other_obj, 0); other_area = nsrect_clip(s, y, other_area, other_priority); } else { other_area.x = GET_SEL32V(other_obj, brLeft); other_area.xend = GET_SEL32V(other_obj, brRight); other_area.y = GET_SEL32V(other_obj, brTop); other_area.yend = GET_SEL32V(other_obj, brBottom); } if (other_area.xend < 0 || other_area.yend < 0 || area.xend < 0 || area.yend < 0) return 0; /* Out of scope */ if (other_area.x >= 320 || other_area.y >= 190 || area.xend >= 320 || area.yend >= 190) return 0; /* Out of scope */ SCIkdebug(SCIkBRESEN, "OtherSignal=%04x, z=%04x obj="PREG"\n", other_signal, (other_signal & view_mask), PRINT_REG(other_obj)); if ((other_signal & (view_mask)) == 0) { /* check whether the other object ignores actors */ SCIkdebug(SCIkBRESEN, " against (%d,%d) to (%d,%d)\n", other_area.x, other_area.y, other_area.xend, other_area.yend); if (((other_area.xend > area.x) && (other_area.x < area.xend)) /* [other_x, other_xend] intersects [x, xend])? */ && ((other_area.yend > area.y) && (other_area.y < area.yend))) /* [other_y, other_yend] intersects [y, yend]? */ return 1; /* CR (from :Bob Heitman:) Collision rects have Mac semantics, ((0,0),(1,1)) only ** covers the coordinate (0,0) */ } SCIkdebug(SCIkBRESEN, " (no)\n"); return 0; } reg_t kCanBeHere(state_t *s, int funct_nr, int argc, reg_t * argv) { reg_t obj = argv[0]; reg_t cliplist_ref = KP_ALT(1, NULL_REG); list_t *cliplist = NULL; gfxw_port_t *port = s->picture_port; word signal; int retval; abs_rect_t abs_zone; rect_t zone; word edgehit; word illegal_bits; abs_zone.x = GET_SEL32SV(obj, brLeft); abs_zone.xend = GET_SEL32SV(obj, brRight); abs_zone.y = GET_SEL32SV(obj, brTop); abs_zone.yend = GET_SEL32SV(obj, brBottom); zone = gfx_rect(abs_zone.x + port->zone.x, abs_zone.y + port->zone.y, abs_zone.xend - abs_zone.x, abs_zone.yend - abs_zone.y); signal = GET_SEL32V(obj, signal); SCIkdebug(SCIkBRESEN,"Checking collision: (%d,%d) to (%d,%d) ([%d..%d]x[%d..%d]), obj="PREG", sig=%04x, cliplist="PREG"\n", GFX_PRINT_RECT(zone), abs_zone.x, abs_zone.xend, abs_zone.y, abs_zone.yend, PRINT_REG(obj), signal, PRINT_REG(cliplist_ref)); illegal_bits = GET_SEL32V(obj, illegalBits); retval = !(illegal_bits & (edgehit = gfxop_scan_bitmask(s->gfx_state, zone, GFX_MASK_CONTROL))); SCIkdebug(SCIkBRESEN, "edgehit = %04x (illegalBits %04x)\n", edgehit, illegal_bits); if (retval == 0) { SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); return not_register(s, NULL_REG); /* Can'tBeHere */ } retval = 0; if ((illegal_bits & 0x8000) /* If we are vulnerable to those views at all... */ && s->dyn_views) { /* ...check against all stop-updated dynviews */ gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) s->dyn_views->contents; SCIkdebug(SCIkBRESEN, "Checking vs dynviews:\n"); while (widget) { if (widget->ID && (widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD) && ((widget->ID != obj.segment) || (widget->subID != obj.offset)) && is_object(s, make_reg(widget->ID, widget->subID))) if (collides_with(s, abs_zone, make_reg(widget->ID, widget->subID), 1, GASEOUS_VIEW_MASK_ACTIVE, funct_nr, argc, argv)) return not_register(s, NULL_REG); widget = (gfxw_dyn_view_t *) widget->next; } } if (signal & GASEOUS_VIEW_MASK_ACTIVE) { retval = signal & GASEOUS_VIEW_MASK_ACTIVE; /* CanBeHere- it's either being disposed, or it ignores actors anyway */ SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); return not_register(s, make_reg(0, retval)); /* CanBeHere */ } if (cliplist_ref.segment) cliplist = LOOKUP_LIST(cliplist_ref); if (cliplist) { node_t *node = LOOKUP_NODE(cliplist->first); retval = 0; /* Assume that we Can'tBeHere... */ while (node) { /* Check each object in the list against our bounding rectangle */ reg_t other_obj = node->value; SCIkdebug(SCIkBRESEN, " comparing against "PREG"\n", PRINT_REG(other_obj)); if (!is_object(s, other_obj)) { SCIkdebug(SCIkWARNING, "CanBeHere() cliplist contains non-object %04x\n", other_obj); } else if (!REG_EQ(other_obj, obj)) { /* Clipping against yourself is not recommended */ if (collides_with(s, abs_zone, other_obj, 0, GASEOUS_VIEW_MASK_PASSIVE, funct_nr, argc, argv)) { SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); return not_register(s, NULL_REG); } } /* if (other_obj != obj) */ node = LOOKUP_NODE(node->succ); /* move on */ } } if (!retval) retval = 1; SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); return not_register(s, make_reg(0, retval)); } /* CanBeHere */ reg_t kIsItSkip(state_t *s, int funct_nr, int argc, reg_t *argv) { int view = SKPV(0); int loop = SKPV(1); int cel = SKPV(2); int x = UKPV(3); int y = UKPV(4); gfxr_view_t *res = NULL; gfx_pixmap_t *pxm = NULL; if (!(res = gfxr_get_view(s->gfx_state->resstate, view, &loop, &cel, 0))) { GFXWARN("Attempt to get cel parameters for invalid view %d\n", view); return make_reg(0, -1); } pxm = res->loops[loop].cels[cel]; if (x > pxm->index_xl) x = pxm->index_xl-1; if (y > pxm->index_yl) y = pxm->index_yl-1; return make_reg(0, pxm->index_data[y*pxm->index_xl+x] == pxm->color_key); } reg_t kCelHigh(state_t *s, int funct_nr, int argc, reg_t *argv) { int view = SKPV(0); int loop = SKPV(1); int cel = SKPV(2); int height, width; point_t offset; if (argc != 3) { SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc); } if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) { SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view); return NULL_REG; } else return make_reg(0, height); } reg_t kCelWide(state_t *s, int funct_nr, int argc, reg_t *argv) { int view = SKPV(0); int loop = SKPV(1); int cel = SKPV(2); int height, width; point_t offset; if (argc != 3) { SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc); } if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) { SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view); return NULL_REG; } else return make_reg(0, width); } reg_t kNumLoops(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = argv[0]; int view = GET_SEL32V(obj, view); int loops_nr = gfxop_lookup_view_get_loops(s->gfx_state, view); if (loops_nr < 0) { SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view); return NULL_REG; } SCIkdebug(SCIkGRAPHICS, "NumLoops(view.%d) = %d\n", view, loops_nr); return make_reg(0, loops_nr); } reg_t kNumCels(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = argv[0]; int loop = GET_SEL32V(obj, loop); int view = GET_SEL32V(obj, view); int cel = 0xffff; if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { /* OK, this is a hack and there's a ** real function to calculate cel numbers... */ SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view); return NULL_REG; } SCIkdebug(SCIkGRAPHICS, "NumCels(view.%d, %d) = %d\n", view, loop, cel+1); return make_reg(0, cel + 1); } reg_t kOnControl(state_t *s, int funct_nr, int argc, reg_t *argv) { int arg = 0; gfx_map_mask_t map; int xstart, ystart; int xlen = 1, ylen = 1; if (argc == 2 || argc == 4) map = GFX_MASK_CONTROL; else { arg = 1; map = (gfx_map_mask_t) SKPV(0); } ystart = SKPV(arg+1); xstart = SKPV(arg); if (argc > 3) { ylen = SKPV(arg+3) - ystart; xlen = SKPV(arg+2) - xstart; } return make_reg(0, gfxop_scan_bitmask(s->gfx_state, gfx_rect(xstart, ystart + 10, xlen, ylen), map)); } void _k_view_list_free_backgrounds(state_t *s, view_object_t *list, int list_nr); int sci01_priority_table_flags = 0; reg_t kDrawPic(state_t *s, int funct_nr, int argc, reg_t *argv) { int pic_nr = SKPV(0); int add_to_pic = 1; int palette = SKPV_OR_ALT(3, 0); gfx_color_t transparent = s->wm_port->bgcolor; CHECK_THIS_KERNEL_FUNCTION; if (s->version < SCI_VERSION_FTU_NEWER_DRAWPIC_PARAMETERS) { if (!SKPV_OR_ALT(2, 0)) add_to_pic = 0; } else if (SKPV_OR_ALT(2, 1)) add_to_pic = 0; gfxop_disable_dirty_frames(s->gfx_state); if (NULL != s->old_screen) { gfxop_free_pixmap(s->gfx_state, s->old_screen); } s->old_screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190)); SCIkdebug(SCIkGRAPHICS,"Drawing pic.%03d\n", SKPV(0)); if (!s->pics) { s->pics = (drawn_pic_t*)sci_malloc(sizeof(drawn_pic_t) * (s->pics_nr = 8)); s->pics_drawn_nr = 0; } if (add_to_pic) { if (s->pics_nr == s->pics_drawn_nr) { s->pics_nr += 4; s->pics = (drawn_pic_t*)sci_realloc(s->pics, sizeof(drawn_pic_t) * s->pics_nr); } s->pics[s->pics_drawn_nr].palette = palette; s->pics[s->pics_drawn_nr++].nr = pic_nr; GFX_ASSERT(gfxop_add_to_pic(s->gfx_state, pic_nr, 1, palette)); } else { s->pics_drawn_nr = 1; s->pics[0].nr = pic_nr; s->pics[0].palette = palette; GFX_ASSERT(gfxop_new_pic(s->gfx_state, pic_nr, 1, palette)); } gfxw_widget_kill_chrono(s->visual, 0); s->wm_port->widfree(GFXW(s->wm_port)); s->picture_port->widfree(GFXW(s->picture_port)); s->iconbar_port->widfree(GFXW(s->iconbar_port)); s->wm_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); s->picture_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); s->iconbar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 200), s->ega_colors[0], transparent); s->iconbar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH; s->visual->add(GFXWC(s->visual), GFXW(s->picture_port)); s->visual->add(GFXWC(s->visual), GFXW(s->wm_port)); s->visual->add(GFXWC(s->visual), GFXW(s->iconbar_port)); s->port = s->picture_port; s->pic_priority_table = (int*)gfxop_get_pic_metainfo(s->gfx_state); if (sci01_priority_table_flags & 0x2) { if (s->pic_priority_table) { int i; fprintf(stderr,"---------------------------\nPriority table:\n"); for (i = 0; i < 16; i++) fprintf(stderr,"\t%d:\t%d\n", i, s->pic_priority_table[i]); fprintf(stderr,"---------------------------\n"); } } if (sci01_priority_table_flags & 0x1) s->pic_priority_table = NULL; if (argc > 1) s->pic_animate = SKPV(1); /* The animation used during kAnimate() later on */ s->dyn_views = NULL; s->drop_views = NULL; s->priority_first = 42; if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES) s->priority_last = 200; else s->priority_last = 190; s->pic_not_valid = 1; s->pic_is_new = 1; return s->r_acc; } abs_rect_t set_base(state_t *s, reg_t object) { int x, y, original_y, z, ystep, xsize, ysize; int xbase, ybase, xend, yend; int view, loop, cel; int oldloop, oldcel; int xmod = 0, ymod = 0; abs_rect_t retval; x = GET_SEL32SV(object, x); original_y = y = GET_SEL32SV(object, y); if (s->selector_map.z > -1) z = GET_SEL32SV(object, z); else z = 0; y -= z; /* Subtract z offset */ ystep = GET_SEL32SV(object, yStep); view = GET_SEL32SV(object, view); oldloop = loop = sign_extend_byte(GET_SEL32V(object, loop)); oldcel = cel = sign_extend_byte(GET_SEL32V(object, cel)); if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { xsize = ysize = xmod = ymod = 0; } else { point_t offset = gfx_point(0, 0); if (loop != oldloop) { loop = 0; PUT_SEL32V(object, loop, 0); SCIkdebug(SCIkGRAPHICS, "Resetting loop for "PREG"!\n", PRINT_REG(object)); } if (cel != oldcel) { cel = 0; PUT_SEL32V(object, cel, 0); } gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &xsize, &ysize, &offset); xmod = offset.x; ymod = offset.y; } xbase = x - xmod - (xsize >> 1); xend = xbase + xsize; yend = y /* - ymod */ + 1; ybase = yend - ystep; SCIkdebug(SCIkBASESETTER, "(%d,%d)+/-(%d,%d), (%d x %d) -> (%d, %d) to (%d, %d)\n", x, y, xmod, ymod, xsize, ysize, xbase, ybase, xend, yend); retval.x = xbase; retval.y = ybase; retval.xend = xend; retval.yend = yend; return retval; } void _k_base_setter(state_t *s, reg_t object) { abs_rect_t absrect = set_base(s, object); if (lookup_selector(s, object, s->selector_map.brLeft, NULL, NULL) != SELECTOR_VARIABLE) return; /* non-fatal */ if (s->version <= SCI_VERSION_LTU_BASE_OB1) --absrect.y; /* Compensate for early SCI OB1 'bug' */ PUT_SEL32V(object, brLeft, absrect.x); PUT_SEL32V(object, brRight, absrect.xend); PUT_SEL32V(object, brTop, absrect.y); PUT_SEL32V(object, brBottom, absrect.yend); } reg_t kBaseSetter(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t object = argv[0]; _k_base_setter(s, object); return s->r_acc; } /* kBaseSetter */ static inline abs_rect_t nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority) { int pri_top; if (priority == -1) priority = VIEW_PRIORITY(y); pri_top = PRIORITY_BAND_FIRST(priority) + 1; /* +1: Don't know why, but this seems to be happening */ if (retval.y < pri_top) retval.y = pri_top; if (retval.yend < retval.y) retval.y = retval.yend - 1; return retval; } inline abs_rect_t calculate_nsrect(state_t *s, int x, int y, int view, int loop, int cel) { int xbase, ybase, xend, yend, xsize, ysize; int xmod = 0, ymod = 0; abs_rect_t retval = {0,0,0,0}; if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { xsize = ysize = xmod = ymod = 0; } else { point_t offset = gfx_point(0, 0); gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &xsize, &ysize, &offset); xmod = offset.x; ymod = offset.y; } xbase = x - xmod - (xsize >> 1); xend = xbase + xsize; yend = y - ymod + 1; /* +1: magic modifier */ ybase = yend - ysize; retval.x = xbase; retval.y = ybase; retval.xend = xend; retval.yend = yend; return retval; } inline abs_rect_t get_nsrect(state_t *s, reg_t object, byte clip) { int x, y, z; int view, loop, cel; abs_rect_t retval; x = GET_SEL32SV(object, x); y = GET_SEL32SV(object, y); if (s->selector_map.z > -1) z = GET_SEL32SV(object, z); else z = 0; y -= z; /* Subtract z offset */ view = GET_SEL32SV(object, view); loop = sign_extend_byte(GET_SEL32SV(object, loop)); cel = sign_extend_byte(GET_SEL32SV(object, cel)); retval = calculate_nsrect(s, x, y, view, loop, cel); if (clip) { int priority = GET_SEL32SV(object, priority); return nsrect_clip(s, y, retval, priority); } return retval; } static void _k_set_now_seen(state_t *s, reg_t object) { abs_rect_t absrect = get_nsrect(s, object, 0); if (lookup_selector(s, object, s->selector_map.nsTop, NULL, NULL) != SELECTOR_VARIABLE) { return; } /* This isn't fatal */ PUT_SEL32V(object, nsLeft, absrect.x); PUT_SEL32V(object, nsRight, absrect.xend); PUT_SEL32V(object, nsTop, absrect.y); PUT_SEL32V(object, nsBottom, absrect.yend); } reg_t kSetNowSeen(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t object = argv[0]; _k_set_now_seen(s, object); return s->r_acc; } /* kSetNowSeen */ reg_t kPalette(state_t *s, int funct_nr, int argc, reg_t *argv) { switch (UKPV(0)) { case 5 : { int r = UKPV(1); int g = UKPV(2); int b = UKPV(3); int i, delta, bestindex = -1, bestdelta = 200000; for (i = 0; i < KERNEL_COLORS_NR; i++) { int dr = abs (KERNEL_COLOR_PALETTE[i].r - r); int dg = abs (KERNEL_COLOR_PALETTE[i].g - g); int db = abs (KERNEL_COLOR_PALETTE[i].b - b); delta = dr*dr + dg * dg + db * db; if (delta < bestdelta) { bestdelta = delta; bestindex = i; } } /* Don't warn about inexact mappings -- it's actually the ** rule rather than the exception */ return make_reg(0, bestindex); } case 4 : case 6 : break; default : SCIkdebug(SCIkWARNING, "Unimplemented subfunction: %d\n", UKPV(0)); } return s->r_acc; } static void _k_draw_control(state_t *s, reg_t obj, int inverse); static void _k_disable_delete_for_now(state_t *s, reg_t obj) { reg_t text_pos = GET_SEL32(obj, text); char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL); int type = GET_SEL32V(obj, type); int state = GET_SEL32V(obj, state); if (type == K_CONTROL_BUTTON && text && !strcmp(s->game_name, "sq4") && s->version < SCI_VERSION(1,001,000) && !strcmp(text, " Delete ")) PUT_SEL32V(obj, state, (state | CONTROL_STATE_GRAY) & ~CONTROL_STATE_ENABLED); } reg_t kDrawControl(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = argv[0]; _k_disable_delete_for_now(s, obj); _k_draw_control(s, obj, 0); FULL_REDRAW(); return NULL_REG; } reg_t kHiliteControl(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = argv[0]; _k_draw_control(s, obj, 1); return s->r_acc; } void update_cursor_limits(int *display_offset, int *cursor, int max_displayed) { if (*cursor < *display_offset + 4) { if (*cursor < 8) *display_offset = 0; else *display_offset = *cursor - 8; } else if (*cursor - *display_offset > max_displayed - 8) *display_offset = 12 + *cursor - max_displayed; } #define _K_EDIT_DELETE \ if (cursor < textlen) { \ memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ } #define _K_EDIT_BACKSPACE \ if (cursor) { \ --cursor; \ memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ --textlen; \ } reg_t kEditControl(state_t *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = argv[0]; reg_t event = argv[1]; if (obj.segment) { word ct_type = GET_SEL32V(obj, type); switch (ct_type) { case 0: break; /* NOP */ case K_CONTROL_EDIT: if (event.segment && ((GET_SEL32V(event, type)) == SCI_EVT_KEYBOARD)) { int max_displayed = GET_SEL32V(obj, max); int max = max_displayed; int cursor = GET_SEL32V(obj, cursor); int modifiers = GET_SEL32V(event, modifiers); int key = GET_SEL32V(event, message); reg_t text_pos = GET_SEL32(obj, text); int display_offset = 0; char *text = (char *) sm_dereference(&s->seg_manager, text_pos, NULL); int textlen; if (!text) { SCIkdebug(SCIkWARNING, "Could not draw control: "PREG" does not reference text!\n", PRINT_REG(text_pos)); return s->r_acc; } if (REG_EQ(text_pos, s->save_dir_copy)) { max = MAX_SAVE_DIR_SIZE - 1; display_offset = s->save_dir_edit_offset; } textlen = strlen(text); cursor += display_offset; if (cursor > textlen) cursor = textlen; if (modifiers & SCI_EVM_CTRL) { switch (tolower((char)key)) { case 'a': cursor = 0; break; case 'e': cursor = textlen; break; case 'f': if (cursor < textlen) ++cursor; break; case 'b': if (cursor > 0) --cursor; break; case 'k': text[cursor] = 0; break; /* Terminate string */ case 'h': _K_EDIT_BACKSPACE; break; case 'd': _K_EDIT_DELETE; break; } PUT_SEL32V(event, claimed, 1); } else if (modifiers & SCI_EVM_ALT) { /* Ctrl has precedence over Alt */ switch (key) { case 0x2100 /* A-f */: while ((cursor < textlen) && (text[cursor++] != ' ')); break; case 0x3000 /* A-b */: while ((cursor > 0) && (text[--cursor - 1] != ' ')); break; case 0x2000 /* A-d */: { while ((cursor < textlen) && (text[cursor] == ' ')) { _K_EDIT_DELETE; textlen--; } while ((cursor < textlen) && (text[cursor] != ' ')) { _K_EDIT_DELETE; textlen--; } break; } } PUT_SEL32V(event, claimed, 1); } else if (key < 31) { PUT_SEL32V(event, claimed, 1); switch(key) { case SCI_K_BACKSPACE: _K_EDIT_BACKSPACE; break; default: PUT_SEL32V(event, claimed, 0); } } else if (key & 0xff00) { switch(key) { case SCI_K_HOME: cursor = 0; break; case SCI_K_END: cursor = textlen; break; case SCI_K_RIGHT: if (cursor + 1 <= textlen) ++cursor; break; case SCI_K_LEFT: if (cursor > 0) --cursor; break; case SCI_K_DELETE: _K_EDIT_DELETE; break; } PUT_SEL32V(event, claimed, 1); } else if ((key > 31) && (key < 128)) { int inserting = (modifiers & SCI_EVM_INSERT); modifiers &= ~(SCI_EVM_RSHIFT | SCI_EVM_LSHIFT | SCI_EVM_CAPSLOCK); if (cursor == textlen) { if (textlen < max) { text[cursor++] = key; text[cursor] = 0; /* Terminate string */ } } else if (inserting) { if (textlen < max) { int i; for (i = textlen + 2; i >= cursor; i--) text[i] = text[i - 1]; text[cursor++] = key; } } else { /* Overwriting */ text[cursor++] = key; } if (max_displayed < max) update_cursor_limits(&display_offset, &cursor, max_displayed); if (REG_EQ(text_pos, s->save_dir_copy)) s->save_dir_edit_offset = display_offset; cursor -= display_offset; PUT_SEL32V(event, claimed, 1); } PUT_SEL32V(obj, cursor, cursor); /* Write back cursor position */ } case K_CONTROL_ICON: case K_CONTROL_BOX: case K_CONTROL_BUTTON: if (event.segment) PUT_SEL32V(event, claimed, 1); _k_draw_control(s, obj, 0); return NULL_REG; break; case K_CONTROL_TEXT: { int state = GET_SEL32V(obj, state); PUT_SEL32V(obj, state, state | CONTROL_STATE_DITHER_FRAMED); _k_draw_control(s, obj, 0); PUT_SEL32V(obj, state, state); } break; default: SCIkwarn(SCIkWARNING, "Attempt to edit control type %d\n", ct_type); } } return s->r_acc; } static void _k_draw_control(state_t *s, reg_t obj, int inverse) { int x = GET_SEL32SV(obj, nsLeft); int y = GET_SEL32SV(obj, nsTop); int xl = GET_SEL32SV(obj, nsRight) - x; int yl = GET_SEL32SV(obj, nsBottom) - y; rect_t area = gfx_rect(x, y, xl, yl); int font_nr = GET_SEL32V(obj, font); reg_t text_pos = GET_SEL32(obj, text); char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL); int view = GET_SEL32V(obj, view); int cel = sign_extend_byte(GET_SEL32V(obj, cel)); int loop = sign_extend_byte(GET_SEL32V(obj, loop)); gfx_alignment_t mode; int type = GET_SEL32V(obj, type); int state = GET_SEL32V(obj, state); int cursor; int max; if (REG_EQ(text_pos, s->save_dir_copy)) { SCIkdebug(SCIkGRAPHICS, "Displaying the save_dir copy\n"); } switch (type) { case K_CONTROL_BUTTON: SCIkdebug(SCIkGRAPHICS, "drawing button "PREG" to %d,%d\n", PRINT_REG(obj), x, y); ADD_TO_CURRENT_BG_WIDGETS(sciw_new_button_control(s->port, obj, area, text, font_nr, (gint8)(state & CONTROL_STATE_FRAMED), (gint8)inverse, (gint8)(state & CONTROL_STATE_GRAY))); break; case K_CONTROL_TEXT: mode = (gfx_alignment_t) GET_SEL32V(obj, mode); SCIkdebug(SCIkGRAPHICS, "drawing text "PREG" to %d,%d, mode=%d\n", PRINT_REG(obj), x, y, mode); ADD_TO_CURRENT_BG_WIDGETS( sciw_new_text_control(s->port, obj, area, text, font_nr, mode, (gint8)(!!(state & CONTROL_STATE_DITHER_FRAMED)), (gint8)inverse)); break; case K_CONTROL_EDIT: SCIkdebug(SCIkGRAPHICS, "drawing edit control "PREG" to %d,%d\n", PRINT_REG(obj), x, y); max = GET_SEL32V(obj, max); cursor = GET_SEL32V(obj, cursor); if (cursor > (signed)strlen(text)) cursor = strlen(text); if (REG_EQ(text_pos, s->save_dir_copy)) update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); ADD_TO_CURRENT_BG_WIDGETS(sciw_new_edit_control(s->port, obj, area, text, font_nr, (unsigned)cursor, (gint8)inverse)); break; case K_CONTROL_ICON: SCIkdebug(SCIkGRAPHICS, "drawing icon control "PREG" to %d,%d\n", PRINT_REG(obj), x, y -1); ADD_TO_CURRENT_BG_WIDGETS(sciw_new_icon_control(s->port, obj, area, view, loop, cel, (gint8)(state & CONTROL_STATE_FRAMED), (gint8)inverse)); break; case K_CONTROL_CONTROL: case K_CONTROL_CONTROL_ALIAS: { char **entries_list = NULL; char *seeker; int entries_nr; int lsTop = GET_SEL32V(obj, lsTop)-text_pos.offset; int list_top = 0; int selection = 0; int entry_size = GET_SEL32V(obj, x); int i; SCIkdebug(SCIkGRAPHICS, "drawing list control %04x to %d,%d, diff %d\n", obj, x, y, SCI_MAX_SAVENAME_LENGTH); cursor = GET_SEL32V(obj, cursor) - text_pos.offset; entries_nr = 0; seeker = text; while (seeker[0]) { /* Count string entries in NULL terminated string list */ ++entries_nr; seeker += entry_size; } if (entries_nr) { /* determine list_top, selection, and the entries_list */ seeker = text; entries_list = (char**)sci_malloc(sizeof(char *) * entries_nr); for (i = 0; i < entries_nr; i++) { entries_list[i] = seeker; seeker += entry_size ; if ((seeker - text) == lsTop) list_top = i + 1; if ((seeker - text) == cursor) selection = i + 1; } } ADD_TO_CURRENT_BG_WIDGETS(sciw_new_list_control(s->port, obj, area, font_nr, entries_list, entries_nr, list_top, selection, (gint8)inverse)); if (entries_nr) free(entries_list); } break; case K_CONTROL_BOX: break; default: SCIkwarn(SCIkWARNING, "Unknown control type: %d at "PREG", at (%d, %d) size %d x %d\n", type, PRINT_REG(obj), x, y, xl, yl); } if (!s->pic_not_valid) { FULL_REDRAW(); } } static void draw_rect_to_control_map(state_t *s, abs_rect_t abs_zone) { gfxw_box_t *box; gfx_color_t color; gfxop_set_color(s->gfx_state, &color, -1, -1, -1, -1, -1, 0xf); SCIkdebug(SCIkGRAPHICS," adding control block (%d,%d)to(%d,%d)\n", abs_zone.x, abs_zone.y, abs_zone.xend, abs_zone.yend); box = gfxw_new_box(s->gfx_state, gfx_rect(abs_zone.x, abs_zone.y, abs_zone.xend - abs_zone.x, abs_zone.yend - abs_zone.y), color, color, GFX_BOX_SHADE_FLAT); assert_primary_widget_lists(s); ADD_TO_CURRENT_PICTURE_PORT(box); } static inline void draw_obj_to_control_map(state_t *s, gfxw_dyn_view_t *view) { reg_t obj = make_reg(view->ID, view->subID); if (!is_object(s, obj)) SCIkwarn(SCIkWARNING, "View %d does not contain valid object reference "PREG"\n", view->ID, PRINT_REG(obj)); if (!(view->signalp && (((reg_t *)view->signalp)->offset & _K_VIEW_SIG_FLAG_IGNORE_ACTOR))) { abs_rect_t abs_zone = get_nsrect(s, make_reg(view->ID, view->subID), 1); draw_rect_to_control_map (s, abs_zone); } } static void _k_view_list_do_postdraw(state_t *s, gfxw_list_t *list) { gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents; while (widget) { reg_t obj = make_reg(widget->ID, widget->subID); if (widget->type == GFXW_SORTED_LIST) _k_view_list_do_postdraw(s, GFXWC(widget)); if (widget->type != GFXW_DYN_VIEW) { widget = (gfxw_dyn_view_t *) widget->next; continue; } /* * this fixes a few problems, but doesn't match SSCI's logic. * The semantics of the private flag need to be verified before this can be uncommented. * Fixes bug #326 (CB1, ego falls down stairs) * if ((widget->signal & (_K_VIEW_SIG_FLAG_FREESCI_PRIVATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == _K_VIEW_SIG_FLAG_FREESCI_PRIVATE) { */ if ((widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == 0) { int has_nsrect = lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE; if (has_nsrect) { int temp; temp = GET_SEL32V(obj, nsLeft); PUT_SEL32V(obj, lsLeft, temp); temp = GET_SEL32V(obj, nsRight); PUT_SEL32V(obj, lsRight, temp); temp = GET_SEL32V(obj, nsTop); PUT_SEL32V(obj, lsTop, temp); temp = GET_SEL32V(obj, nsBottom); PUT_SEL32V(obj, lsBottom, temp); #ifdef DEBUG_LSRECT fprintf(stderr, "lsRected "PREG"\n", PRINT_REG(obj)); #endif } #ifdef DEBUG_LSRECT else fprintf(stderr, "Not lsRecting "PREG" because %d\n", PRINT_REG(obj), lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL)); #endif if (widget->signal & _K_VIEW_SIG_FLAG_HIDDEN) widget->signal |= _K_VIEW_SIG_FLAG_REMOVE; } #ifdef DEBUG_LSRECT fprintf(stderr, "obj "PREG" has pflags %x\n", PRINT_REG(obj), (widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE))); #endif if (widget->signalp) { *((reg_t *)(widget->signalp)) = make_reg(0, widget->signal & 0xffff); /* Write back signal */ } widget = (gfxw_dyn_view_t *) widget->next; } } void _k_view_list_mark_free(state_t *s, reg_t off) { if (s->dyn_views) { gfxw_dyn_view_t *w = (gfxw_dyn_view_t *) s->dyn_views->contents; while (w) { if (w->ID == off.segment && w->subID == off.offset) { w->under_bitsp = NULL; } w = (gfxw_dyn_view_t *) w->next; } } } static int _k_animate_ran = 0; int _k_view_list_dispose_loop(state_t *s, list_t *list, gfxw_dyn_view_t *widget, int funct_nr, int argc, reg_t *argv) /* disposes all list members flagged for disposal; funct_nr is the invoking kfunction */ /* returns non-zero IFF views were dropped */ { int signal; int dropped = 0; _k_animate_ran = 0; if (widget) { int retval; /* Recurse: */ retval = _k_view_list_dispose_loop(s, list, (gfxw_dyn_view_t *) widget->next, funct_nr, argc, argv); if (retval == -1) /* Bail out on annihilation, rely on re-start from Animate() */ return -1; if (GFXW_IS_DYN_VIEW(widget) && (widget->ID != GFXW_NO_ID)) { signal = ((reg_t *)widget->signalp)->offset; if (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME) { reg_t obj = make_reg(widget->ID, widget->subID); reg_t under_bits = NULL_REG; if (!is_object(s, obj)) { SCIkwarn(SCIkERROR, "Non-object "PREG" present" " in view list during delete time\n", PRINT_REG(obj)); obj = NULL_REG; } else if (widget->under_bitsp) { /* Is there a bg picture left to clean? */ reg_t mem_handle = *((reg_t*)(widget->under_bitsp)); if (mem_handle.segment) { if (!kfree(s, mem_handle)) { *((reg_t*)(widget->under_bitsp)) = make_reg(0, widget->under_bits = 0); } else { SCIkwarn(SCIkWARNING, "Treating viewobj "PREG " as no longer" " present\n", PRINT_REG(obj)); obj = NULL_REG; } } } if (is_object(s, obj)) { if (invoke_selector(INV_SEL(obj, delete, 1), 0)) SCIkwarn(SCIkWARNING, "Object at "PREG" requested deletion, but does not have" " a delete funcselector\n", PRINT_REG(obj)); if (_k_animate_ran) { SCIkwarn(SCIkWARNING, "Object at "PREG" invoked kAnimate() during deletion!\n", PRINT_REG(obj)); return dropped; } if (widget->under_bitsp) under_bits = *((reg_t*)(widget->under_bitsp)); if (under_bits.segment) { *((reg_t*)(widget->under_bitsp)) = make_reg(0, 0); graph_restore_box(s, under_bits); } SCIkdebug(SCIkGRAPHICS, "Freeing "PREG" with signal=%04x\n", PRINT_REG(obj), signal); if (!(signal & _K_VIEW_SIG_FLAG_HIDDEN)) { SCIkdebug(SCIkGRAPHICS, "Adding view at "PREG" to background\n", PRINT_REG(obj)); if (!(gfxw_remove_id(widget->parent, widget->ID, widget->subID) == GFXW(widget))) { SCIkwarn(SCIkERROR, "Attempt to remove view with ID %x:%x from list failed!\n", widget->ID, widget->subID); BREAKPOINT(); } s->drop_views->add(GFXWC(s->drop_views), GFXW(gfxw_picviewize_dynview(widget))); draw_obj_to_control_map(s, widget); widget->draw_bounds.y += s->dyn_views->bounds.y - widget->parent->bounds.y; widget->draw_bounds.x += s->dyn_views->bounds.x - widget->parent->bounds.x; dropped = 1; } else { SCIkdebug(SCIkGRAPHICS, "Deleting view at "PREG"\n", PRINT_REG(obj)); widget->flags |= GFXW_FLAG_VISIBLE; gfxw_annihilate(GFXW(widget)); return -1; /* restart: Done in Animate() */ } } } } } return dropped; } #define _K_MAKE_VIEW_LIST_CYCLE 1 #define _K_MAKE_VIEW_LIST_CALC_PRIORITY 2 #define _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP 4 static gfxw_dyn_view_t * _k_make_dynview_obj(state_t *s, reg_t obj, int options, int nr, int funct_nr, int argc, reg_t *argv) { short oldloop, oldcel; int cel, loop, view_nr = GET_SEL32SV(obj, view); int palette; int signal; reg_t under_bits; reg_t *under_bitsp, *signalp; point_t pos; int z; gfxw_dyn_view_t *widget; SCIkdebug(SCIkGRAPHICS, " - Adding "PREG"\n", PRINT_REG(obj)); obj = obj; pos.x = GET_SEL32SV(obj, x); pos.y = GET_SEL32SV(obj, y); pos.y++; /* magic: Sierra appears to do something like this */ z = GET_SEL32SV(obj, z); /* !-- nsRect used to be checked here! */ loop = oldloop = sign_extend_byte(GET_SEL32V(obj, loop)); cel = oldcel = sign_extend_byte(GET_SEL32V(obj, cel)); if (s->selector_map.palette) palette = GET_SEL32V(obj, palette); else palette = 0; /* Clip loop and cel, write back if neccessary */ if (gfxop_check_cel(s->gfx_state, view_nr, &loop, &cel)) { return NULL; } if (loop != oldloop) loop = 0; if (cel != oldcel) cel = 0; if (oldloop != loop) PUT_SEL32V(obj, loop, loop); if (oldcel != cel) { PUT_SEL32V(obj, cel, cel); } if (lookup_selector(s, obj, s->selector_map.underBits, &(under_bitsp), NULL) != SELECTOR_VARIABLE) { under_bitsp = NULL; under_bits = NULL_REG; SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no underBits\n", PRINT_REG(obj)); } else under_bits = *((reg_t *)under_bitsp); if (lookup_selector(s, obj, s->selector_map.signal, &(signalp), NULL) != SELECTOR_VARIABLE) { signalp = NULL; signal = 0; SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no signal selector\n", PRINT_REG(obj)); } else { signal = signalp->offset; SCIkdebug(SCIkGRAPHICS, " with signal = %04x\n", signal); } widget = gfxw_new_dyn_view(s->gfx_state, pos, z, view_nr, loop, cel, palette, -1, -1, ALIGN_CENTER, ALIGN_BOTTOM, nr); if (widget) { widget = (gfxw_dyn_view_t *) gfxw_set_id(GFXW(widget), obj.segment, obj.offset); widget = gfxw_dyn_view_set_params(widget, under_bits.segment, under_bitsp, signal, signalp); widget->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; /* Only works the first time 'round */ return widget; } else { SCIkwarn(SCIkWARNING, "Could not generate dynview widget for %d/%d/%d\n", view_nr, loop, cel); return NULL; } } static void _k_make_view_list(state_t *s, gfxw_list_t **widget_list, list_t *list, int options, int funct_nr, int argc, reg_t *argv) /* Creates a view_list from a node list in heap space. Returns the list, stores the ** number of list entries in *list_nr. Calls doit for each entry if cycle is set. ** argc, argv, funct_nr should be the same as in the calling kernel function. */ { node_t *node; int sequence_nr = 0; gfxw_dyn_view_t *widget; if (!*widget_list) { SCIkwarn(SCIkERROR, "make_view_list with widget_list == ()\n"); BREAKPOINT(); }; assert_primary_widget_lists(s); /* In case one of the views' doit() does a DrawPic... */ /* Yes, this _does_ happen! */ if (!list) { /* list sanity check */ SCIkwarn(SCIkERROR, "Attempt to make list from non-list!\n"); BREAKPOINT(); } node = LOOKUP_NODE(list->first); while (node) { reg_t obj = node->value; /* The object we're using */ reg_t next_node; gfxw_dyn_view_t *widget; if (options & _K_MAKE_VIEW_LIST_CYCLE) { unsigned int signal = GET_SEL32V(obj, signal); if (!(signal & _K_VIEW_SIG_FLAG_FROZEN)) { SCIkdebug(SCIkGRAPHICS, " invoking "PREG"::doit()\n", PRINT_REG(obj)); invoke_selector(INV_SEL(obj, doit, 1), 0); /* Call obj::doit() if neccessary */ } } next_node = node->succ; /* In case the cast list was changed */ if (list->first.segment == 0 && list->first.offset == 0) /* The cast list was completely emptied! */ break; widget = _k_make_dynview_obj(s, obj, options, sequence_nr--, funct_nr, argc, argv); if (widget) GFX_ASSERT((*widget_list)->add(GFXWC(*widget_list), GFXW(widget))); node = LOOKUP_NODE(next_node); /* Next node */ } widget = (gfxw_dyn_view_t *) (*widget_list)->contents; while(widget) { /* Read back widget values */ if (widget->signalp) widget->signal = ((reg_t *)(widget->signalp))->offset; widget = (gfxw_dyn_view_t *) widget->next; } } static void _k_prepare_view_list(state_t *s, gfxw_list_t *list, int options) { gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents; while (view) { reg_t obj = make_reg(view->ID, view->subID); int priority, _priority; int has_nsrect = (view->ID <=0)? 0 : lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE; int oldsignal = view->signal; _k_set_now_seen(s, obj); _priority = /*GET_SELECTOR(obj, y); */((view->pos.y));/**/ _priority = _find_view_priority(s, _priority - 1); if (options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP) { /* Picview */ priority = GET_SEL32SV(obj, priority); if (priority < 0) priority = _priority; /* Always for picviews */ } else { /* Dynview */ if (has_nsrect && !(view->signal & _K_VIEW_SIG_FLAG_FIX_PRI_ON)) { /* Calculate priority */ if (options & _K_MAKE_VIEW_LIST_CALC_PRIORITY) PUT_SEL32V(obj, priority, _priority); priority = _priority; } else /* DON'T calculate the priority */ priority = GET_SEL32SV(obj, priority); } view->color.priority = priority; if (priority > -1) view->color.mask |= GFX_MASK_PRIORITY; else view->color.mask &= ~GFX_MASK_PRIORITY; /* CR (from :Bob Heitman:) stopupdated views (like pic views) have ** their clipped nsRect drawn to the control map */ if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { view->signal |= _K_VIEW_SIG_FLAG_FREESCI_STOPUPD; SCIkdebug(SCIkGRAPHICS, "Setting magic STOP_UPD for "PREG"\n", PRINT_REG(obj)); } if ((options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP)) draw_obj_to_control_map(s, view); /* Extreme Pattern Matching ugliness ahead... */ if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { if (((view->signal & (_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_FORCE_UPDATE))) /* 9.1.1.1 */ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_HIDDEN) || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_REMOVE) /* 9.1.1.2 */ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) /* 9.1.1.3 */ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE))) /* 9.1.1.4 */ { s->pic_not_valid++; view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; } else if (((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0) || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_HIDDEN)) { view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; } } else { if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { s->pic_not_valid++; view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; } else { /* if not STOP_UPDATE */ if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) s->pic_not_valid++; view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; } } SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x -> %04x\n", PRINT_REG(obj), oldsignal, view->signal); /* Never happens if (view->signal & 0) { view->signal &= ~_K_VIEW_SIG_FLAG_FREESCI_STOPUPD; fprintf(stderr, "Unsetting magic StopUpd for view "PREG"\n", PRINT_REG(obj)); } */ view = (gfxw_dyn_view_t *) view->next; } } static void _k_update_signals_in_view_list(gfxw_list_t *old_list, gfxw_list_t *new_list) { /* O(n^2)... a bit painful, but much faster than the redraws it helps prevent */ gfxw_dyn_view_t *old_widget = (gfxw_dyn_view_t *) old_list->contents; /* Traverses all old widgets, updates them with signals from the new widgets. ** This is done to avoid evil hacks in widget.c; widgets with unique IDs are ** replaced there iff they are NOT equal_to a new widget with the same ID. ** If they were replaced every time, we'd be doing far too many redraws. */ while (old_widget) { gfxw_dyn_view_t *new_widget = (gfxw_dyn_view_t *) new_list->contents; while (new_widget && (new_widget->ID != old_widget->ID || new_widget->subID != old_widget->subID)) new_widget = (gfxw_dyn_view_t *) new_widget->next; if (new_widget) { int carry = old_widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD; /* Transfer 'stopupd' flag */ if ((new_widget->pos.x != old_widget->pos.x) || (new_widget->pos.y != old_widget->pos.y) /* ** No idea why this is supposed to be bad ** || (new_widget->z != old_widget->z) || (new_widget->view != old_widget->view) || (new_widget->loop != old_widget->loop) || (new_widget->cel != old_widget->cel) */ ) carry = 0; old_widget->signal = new_widget->signal |= carry; } old_widget = (gfxw_dyn_view_t *) old_widget->next; } } static void _k_view_list_kryptonize(gfxw_widget_t *v) { if (v) { v->flags &= ~GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; _k_view_list_kryptonize(v->next); } } static void _k_raise_topmost_in_view_list(state_t *s, gfxw_list_t *list, gfxw_dyn_view_t *view) { if (view) { gfxw_dyn_view_t *next = (gfxw_dyn_view_t *) view->next; /* step 11 */ if ((view->signal & (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0) { SCIkdebug(SCIkGRAPHICS, "Forcing precedence 2 at ["PREG"] with %04x\n", PRINT_REG(make_reg(view->ID, view->subID)), view->signal); view->force_precedence = 2; if ((view->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_HIDDEN)) == _K_VIEW_SIG_FLAG_REMOVE) { view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE; } } gfxw_remove_widget_from_container(view->parent, GFXW(view)); gfxw_widget_reparent_chrono(s->visual, GFXW(view), GFXWC(list)); if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN) gfxw_hide_widget(GFXW(view)); else gfxw_show_widget(GFXW(view)); list->add(GFXWC(list), GFXW(view)); _k_raise_topmost_in_view_list(s, list, next); } } static void _k_redraw_view_list(state_t *s, gfxw_list_t *list) { gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents; while (view) { SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x\n", make_reg(view->ID, view->subID), view->signal); /* step 1 of subalgorithm */ if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { if (view->signal & _K_VIEW_SIG_FLAG_FORCE_UPDATE) view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; if (view->signal & _K_VIEW_SIG_FLAG_UPDATED) view->signal &= ~(_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_NO_UPDATE); } else { /* NO_UPD is not set */ if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; view->signal |= _K_VIEW_SIG_FLAG_NO_UPDATE; } } SCIkdebug(SCIkGRAPHICS, " at substep 6: signal %04x\n", view->signal); if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) view->signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE); SCIkdebug(SCIkGRAPHICS, " at substep 11/14: signal %04x\n", view->signal); if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN) view->signal |= _K_VIEW_SIG_FLAG_REMOVE; else view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE; } else if (!(view->signal & _K_VIEW_SIG_FLAG_HIDDEN)) view->force_precedence = 1; SCIkdebug(SCIkGRAPHICS, " -> signal %04x\n", view->signal); view = (gfxw_dyn_view_t *) view->next; } } /* Flags for _k_draw_view_list */ /* Whether some magic with the base object's "signal" selector should be done: */ #define _K_DRAW_VIEW_LIST_USE_SIGNAL 1 /* This flag draws all views with the "DISPOSE_ME" flag set: */ #define _K_DRAW_VIEW_LIST_DISPOSEABLE 2 /* Use this one to draw all views with "DISPOSE_ME" NOT set: */ #define _K_DRAW_VIEW_LIST_NONDISPOSEABLE 4 /* Draw as picviews */ #define _K_DRAW_VIEW_LIST_PICVIEW 8 void _k_draw_view_list(state_t *s, gfxw_list_t *list, int flags) /* Draws list_nr members of list to s->pic. */ { gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents; if (GFXWC(s->port) != GFXWC(s->dyn_views->parent)) return; /* Return if the pictures are meant for a different port */ while (widget) { if (flags & _K_DRAW_VIEW_LIST_PICVIEW) widget = gfxw_picviewize_dynview(widget); if (GFXW_IS_DYN_VIEW(widget) && widget->ID) { word signal = (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL)? ((reg_t *)(widget->signalp))->offset : 0; if (signal & _K_VIEW_SIG_FLAG_HIDDEN) gfxw_hide_widget(GFXW(widget)); else gfxw_show_widget(GFXW(widget)); if (!(flags & _K_DRAW_VIEW_LIST_USE_SIGNAL) || ((flags & _K_DRAW_VIEW_LIST_DISPOSEABLE) && (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME)) || ((flags & _K_DRAW_VIEW_LIST_NONDISPOSEABLE) && !(signal & _K_VIEW_SIG_FLAG_DISPOSE_ME))) { if (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL) { signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE); /* Clear all of those flags */ if (signal & _K_VIEW_SIG_FLAG_HIDDEN) gfxw_hide_widget(GFXW(widget)); else gfxw_show_widget(GFXW(widget)); *((reg_t *)(widget->signalp)) = make_reg(0, signal); /* Write the changes back */ }; } /* ...if we're drawing disposeables and this one is disposeable, or if we're drawing non- ** disposeables and this one isn't disposeable */ } widget = (gfxw_dyn_view_t *) widget->next; } /* while (widget) */ } reg_t kAddToPic(state_t *s, int funct_nr, int argc, reg_t *argv) { gfxw_list_t *pic_views; reg_t list_ref = argv[0]; assert_primary_widget_lists(s); if (argc > 1) { int view, cel, loop, x, y, priority, control; gfxw_widget_t *widget; view = KP_UINT(argv[0]); loop = KP_UINT(argv[1]); cel = KP_UINT(argv[2]); x = KP_SINT(argv[3]); y = KP_SINT(argv[4]) + 1 /* magic + 1 */; priority = KP_SINT(argv[5]); control = KP_SINT(argv[6]); widget = GFXW(gfxw_new_dyn_view(s->gfx_state, gfx_point(x, y), 0, view, loop, cel, 0, priority, -1 /* No priority */ , ALIGN_CENTER, ALIGN_BOTTOM, 0)); if (!widget) { SCIkwarn(SCIkERROR, "Attempt to single-add invalid picview (%d/%d/%d)\n", view, loop, cel); } else { widget->ID = -1; if (control >= 0) { abs_rect_t abs_zone = nsrect_clip(s, y, calculate_nsrect(s, x, y, view, loop, cel), priority); draw_rect_to_control_map(s, abs_zone); } ADD_TO_CURRENT_PICTURE_PORT(gfxw_picviewize_dynview((gfxw_dyn_view_t *) widget)); } } else { list_t *list; if (!list_ref.segment) { SCIkdebug(SCIkWARNING, "Attempt to AddToPic single non-list: "PREG"\n", PRINT_REG(list_ref)); return s->r_acc; } list = LOOKUP_LIST(list_ref); pic_views = gfxw_new_list(s->picture_port->bounds, 1); SCIkdebug(SCIkGRAPHICS, "Preparing picview list...\n"); _k_make_view_list(s, &pic_views, list, 0, funct_nr, argc, argv); _k_prepare_view_list(s, pic_views, _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP); /* Store pic views for later re-use */ SCIkdebug(SCIkGRAPHICS, "Drawing picview list...\n"); ADD_TO_CURRENT_PICTURE_PORT(pic_views); _k_draw_view_list(s, pic_views, _K_DRAW_VIEW_LIST_NONDISPOSEABLE | _K_DRAW_VIEW_LIST_DISPOSEABLE | _K_DRAW_VIEW_LIST_PICVIEW); /* Draw relative to the bottom center */ SCIkdebug(SCIkGRAPHICS, "Returning.\n"); } reparentize_primary_widget_lists(s, s->port); return s->r_acc; } reg_t kGetPort(state_t *s, int funct_nr, int argc, reg_t *argv) { return make_reg(0, s->port->ID); } reg_t kSetPort(state_t *s, int funct_nr, int argc, reg_t *argv) { if (activated_icon_bar && argc == 6) { port_origin_x = port_origin_y = 0; activated_icon_bar = 0; return s->r_acc; } switch (argc) { case 1 : { unsigned int port_nr = SKPV(0); gfxw_port_t *new_port; /* We depart from official semantics here, sorry! Reasoning: Sierra SCI does not clip ports while we do. Therefore a draw to the titlebar port (which is the official semantics) would cut off the lower part of the icons in an SCI1 icon bar. Instead we have an iconbar_port that does not exist in SSCI. */ if (port_nr == -1) port_nr = s->iconbar_port->ID; new_port = gfxw_find_port(s->visual, port_nr); if (!new_port) { SCIkwarn(SCIkERROR, "Invalid port %04x requested\n", port_nr); return NULL_REG; } s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */ s->port = new_port; return s->r_acc; } case 6 : { port_origin_y = SKPV(0); port_origin_x = SKPV(1); if (SKPV(0) == -10) { s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */ s->port = s->iconbar_port; activated_icon_bar = 1; return s->r_acc; } s->gfx_state->options->pic_port_bounds = gfx_rect(UKPV(5), UKPV(4), UKPV(3), UKPV(2)); /* FIXME: Should really only invalidate all loaded pic resources here; this is overkill */ gfxr_free_all_resources(s->gfx_state->driver, s->gfx_state->resstate); break; } default : SCIkwarn(SCIkERROR, "SetPort was called with %d parameters\n", argc); break; } return NULL_REG; } static inline void add_to_chrono(state_t *s, gfxw_widget_t *widget) { gfxw_port_t *chrono_port; gfxw_list_t *tw; chrono_port = gfxw_get_chrono_port(s->visual, &tw, 0); tw->add(GFXWC(tw), widget); if (!chrono_port->parent) ADD_TO_CURRENT_PORT(chrono_port); } reg_t kDrawCel(state_t *s, int funct_nr, int argc, reg_t *argv) { int view = SKPV(0); int loop = SKPV(1); int cel = SKPV(2); int x = SKPV(3); int y = SKPV(4); int priority = SKPV_OR_ALT(5, -1); gfxw_view_t *new_view; /* if (!view) { SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view); return; } */ if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view); return s->r_acc; } SCIkdebug(SCIkGRAPHICS, "DrawCel((%d,%d), (view.%d, %d, %d), p=%d)\n", x, y, view, loop, cel, priority); new_view = gfxw_new_view(s->gfx_state, gfx_point(x, y), view, loop, cel, 0, priority, -1, ALIGN_LEFT, ALIGN_TOP, GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET); #if 0 add_to_chrono(s, GFXW(new_view)); #else ADD_TO_CURRENT_PICTURE_PORT(GFXW(new_view)); #endif FULL_REDRAW(); return s->r_acc; } reg_t kDisposeWindow(state_t *s, int funct_nr, int argc, reg_t *argv) { unsigned int goner_nr = SKPV(0); gfxw_port_t *goner; gfxw_port_t *pred; int id = s->visual->port_refs_nr; gfxw_widget_kill_chrono(s->visual, goner_nr); goner = gfxw_find_port(s->visual, goner_nr); if ((goner_nr < 3) || (goner == NULL)) { SCIkwarn(SCIkERROR, "Removal of invalid window %04x requested\n", goner_nr); return s->r_acc; } if (s->dyn_views && GFXWC(s->dyn_views->parent) == GFXWC(goner)) { reparentize_primary_widget_lists(s, (gfxw_port_t *) goner->parent); } if (s->drop_views && GFXWC(s->drop_views->parent) == GFXWC(goner)) s->drop_views = NULL; /* Kill it */ pred = gfxw_remove_port(s->visual, goner); if (goner == s->port) /* Did we kill the active port? */ s->port = pred; /* Find the last port that exists and that isn't marked no-switch */ while ((!s->visual->port_refs[id] && id >= 0) || (s->visual->port_refs[id]->flags & GFXW_FLAG_NO_IMPLICIT_SWITCH)) id--; sciprintf("Activating port %d after disposing window %d\n", id, goner_nr); s->port = s->visual->port_refs[id]; if (!s->port) s->port = gfxw_find_default_port(s->visual); gfxop_update(s->gfx_state); return s->r_acc; } reg_t kNewWindow(state_t *s, int funct_nr, int argc, reg_t *argv) { gfxw_port_t *window; int x, y, xl, yl, flags; gfx_color_t bgcolor; gfx_color_t fgcolor; gfx_color_t black; gfx_color_t white; int priority; int argextra = argc == 13 ? 4 : 0; /* Triggers in PQ3 */ y = SKPV(0); x = SKPV(1); yl = SKPV(2) - y; xl = SKPV(3) - x; y += s->wm_port->bounds.y; if (x+xl > 319) x -= ((x+xl) - 319); flags = SKPV(5+argextra); priority = SKPV_OR_ALT(6+argextra, -1); bgcolor.mask = 0; if (SKPV_OR_ALT(8+argextra, 255) >= 0) { if (s->resmgr->sci_version < SCI_VERSION_01_VGA) bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 15))); else bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 255))); bgcolor.mask = GFX_MASK_VISUAL; } bgcolor.priority = priority; bgcolor.mask |= priority >= 0 ? GFX_MASK_PRIORITY : 0; bgcolor.alpha = 0; SCIkdebug(SCIkGRAPHICS, "New window with params %d, %d, %d, %d\n", SKPV(0), SKPV(1), SKPV(2), SKPV(3)); fgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(7+argextra, 0))); fgcolor.mask = GFX_MASK_VISUAL; fgcolor.alpha = 0; black.visual = *(get_pic_color(s, 0)); black.mask = GFX_MASK_VISUAL; black.alpha = 0; white.visual = *(get_pic_color(s, s->resmgr->sci_version < SCI_VERSION_01_VGA ? 15 : 255)), white.mask = GFX_MASK_VISUAL; white.alpha = 0; window = sciw_new_window(s, gfx_rect(x, y, xl, yl), s->titlebar_port->font_nr, fgcolor, bgcolor, s->titlebar_port->font_nr, white, black, argv[4+argextra].segment ? kernel_dereference_char_pointer(s, argv[4+argextra], 0) : NULL, flags); /* PQ3 has the interpreter store underBits implicitly. The feature was promptly removed after its release, never to be seen again. */ if (argextra) gfxw_port_auto_restore_background(s->visual, window, gfx_rect(SKPV(5), SKPV(4), SKPV(7)-SKPV(5), SKPV(6)-SKPV(4))); ADD_TO_WINDOW_PORT(window); FULL_REDRAW(); window->draw(GFXW(window), gfxw_point_zero); gfxop_update(s->gfx_state); s->port = window; /* Set active port */ return make_reg(0, window->ID); } #define K_ANIMATE_CENTER_OPEN_H 0 /* horizontally open from center */ #define K_ANIMATE_CENTER_OPEN_V 1 /* vertically open from center */ #define K_ANIMATE_RIGHT_OPEN 2 /* open from right */ #define K_ANIMATE_LEFT_OPEN 3 /* open from left */ #define K_ANIMATE_BOTTOM_OPEN 4 /* open from bottom */ #define K_ANIMATE_TOP_OPEN 5 /* open from top */ #define K_ANIMATE_BORDER_OPEN_F 6 /* open from edges to center */ #define K_ANIMATE_CENTER_OPEN_F 7 /* open from center to edges */ #define K_ANIMATE_OPEN_CHECKERS 8 /* open random checkboard */ #define K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H 9 /* horizontally close to center,reopen from center */ #define K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V 10 /* vertically close to center, reopen from center */ #define K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN 11 /* close to right, reopen from right */ #define K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN 12 /* close to left, reopen from left */ #define K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN 13 /* close to bottom, reopen from bottom */ #define K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN 14 /* close to top, reopen from top */ #define K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F 15 /* close from center to edges, ** reopen from edges to center */ #define K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F 16 /* close from edges to center, reopen from ** center to edges */ #define K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS 17 /* close random checkboard, reopen */ #define K_ANIMATE_SCROLL_LEFT 0x28 #define K_ANIMATE_SCROLL_RIGHT 0x29 #define K_ANIMATE_SCROLL_DOWN 0x2a #define K_ANIMATE_SCROLL_UP 0x2b #define K_ANIMATE_OPEN_SIMPLE 100 /* No animation */ #define GRAPH_BLANK_BOX(s, x, y, xl, yl, color) GFX_ASSERT(gfxop_fill_box(s->gfx_state, \ gfx_rect(x, (((y) < 10)? 10 : (y)), xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), s->ega_colors[color])); #define GRAPH_UPDATE_BOX(s, x, y, xl, yl) GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, \ gfx_rect(x, (((y) < 10)? 10 : (y)) - 10, xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), gfx_point(x, ((y) < 10)? 10 : (y) ))); static void animate_do_animation(state_t *s, int funct_nr, int argc, reg_t *argv) { int i, remaining_checkers; int update_counter; int granularity0 = s->animation_granularity << 1; int granularity1 = s->animation_granularity; int granularity2 = s->animation_granularity >> 2; int granularity3 = s->animation_granularity >> 4; char checkers[32 * 19]; gfx_pixmap_t *newscreen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190)); if (!granularity2) granularity2 = 1; if (!granularity3) granularity3 = 1; gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); if (!newscreen) { SCIkwarn(SCIkERROR, "Failed to allocate 'newscreen'!\n"); return; } GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, 0, 320, 190), gfx_point(0, 10))); gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); /*SCIkdebug(SCIkGRAPHICS, "Animating pic opening type %x\n", s->pic_animate);*/ gfxop_enable_dirty_frames(s->gfx_state); if (s->animation_delay < 1) s->pic_animate = K_ANIMATE_OPEN_SIMPLE; switch(s->pic_animate) { case K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H : for (i = 0; i < 159 + granularity1; i += granularity1) { GRAPH_BLANK_BOX(s, i, 10, granularity1, 190, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, 319-i, 10, granularity1, 190, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_CENTER_OPEN_H : for (i = 159; i >= 1-granularity1; i -= granularity1) { GRAPH_UPDATE_BOX(s, i, 10, granularity1, 190); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, 319-i, 10, granularity1, 190); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V : for (i = 0; i < 94 + granularity2; i += granularity2) { GRAPH_BLANK_BOX(s, 0, i + 10, 320, granularity2, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, 0, 199 - i, 320, granularity2, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 2 * s->animation_delay); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_CENTER_OPEN_V : for (i = 94; i >= 1 - granularity2; i -= granularity2) { GRAPH_UPDATE_BOX(s, 0, i + 10, 320, granularity2); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, 0, 199 - i, 320, granularity2); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 2 * s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN : for(i = 0; i < 319 + granularity0; i += granularity0) { GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay / 2); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_RIGHT_OPEN : for(i = 319; i >= 1 - granularity0; i -= granularity0) { GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay / 2); process_sound_events(s); } break; case K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN : for(i = 319; i >= 1-granularity0; i -= granularity0) { GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay / 2); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_LEFT_OPEN : for(i = 0; i < 319 + granularity0; i+= granularity0) { GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay / 2); process_sound_events(s); } break; case K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN : for (i = 10; i < 199 + granularity1; i += granularity1) { GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_BOTTOM_OPEN : for (i = 199; i >= 11 - granularity1; i-= granularity1) { GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN : for (i = 199; i >= 11 - granularity1; i-= granularity1) { GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); case K_ANIMATE_TOP_OPEN : for (i = 10; i < 199 + granularity1; i+= granularity1) { GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F : for (i = 31; i >= 1-granularity3; i -= granularity3) { int real_i = (i < 0)? 0 : i; int height_l = 3 * (granularity3 - real_i + i); int width_l = 5 * (granularity3 - real_i + i); int height = real_i * 3; int width = real_i * 5; GRAPH_BLANK_BOX(s, width, 10 + height, width_l, 190 - 2*height, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, 320 - width_l - width, 10 + height, width_l, 190 - 2*height, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, width, 10 + height, 320 - 2*width, height_l, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, width, 200 - height_l - height, 320 - 2*width, height_l, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 4 * s->animation_delay); process_sound_events(s); } case K_ANIMATE_BORDER_OPEN_F : for (i = 0; i < 31+granularity3; i += granularity3) { int real_i = (i < 0)? 0 : i; int height_l = 3 * (granularity3 - real_i + i); int width_l = 5 * (granularity3 - real_i + i); int height = real_i * 3; int width = real_i * 5; GRAPH_UPDATE_BOX(s, width, 10 + height, width_l, 190 - 2*height); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, 320 - width_l - width, 10 + height, width_l, 190 - 2*height); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, width, 10 + height, 320 - 2*width, height_l); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, width, 200 - height_l - height, 320 - 2*width, height_l); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 4 * s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F : for (i = 0; i < 31+granularity3; i += granularity3) { int real_i = (i < 0)? 0 : i; int height_l = 3 * (granularity3 - real_i + i); int width_l = 5 * (granularity3 - real_i + i); int height = real_i * 3; int width = real_i * 5; GRAPH_BLANK_BOX(s, width, 10 + height, width_l, 190 - 2*height, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, 320 - width_l - width, 10 + height, width_l, 190 - 2*height, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, width, 10 + height, 320 - 2*width, height_l, 0); gfxop_update(s->gfx_state); GRAPH_BLANK_BOX(s, width, 200 - height_l - height, 320 - 2*width, height_l, 0); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 7 * s->animation_delay); process_sound_events(s); } case K_ANIMATE_CENTER_OPEN_F : for (i = 31; i >= 1-granularity3; i -= granularity3) { int real_i = (i < 0)? 0 : i; int height_l = 3 * (granularity3 - real_i + i); int width_l = 5 * (granularity3 - real_i + i); int height = real_i * 3; int width = real_i * 5; GRAPH_UPDATE_BOX(s, width, 10 + height, width_l, 190 - 2*height); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, 320 - width_l - width, 10 + height, width_l, 190 - 2*height); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, width, 10 + height, 320 - 2 * width, height_l); gfxop_update(s->gfx_state); GRAPH_UPDATE_BOX(s, width, 200 - height_l - height, 320 - 2 * width, height_l); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 7 * s->animation_delay); process_sound_events(s); } break; case K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS : memset(checkers, 0, sizeof(checkers)); remaining_checkers = 19 * 32; update_counter = granularity1; while (remaining_checkers) { int x, y, checker = 1 + (int) (1.0 * remaining_checkers*rand()/(RAND_MAX+1.0)); i = -1; while (checker) if (checkers[++i] == 0) --checker; checkers[i] = 1; /* Mark checker as used */ x = i % 32; y = i / 32; GRAPH_BLANK_BOX(s, x * 10, 10 + y * 10, 10, 10, 0); if (!(update_counter--) || (remaining_checkers == 1)) { gfxop_update(s->gfx_state); update_counter = granularity1; } if (remaining_checkers & 1) { gfxop_usleep(s->gfx_state, s->animation_delay / 4); } --remaining_checkers; process_sound_events(s); } case K_ANIMATE_OPEN_CHECKERS : memset(checkers, 0, sizeof(checkers)); remaining_checkers = 19 * 32; update_counter = granularity1; while (remaining_checkers) { int x, y, checker = 1 + (int) (1.0 * remaining_checkers * rand()/(RAND_MAX+1.0)); i = -1; while (checker) if (checkers[++i] == 0) --checker; checkers[i] = 1; /* Mark checker as used */ x = i % 32; y = i / 32; GRAPH_UPDATE_BOX(s, x * 10, 10 + y * 10, 10, 10); if (!(update_counter--) || (remaining_checkers == 1)) { gfxop_update(s->gfx_state); update_counter = granularity1; } if (remaining_checkers & 1) { gfxop_usleep(s->gfx_state, s->animation_delay / 4); } --remaining_checkers; process_sound_events(s); } break; case K_ANIMATE_SCROLL_LEFT : for (i = 0; i < 319; i += granularity0) { GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, gfx_rect(320 - i, 0, i, 190), gfx_point(0, 10))); GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, 0, 320 - i, 190), gfx_point(i, 10))); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay >> 3); } GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); break; case K_ANIMATE_SCROLL_RIGHT : for (i = 0; i < 319; i += granularity0) { GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, gfx_rect(0, 0, i, 190), gfx_point(319-i, 10))); GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(i, 0, 320 - i, 190), gfx_point(0, 10))); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay >> 3); } GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); break; case K_ANIMATE_SCROLL_UP : for (i = 0; i < 189; i += granularity0) { GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, gfx_rect(0, 190 - i, 320, i), gfx_point(0, 10))); GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, 0, 320, 190 - i), gfx_point(0, 10 + i))); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay >> 3); } GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); break; case K_ANIMATE_SCROLL_DOWN : for (i = 0; i < 189; i += granularity0) { GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, gfx_rect(0, 0, 320, i), gfx_point(0, 200 - i))); GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, i, 320, 190 - i), gfx_point(0, 10))); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, s->animation_delay >> 3); } GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); break; default: if (s->pic_animate != K_ANIMATE_OPEN_SIMPLE) SCIkwarn(SCIkWARNING, "Unknown opening animation 0x%02x\n", s->pic_animate); GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); } GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, s->old_screen)); GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, newscreen)); s->old_screen = NULL; } reg_t kAnimate(state_t *s, int funct_nr, int argc, reg_t *argv) /* Animations are supposed to take a maximum of s->animation_delay milliseconds. */ { reg_t cast_list_ref = KP_ALT(0, NULL_REG); int cycle = (KP_ALT(1, NULL_REG)).offset; list_t *cast_list = NULL; int open_animation = 0; process_sound_events(s); /* Take care of incoming events (kAnimate is called semi-regularly) */ _k_animate_ran = 1; /* Used by some of the invoked functions to check for recursion, which may, ** after all, damage the cast list */ if (cast_list_ref.segment) { cast_list = LOOKUP_LIST(cast_list_ref); if (!cast_list) return s->r_acc; } open_animation = (s->pic_is_new) && (s->pic_not_valid); s->pic_is_new = 0; assert_primary_widget_lists(s); if (!s->dyn_views->contents /* Only reparentize empty dynview list */ && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */ || (s->dyn_views->next))) /* ... or not on top of the view list */ reparentize_primary_widget_lists(s, s->port); if (cast_list) { gfxw_list_t *templist = gfxw_new_list(s->dyn_views->bounds, 0); _k_make_view_list(s, &(templist), cast_list, (cycle? _K_MAKE_VIEW_LIST_CYCLE : 0) | _K_MAKE_VIEW_LIST_CALC_PRIORITY, funct_nr, argc, argv); /* Make sure that none of the doits() did something evil */ assert_primary_widget_lists(s); if (!s->dyn_views->contents /* Only reparentize empty dynview list */ && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */ || (s->dyn_views->next))) /* ... or not on top of the view list */ reparentize_primary_widget_lists(s, s->port); /* End of doit() recovery code */ if (s->pic_is_new) { /* Happens if DrawPic() is executed by a dynview (yes, that happens) */ kAnimate(s, funct_nr, argc, argv); /* Tail-recurse */ return s->r_acc; } SCIkdebug(SCIkGRAPHICS, "Handling Dynviews (..step 9 inclusive):\n"); _k_prepare_view_list(s, templist, _K_MAKE_VIEW_LIST_CALC_PRIORITY); if (s->pic_not_valid) { SCIkdebug(SCIkGRAPHICS, "PicNotValid=%d -> Subalgorithm:\n"); _k_redraw_view_list(s, templist); } _k_update_signals_in_view_list(s->dyn_views, templist); s->dyn_views->tag(GFXW(s->dyn_views)); _k_raise_topmost_in_view_list(s, s->dyn_views, (gfxw_dyn_view_t *) templist->contents); templist->widfree(GFXW(templist)); s->dyn_views->free_tagged(GFXWC(s->dyn_views)); /* Free obsolete dynviews */ } /* if (cast_list) */ if (open_animation) { gfxop_clear_box(s->gfx_state, gfx_rect(0, 10, 320, 190)); /* Propagate pic */ s->visual->add_dirty_abs(GFXWC(s->visual), gfx_rect_fullscreen, 0); /* Mark screen as dirty so picviews will be drawn correctly */ FULL_REDRAW(); animate_do_animation(s, funct_nr, argc, argv); } /* if (open_animation) */ if (cast_list) { int retval; int reparentize = 0; s->pic_not_valid = 0; _k_view_list_do_postdraw(s, s->dyn_views); /* _k_view_list_dispose_loop() returns -1 if it requested a re-start, so we do just that. */ while ((retval = _k_view_list_dispose_loop(s, cast_list, (gfxw_dyn_view_t *) s->dyn_views->contents, funct_nr, argc, argv) < 0)) reparentize = 1; if (s->drop_views->contents) { s->drop_views = gfxw_new_list(s->dyn_views->bounds, GFXW_LIST_SORTED); s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); } else { assert(s->drop_views); gfxw_remove_widget_from_container(s->drop_views->parent, GFXW(s->drop_views)); ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); } if ((reparentize | retval) && (GFXWC(s->port) == GFXWC(s->dyn_views->parent)) /* If dynviews are on the same port... */ && (s->dyn_views->next)) /* ... and not on top of the view list... */ reparentize_primary_widget_lists(s, s->port); /* ...then reparentize. */ _k_view_list_kryptonize(s->dyn_views->contents); } FULL_REDRAW(); return s->r_acc; } #define SHAKE_DOWN 1 #define SHAKE_RIGHT 2 reg_t kShakeScreen(state_t *s, int funct_nr, int argc, reg_t *argv) { int shakes = SKPV_OR_ALT(0, 1); int directions = SKPV_OR_ALT(1, 1); gfx_pixmap_t *screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 0, 320, 200)); int i; if (directions & ~3) SCIkdebug(SCIkGRAPHICS, "ShakeScreen(): Direction bits are %x (unknown)\n", directions); gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); for (i = 0; i < shakes; i++) { int shake_down = (directions & SHAKE_DOWN)? 10 : 0; int shake_right = (directions & SHAKE_RIGHT)? 10 : 0; if (directions & SHAKE_DOWN) gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 320, 10), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT); if (directions & SHAKE_RIGHT) gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 10, 200), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT); gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320 - shake_right, 200 - shake_down), gfx_point(shake_right, shake_down)); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 50000); gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320, 200), gfx_point(0, 0)); gfxop_update(s->gfx_state); gfxop_usleep(s->gfx_state, 50000); } gfxop_free_pixmap(s->gfx_state, screen); gfxop_update(s->gfx_state); return s->r_acc; } #define K_DISPLAY_SET_COORDS 100 #define K_DISPLAY_SET_ALIGNMENT 101 #define K_DISPLAY_SET_COLOR 102 #define K_DISPLAY_SET_BGCOLOR 103 #define K_DISPLAY_SET_GRAYTEXT 104 #define K_DISPLAY_SET_FONT 105 #define K_DISPLAY_WIDTH 106 #define K_DISPLAY_SAVE_UNDER 107 #define K_DISPLAY_RESTORE_UNDER 108 #define K_DONT_UPDATE_IMMEDIATELY 121 reg_t kDisplay(state_t *s, int funct_nr, int argc, reg_t *argv) { int argpt; reg_t textp = argv[0]; int index = UKPV_OR_ALT(1, 0); int temp; int save_under = 0; gfx_color_t transparent; char *text; gfxw_port_t *port = (s->port)? s->port : s->picture_port; int update_immediately = 1; gfx_color_t color0, *color1, bg_color; gfx_alignment_t halign = ALIGN_LEFT; rect_t area = gfx_rect(port->draw_pos.x, port->draw_pos.y, 320 - port->draw_pos.x, 200 - port->draw_pos.y); int gray = port->gray_text; int font_nr = port->font_nr; gfxw_text_t *text_handle; transparent.mask = 0; color0 = port->color; bg_color = port->bgcolor; if (textp.segment) { argpt = 1; text = (char *) kernel_dereference_bulk_pointer(s, textp, 0); } else { argpt = 2; text = kernel_lookup_text(s, textp, index); } if (!text) { SCIkdebug(SCIkERROR, "Display with invalid reference "PREG"!\n", PRINT_REG(textp)); return NULL_REG; } while (argpt < argc) { switch(UKPV(argpt++)) { case K_DISPLAY_SET_COORDS: area.x = UKPV(argpt++); area.y = UKPV(argpt++); SCIkdebug(SCIkGRAPHICS, "Display: set_coords(%d, %d)\n", area.x, area.y); break; case K_DISPLAY_SET_ALIGNMENT: halign = (gfx_alignment_t)KP_SINT(argv[argpt++]); SCIkdebug(SCIkGRAPHICS, "Display: set_align(%d)\n", halign); break; case K_DISPLAY_SET_COLOR: temp = KP_SINT(argv[argpt++]); SCIkdebug(SCIkGRAPHICS, "Display: set_color(%d)\n", temp); if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15) color0 = (s->ega_colors[temp]); else if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp < 256) { color0.visual = *(get_pic_color(s, temp)); color0.mask = GFX_MASK_VISUAL; } else if (temp == -1) color0 = transparent; else SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp); break; case K_DISPLAY_SET_BGCOLOR: temp = KP_SINT(argv[argpt++]); SCIkdebug(SCIkGRAPHICS, "Display: set_bg_color(%d)\n", temp); if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15) bg_color = s->ega_colors[temp]; else if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp <= 256) { bg_color.visual = *get_pic_color(s, temp); bg_color.mask = GFX_MASK_VISUAL; } else if (temp == -1) bg_color = transparent; else SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp); break; case K_DISPLAY_SET_GRAYTEXT: gray = KP_SINT(argv[argpt++]); SCIkdebug(SCIkGRAPHICS, "Display: set_graytext(%d)\n", gray); break; case K_DISPLAY_SET_FONT: font_nr = KP_UINT(argv[argpt++]); SCIkdebug(SCIkGRAPHICS, "Display: set_font(\"font.%03d\")\n", font_nr); break; case K_DISPLAY_WIDTH: area.xl = UKPV(argpt++); if (area.xl == 0) area.xl = MAX_TEXT_WIDTH_MAGIC_VALUE; SCIkdebug(SCIkGRAPHICS, "Display: set_width(%d)\n", area.xl); break; case K_DISPLAY_SAVE_UNDER: save_under = 1; SCIkdebug(SCIkGRAPHICS, "Display: set_save_under()\n"); break; case K_DISPLAY_RESTORE_UNDER: SCIkdebug(SCIkGRAPHICS, "Display: restore_under(%04x)\n", UKPV(argpt)); graph_restore_box(s, argv[argpt++]); update_immediately = 1; argpt++; return s->r_acc; case K_DONT_UPDATE_IMMEDIATELY: update_immediately=0; SCIkdebug(SCIkGRAPHICS, "Display: set_dont_update()\n"); argpt++; break; default: SCIkdebug(SCIkGRAPHICS, "Unknown Display() command %x\n", UKPV(argpt-1)); return NULL_REG; } } if (s->version >= SCI_VERSION_FTU_DISPLAY_COORDS_FUZZY) { if (halign == ALIGN_LEFT) GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text, area.xl, &area.xl, &area.yl, 0, NULL, NULL, NULL)); /* Make the text fit on the screen */ if (area.x + area.xl > 320) area.x += 320 - area.x - area.xl; /* Plus negative number = subtraction */ if (area.y + area.yl > 200) { area.y += 200 - area.y - area.yl; /* Plus negative number = subtraction */ } } if (gray) color1 = &bg_color; else color1 = &color0; assert_primary_widget_lists(s); text_handle = gfxw_new_text(s->gfx_state, area, font_nr, text, halign, ALIGN_TOP, color0, *color1, bg_color, 0); if (!text_handle) { SCIkwarn(SCIkERROR, "Display: Failed to create text widget!\n"); return NULL_REG; } if (save_under) { /* Backup */ rect_t save_area = text_handle->bounds; save_area.x += port->bounds.x; save_area.y += port->bounds.y; s->r_acc = graph_save_box(s, save_area); text_handle->serial++; /* This is evil! */ SCIkdebug(SCIkGRAPHICS, "Saving (%d, %d) size (%d, %d) as "PREG"\n", save_area.x, save_area.y, save_area.xl, save_area.yl, s->r_acc); } SCIkdebug(SCIkGRAPHICS, "Display: Commiting text '%s'\n", text); /* ADD_TO_CURRENT_FG_WIDGETS(text_handle); */ ADD_TO_CURRENT_FG_WIDGETS(GFXW(text_handle)); if ((!s->pic_not_valid)&&update_immediately) { /* Refresh if drawn to valid picture */ FULL_REDRAW(); SCIkdebug(SCIkGRAPHICS, "Refreshing display...\n"); } return s->r_acc; }