diff --git a/libmui/Makefile b/libmui/Makefile index cf0dea2..3dac44f 100644 --- a/libmui/Makefile +++ b/libmui/Makefile @@ -61,6 +61,8 @@ $(LIB)/ui_tests.so : $(OBJ)/mii_mui_1mb.o $(LIB)/ui_tests.so : $(OBJ)/mii_mui_2dsk.o $(LIB)/ui_tests.so : $(OBJ)/mii_mui_about.o +$(OBJ)/mii_mui_about.o : CPPFLAGS+=-DMII_ICON64_DEFINE + # use a .temp file, otherwise the playground tries to reload before the file # is fully written, and it fails. # the ${filter} are there to make the sure object files are linked before the .a diff --git a/libmui/fonts/Geneva.ttf b/libmui/fonts/Geneva.ttf new file mode 100644 index 0000000..f0b48cb Binary files /dev/null and b/libmui/fonts/Geneva.ttf differ diff --git a/libmui/mui/mui.c b/libmui/mui/mui.c index e19a205..136aa33 100644 --- a/libmui/mui/mui.c +++ b/libmui/mui/mui.c @@ -242,15 +242,20 @@ mui_timer_reset( if (id >= MUI_TIMER_COUNT) return 0; if (!(ui->timer.map & (1 << id)) || - ui->timer.timers[id].cb != cb) + ui->timer.timers[id].cb != cb) { + printf("%s: timer %d not active\n", __func__, id); return 0; + } mui_time_t res = 0; uint64_t now = mui_get_time(); if (ui->timer.timers[id].when > now) res = ui->timer.timers[id].when - now; ui->timer.timers[id].when = now + delay; - if (delay == 0) + if (delay == 0) { ui->timer.map &= ~(1 << id); + printf("%s: timer %d removed\n", __func__, id); + } + return res; } diff --git a/libmui/mui/mui.h b/libmui/mui/mui.h index 5319b3d..56b6047 100644 --- a/libmui/mui/mui.h +++ b/libmui/mui/mui.h @@ -326,10 +326,16 @@ typedef struct mui_drawable_t { struct cg_ctx_t * cg; union pixman_image * pixman; // (try) not to use these directly unsigned int pixman_clip_dirty: 1, - cg_clip_dirty : 1; + cg_clip_dirty : 1, + dispose_pixels : 1; + // (default) position in destination when drawing + c2_pt_t origin; mui_clip_stack_t clip; } mui_drawable_t; +// Use IMPLEMENT_C_ARRAY(mui_drawable_array); if you need this +DECLARE_C_ARRAY(mui_drawable_t *, mui_drawable_array, 4); + /* * Drawable related */ @@ -425,6 +431,13 @@ mui_font_t * mui_font_find( struct mui_t * ui, const char * name); +mui_font_t * +mui_font_from_mem( + struct mui_t * ui, + const char *name, + unsigned int size, + const void *font_data, + unsigned int font_size ); void mui_font_text_draw( mui_font_t * font, @@ -473,6 +486,15 @@ mui_font_measure( unsigned int text_len, mui_glyph_line_array_t *lines, uint16_t flags); +// to be used exclusively with mui_font_measure +void +mui_font_measure_draw( + mui_font_t *font, + mui_drawable_t *dr, + c2_rect_t bbox, + mui_glyph_line_array_t *lines, + mui_color_t color, + uint16_t flags); // clear all the lines, and glyph lists. Use it after mui_font_measure void mui_font_measure_clear( diff --git a/libmui/mui/mui_drawable.c b/libmui/mui/mui_drawable.c index c1bbe47..0ce4e8e 100644 --- a/libmui/mui/mui_drawable.c +++ b/libmui/mui/mui_drawable.c @@ -36,6 +36,8 @@ mui_drawable_clear( for (int i = 0; i < (int)dr->clip.count; i++) pixman_region32_fini(&dr->clip.e[i]); mui_clip_stack_clear(&dr->clip); + if (dr->pix.pixels && dr->dispose_pixels) + free(dr->pix.pixels); dr->_pix_hash = NULL; } diff --git a/libmui/mui/mui_font.c b/libmui/mui/mui_font.c index 58f6107..383c144 100644 --- a/libmui/mui/mui_font.c +++ b/libmui/mui/mui_font.c @@ -24,6 +24,7 @@ INCBIN(main_font, "fonts/Charcoal_mui.ttf"); INCBIN(icon_font, "fonts/typicon.ttf"); INCBIN(dingbat_font, "fonts/Dingbat.ttf"); +INCBIN(geneva_font, "fonts/Geneva.ttf"); #include "mui.h" diff --git a/libmui/tests/ui_tests.c b/libmui/tests/ui_tests.c index 0bc5db9..b621a93 100644 --- a/libmui/tests/ui_tests.c +++ b/libmui/tests/ui_tests.c @@ -172,11 +172,10 @@ _init( m_cpu_menu); // mii_mui_configure_slots(g->ui, &g_machine_conf); -// mii_mui_load_binary(g->ui, &g_loadbin_conf); + mii_mui_load_binary(g->ui, &g_loadbin_conf); // mii_mui_load_1mbrom(g->ui, &g_machine_conf.slot[0].conf.rom1mb); -// mii_mui_load_2dsk(g->ui, -// &g_machine_conf.slot[0].conf.disk2, MII_2DSK_DISKII); - mii_mui_about(g->ui); +// mii_mui_load_2dsk(g->ui, &g_machine_conf.slot[0].conf.disk2, MII_2DSK_DISKII); +// mii_mui_about(g->ui); #if 0 mui_alert(ui, C2_PT(0,0), "Testing one Two", @@ -234,4 +233,4 @@ mui_plug_t mui_plug = { .dispose = _dispose, .draw = _draw, .event = _event, -}; \ No newline at end of file +}; diff --git a/src/format/mii_floppy.c b/src/format/mii_floppy.c index d48c1b6..6fdae72 100644 --- a/src/format/mii_floppy.c +++ b/src/format/mii_floppy.c @@ -205,6 +205,24 @@ mii_floppy_write_track_woz( return 0; } +static uint64_t +mii_floppy_woz_load_tmap( + mii_floppy_t *f, + mii_woz_tmap_t *tmap ) +{ + uint64_t used_tracks = 0; + int tmap_size = le32toh(tmap->chunk.size_le); + for (int ti = 0; ti < (int)sizeof(f->track_id) && ti < tmap_size; ti++) { + if (tmap->track_id[ti] == 0xff) { + f->track_id[ti] = MII_FLOPPY_RANDOM_TRACK_ID; + continue; + } + f->track_id[ti] = tmap->track_id[ti]; + used_tracks |= 1L << f->track_id[ti]; + } + return used_tracks; +} + static int mii_floppy_load_woz( mii_floppy_t *f, @@ -221,12 +239,15 @@ mii_floppy_load_woz( } version += !strncmp((char*)header, "WOZ2", 4); mii_woz_tmap_t *tmap = NULL; + uint64_t used_tracks = 0; + if (version == 1) { mii_woz1_info_t *info = (mii_woz1_info_t *)(header + 1); tmap = (mii_woz_tmap_t *)((uint8_t *)info + le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t)); mii_woz1_trks_t *trks = (mii_woz1_trks_t *)((uint8_t *)tmap + le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t)); + used_tracks = mii_floppy_woz_load_tmap(f, tmap); #if 1 printf("WOZ: version %d, type %d\n", info->version, info->disk_type ); @@ -237,8 +258,13 @@ mii_floppy_load_woz( printf("WOZ: Track chunk %4.4s size %d\n", (char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le)); #endif - for (int i = 0; i < 35; i++) { + int max_track = le32toh(trks->chunk.size_le) / sizeof(trks->track[0]); + for (int i = 0; i < 35 && i < max_track; i++) { uint8_t *track = trks->track[i].bits; + if (!(used_tracks & (1L << i))) { + // printf("WOZ: Track %d not used\n", i); + continue; + } memcpy(f->tracks[i].data, track, le16toh(trks->track[i].byte_count_le)); f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le); } @@ -248,6 +274,7 @@ mii_floppy_load_woz( le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t)); mii_woz2_trks_t *trks = (mii_woz2_trks_t *)((uint8_t *)tmap + le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t)); + used_tracks = mii_floppy_woz_load_tmap(f, tmap); #if 1 printf("WOZ: version %d, type %d, sides %d, largest track %d, optimal bit timing: %d\n", info->version, info->disk_type, info->sides, @@ -263,6 +290,10 @@ mii_floppy_load_woz( /* TODO: this doesn't work yet... */ // f->bit_timing = info->optimal_bit_timing; for (int i = 0; i < 35; i++) { + if (!(used_tracks & (1L << i))) { + // printf("WOZ: Track %d not used\n", i); + continue; + } uint8_t *track = file->map + (le16toh(trks->track[i].start_block_le) << 9); uint32_t byte_count = (le32toh(trks->track[i].bit_count_le) + 7) >> 3; @@ -270,15 +301,6 @@ mii_floppy_load_woz( f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le); } } - // copy the track map from the file to the floppy - for (int ti = 0; ti < (int)sizeof(f->track_id); ti++) { - f->track_id[ti] = tmap->track_id[ti] == 0xff ? - MII_FLOPPY_RANDOM_TRACK_ID : tmap->track_id[ti]; - if (f->tracks[f->track_id[ti]].bit_count == 0) { - printf("%s Invalid qtrack %d (points to track %d) has zero bits!\n", - __func__, ti, f->track_id[ti]); - } - } return version; } diff --git a/ui_gl/mii_mui_about.c b/ui_gl/mii_mui_about.c index ba047c4..06d8cce 100644 --- a/ui_gl/mii_mui_about.c +++ b/ui_gl/mii_mui_about.c @@ -14,20 +14,129 @@ #include "mui.h" #include "mii-icon-64.h" - +extern const unsigned char mui_geneva_font_data[]; +extern const unsigned int mui_geneva_font_size; enum { MII_ABOUT_WINDOW_ID = FCC('a','b','o','t'), MII_ABOUT_OK = FCC('O','K','!',' '), }; +struct mui_drawable_control_t; typedef struct mii_mui_about_t { mui_window_t win; + struct mui_drawable_control_t * text; + uint8_t timer_id; + bool terminate; } mii_mui_about_t; +//DECLARE_C_ARRAY(mui_drawable_t *, mui_drawable_array, 4); + +typedef struct mui_drawable_control_t { + mui_control_t control; + uint16_t flags; + mui_drawable_t * mask; + mui_drawable_array_t drawables; +} mui_drawable_control_t; + +IMPLEMENT_C_ARRAY(mui_drawable_array); + +static void +mui_drawable_draw( + mui_window_t * win, + mui_control_t * c, + mui_drawable_t *dr ) +{ + c2_rect_t f = c->frame; + c2_rect_offset(&f, win->content.l, win->content.t); + + mui_drawable_clip_push(dr, &f); + mui_drawable_control_t *dc = (mui_drawable_control_t *)c; + + for (int i = 0; i < (int)dc->drawables.count; i++) { + mui_drawable_t *d = dc->drawables.e[i]; + if (!d->pix.pixels) + continue; + c2_rect_t src = C2_RECT_WH(0, 0, + d->pix.size.x, + d->pix.size.y); + c2_rect_offset(&src, d->origin.x, d->origin.y); + + pixman_image_composite32(PIXMAN_OP_OVER, + mui_drawable_get_pixman(d), + mui_drawable_get_pixman(dc->mask), + mui_drawable_get_pixman(dr), + src.l, src.t, 0, 0, + f.l, f.t, + c2_rect_width(&src), c2_rect_height(&src)); + } + mui_drawable_clip_pop(dr); +} + +static bool +mui_cdef_drawable( + struct mui_control_t * c, + uint8_t what, + void * param) +{ +// mui_textbox_control_t *tb = (mui_textbox_control_t *)c; + switch (what) { + case MUI_CDEF_DRAW: { + mui_drawable_t * dr = param; + switch (c->type) { + case 0: + mui_drawable_draw(c->win, c, dr); + break; + } + } break; + case MUI_CDEF_DISPOSE: { + switch (c->type) { + case 0: { + mui_drawable_control_t *dc = (mui_drawable_control_t *)c; + for (int i = 0; i < (int)dc->drawables.count; i++) { + mui_drawable_t *d = dc->drawables.e[i]; + mui_drawable_dispose(d); + free(d); + } + mui_drawable_dispose(dc->mask); + if (dc->mask) + free(dc->mask); + mui_drawable_array_free(&dc->drawables); + } break; + } + } break; + } + return false; +} + + +static mui_time_t +mui_about_timer_cb( + struct mui_t * mui, + mui_time_t now, + void * param) +{ + mii_mui_about_t * m = (mii_mui_about_t*)param; + + if (m->terminate) { + mui_window_dispose(&m->win); + return 0; + } + mui_drawable_control_t *dc = m->text; + mui_drawable_t * dr = dc->drawables.e[0]; + + dr->origin.y++; + int height = dr->pix.size.y + c2_rect_height(&dc->control.frame); + if (dr->origin.y > dr->pix.size.y) + dr->origin.y -= height; +// printf("Y: %d/%d\n", dr->origin.y, height); + mui_control_inval(&dc->control); + return MUI_TIME_SECOND / 30; +} + static int -_mii_about_action_cb( +_mii_about_button_cb( mui_control_t * c, void * cb_param, // mii_mui_about_t uint32_t what, @@ -42,8 +151,7 @@ _mii_about_action_cb( printf("%s control %4.4s\n", __func__, (char*)&uid); switch (uid) { case MII_ABOUT_OK: { - // save the config - mui_window_dispose(&m->win); + m->terminate = true; } break; } break; @@ -51,6 +159,32 @@ _mii_about_action_cb( return 0; } + +#ifndef MII_VERSION +#define MII_VERSION "0.0.0" +#endif +static const char * about = + "\n" + "The MII " MUI_GLYPH_IIE " Emulator\n" + "Version " MII_VERSION "\n" + "Built " __DATE__ " " __TIME__ "\n" + "© Michel Pollet 2023-2024\n\n" + "Thanks to:\n" + ; +static const char * thanksto = + "Steve Wozniak\n" + "Bill Atkinson\n" + "Andy Hertzfeld\n" + "Randy Wigginton\n" + "Jef Raskin\n" + "Susan Kare\n" + "Thierry Magniez\n" + "Charles \"regnips\" Springer\n" + "Jeroen \"Sprite_tm\" Domburg\n" + "Claude \"Claude\" Schwarz\n" + "... and many others" + ; + struct mui_window_t * mii_mui_about( struct mui_t *mui ) @@ -64,7 +198,7 @@ mii_mui_about( return w; } c2_pt_t where = {}; - c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 560, 240); + c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 500, 240); if (where.x == 0 && where.y == 0) c2_rect_offset(&wpos, (ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2), @@ -73,18 +207,119 @@ mii_mui_about( "About the MII " MUI_GLYPH_IIE " Emulator", sizeof(mii_mui_about_t)); mui_window_set_id(w, MII_ABOUT_WINDOW_ID); + mii_mui_about_t * m = (mii_mui_about_t*)w; - mui_control_t * c = NULL; + mui_control_t * c = NULL, *ok = NULL; c2_rect_t cf; cf = C2_RECT_WH(0, 0, base_size * 4, base_size * 1.4); c2_rect_left_of(&cf, c2_rect_width(&w->content), margin); c2_rect_top_of(&cf, c2_rect_height(&w->content), margin); - c = mui_button_new(w, + ok = c = mui_button_new(w, cf, MUI_BUTTON_STYLE_DEFAULT, "OK", MII_ABOUT_OK); c->key_equ = MUI_KEY_EQU(0, 13); + mui_control_set_action(c, _mii_about_button_cb, w); + cf = C2_RECT_WH(margin, margin, mii_icon64[0], mii_icon64[1]); + /* + Icon + */ + c = mui_control_new( + w, 0, mui_cdef_drawable, + cf, NULL, 0, sizeof(mui_drawable_control_t)); + mui_drawable_control_t *dc = (mui_drawable_control_t*)c; + mui_drawable_t *dr = calloc(1, sizeof(mui_drawable_t)); + /* the Xorg icon is in fact using unsigned int (64 bits!) per pixels + * for some bizare reason. Here we convert it back to 32bpp */ + dr->pix.bpp = 32; + dr->pix.size.x = mii_icon64[0]; + dr->pix.size.y = mii_icon64[1]; + dr->pix.row_bytes = dr->pix.size.x * 4; + dr->pix.pixels = calloc(1, dr->pix.row_bytes * dr->pix.size.y); + dr->dispose_pixels = true; + + /* + * if the clear color is 0, it means we are using OpenGL as a renderer + * this is not ideal as it means we need to reverse our internal format + * (ARGB) to the one used by OpenGL (ABGR) + */ + for (int y = 0; y < dr->pix.size.y; y++) { + for (int x = 0; x < dr->pix.size.x; x++) { + uint32_t *p = (uint32_t*)dr->pix.pixels; + p += y * dr->pix.size.x + x; + uint32_t pix = mii_icon64[2 + y * dr->pix.size.x + x]; + if (ui->color.clear.a == 0) { + // ARGB -> ABGR + *p = ((pix & 0xff00ff00) | + ((pix & 0x00ff0000) >> 16) | + ((pix & 0x000000ff) << 16)); + } else + *p = pix; + } + } + mui_drawable_array_add(&dc->drawables, dr); + + /* + * Text in two parts + */ + cf = C2_RECT_WH(cf.r + margin, 10, + c2_rect_width(&w->frame) - cf.r - margin*2, + ok->frame.t - margin); + + c2_rect_t tbox = cf; + c2_rect_offset(&tbox, -tbox.l, -tbox.t); + tbox.b = 1000; + mui_font_t *font = mui_font_find(ui, "main"); + mui_font_t *geneva = mui_font_find(ui, "geneva"); + if (!geneva) { + geneva = mui_font_from_mem(ui, "geneva", 24, + mui_geneva_font_data, mui_geneva_font_size); + } + mui_glyph_line_array_t lines_about = {}; + mui_font_measure(font, tbox, about, 0, &lines_about, MUI_TEXT_ALIGN_CENTER); + + c2_rect_t about_frame = tbox; + about_frame.b = 0; + for (int li = 0; li < (int)lines_about.count; li++) { + mui_glyph_array_t * line = &lines_about.e[li]; + // printf("line %d x %d y %d w %d\n", li, line->x, line->y, line->w); + about_frame.b = line->y; + } + mui_glyph_line_array_t lines_thanks = {}; + mui_font_measure(geneva, tbox, thanksto, 0, &lines_thanks, MUI_TEXT_ALIGN_CENTER); + c2_rect_t frame_thanks = tbox; + frame_thanks.b = 0; + for (int li = 0; li < (int)lines_thanks.count; li++) { + mui_glyph_array_t * line = &lines_thanks.e[li]; + // printf("line %d x %d y %d w %d\n", li, line->x, line->y, line->w); + frame_thanks.b = line->y; + } + c2_rect_offset(&frame_thanks, 0, about_frame.b + 0); + + c = mui_control_new( + w, 0, mui_cdef_drawable, + cf, NULL, 0, sizeof(mui_drawable_control_t)); + tbox.b = frame_thanks.b + 2; + + m->text = dc = (mui_drawable_control_t*)c; + + dr = calloc(1, sizeof(mui_drawable_t)); + dr->pix.bpp = 32; + dr->pix.size.x = c2_rect_width(&tbox); + dr->pix.size.y = c2_rect_height(&tbox); + dr->pix.row_bytes = dr->pix.size.x * 4; + dr->pix.pixels = calloc(1, dr->pix.row_bytes * dr->pix.size.y); + dr->dispose_pixels = true; + mui_color_t text_color = MUI_COLOR(0x000000ff); + mui_font_measure_draw(font, dr, tbox, &lines_about, + text_color, MUI_TEXT_ALIGN_CENTER); + mui_font_measure_draw(geneva, dr, frame_thanks, &lines_thanks, + text_color, MUI_TEXT_ALIGN_CENTER); + mui_drawable_array_add(&dc->drawables, dr); + + m->timer_id = mui_timer_register(ui, + mui_about_timer_cb, m, MUI_TIME_SECOND); return w; } diff --git a/ui_gl/mii_mui_menus.c b/ui_gl/mii_mui_menus.c index b4b248f..c9075da 100644 --- a/ui_gl/mii_mui_menus.c +++ b/ui_gl/mii_mui_menus.c @@ -209,7 +209,8 @@ mii_menubar_action( // (char*)&item->uid, item->title); switch (item->uid) { case FCC('a','b','o','t'): { - _mii_show_about(ui); +// _mii_show_about(ui); + mii_mui_about(&ui->mui); } break; case FCC('q','u','i','t'): { // printf("%s Quit?\n", __func__); @@ -395,4 +396,4 @@ mii_mui_menus_init( mui_menubar_add_simple(mbar, "CPU", FCC('c','p','u','m'), m_cpu_menu); -} +} \ No newline at end of file