diff --git a/Makefile b/Makefile
index 1d21c6f..52c7148 100644
--- a/Makefile
+++ b/Makefile
@@ -129,8 +129,13 @@ lsp:
-include $(O)/*.d
-include $(O)/obj/*.d
+DESTDIR := /usr/local
install:
mkdir -p $(DESTDIR)/bin
- mkdir -p $(DESTDIR)/share/games/mii/
cp $(BIN)/mii_emu_gl $(DESTDIR)/bin/
+
+avail:
+ mkdir -p $(DESTDIR)/bin
+ rm -f $(DESTDIR)/bin/mii_emu_gl && \
+ ln -sf $(realpath $(BIN)/mii_emu_gl) $(DESTDIR)/bin/mii_emu_gl
diff --git a/README.md b/README.md
index 724369c..1ae5be1 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,16 @@
# MII Version Changelog
+## 1.7
+ * New animated about box, because, everyone loves a good about box.
+ * Added support for Write Protect of floppy disks; disk can be write protected manually, or if the file lacks write permissions, OR if the file format (NIB, DSK) doesn't support writes.
+ * New fancypants 'bit view' of the floppy are they are read/written, with a
+ heat map to show where the head was last. Drive 1 appears left of the screen,
+ drive 2 on the right. It fades out after the drive motor stops.
+
+![Heat map disk viewq](docs/heat_map.png)
+*DOS 3.3 Disk 'bitstream view' on the left, the green trace shows what's just be read.*
+
## 1.6
* Another big update, yanked the old DiskII driver, and replaced it with a
homebrew one, low level, with native support for WOZ (1 & 2) files (*read AND write!*) as well as NIB and DSK (read only).
diff --git a/docs/heat_map.png b/docs/heat_map.png
new file mode 100644
index 0000000..b0e8c57
Binary files /dev/null and b/docs/heat_map.png differ
diff --git a/libmui/mui/mui.h b/libmui/mui/mui.h
index cfbb27b..d452c27 100644
--- a/libmui/mui/mui.h
+++ b/libmui/mui/mui.h
@@ -64,6 +64,7 @@ enum mui_key_e {
MUI_KEY_RALT,
MUI_KEY_RSUPER,
MUI_KEY_LSUPER,
+ MUI_KEY_CAPSLOCK,
MUI_KEY_MODIFIERS_LAST,
MUI_KEY_F1 = 0x100,
MUI_KEY_F2,
@@ -330,7 +331,11 @@ typedef struct mui_drawable_t {
dispose_pixels : 1,
dispose_drawable : 1;
// not used internally, but useful for the application
- unsigned int texture_id;
+ struct {
+ float opacity;
+ c2_pt_t size;
+ unsigned int id;
+ } texture;
// (default) position in destination when drawing
c2_pt_t origin;
mui_clip_stack_t clip;
@@ -364,6 +369,11 @@ mui_drawable_init(
void
mui_drawable_dispose(
mui_drawable_t * dr);
+// Clear, but do not dispose of the drawable
+void
+mui_drawable_clear(
+ mui_drawable_t * dr);
+
// get/allocate a pixman structure for this drawable
union pixman_image *
mui_drawable_get_pixman(
diff --git a/libmui/mui/mui_controls.c b/libmui/mui/mui_controls.c
index bd57f2f..cd5e334 100644
--- a/libmui/mui/mui_controls.c
+++ b/libmui/mui/mui_controls.c
@@ -148,7 +148,7 @@ _mui_control_highlight_timer_cb(
{
mui_control_t * c = param;
- printf("%s: %s\n", __func__, c->title);
+// printf("%s: %s\n", __func__, c->title);
mui_control_set_state(c, MUI_CONTROL_STATE_NORMAL);
if (c->cdef)
c->cdef(c, MUI_CDEF_SELECT, NULL);
diff --git a/libmui/mui/mui_drawable.c b/libmui/mui/mui_drawable.c
index 17d3689..045457b 100644
--- a/libmui/mui/mui_drawable.c
+++ b/libmui/mui/mui_drawable.c
@@ -73,6 +73,9 @@ mui_drawable_clear(
mui_clip_stack_clear(&dr->clip);
if (dr->pix.pixels && dr->dispose_pixels)
free(dr->pix.pixels);
+ static const mui_pixmap_t zero = {};
+ dr->pix = zero;
+ dr->dispose_pixels = 0;
dr->_pix_hash = NULL;
}
diff --git a/src/drivers/mii_disk2.c b/src/drivers/mii_disk2.c
index ffc9940..32b241f 100644
--- a/src/drivers/mii_disk2.c
+++ b/src/drivers/mii_disk2.c
@@ -13,7 +13,7 @@
#include
#include "mii.h"
#include "mii_bank.h"
-#include "mii_disk2.h"
+
#include "mii_rom_disk2.h"
#include "mii_woz.h"
#include "mii_floppy.h"
@@ -62,8 +62,8 @@ _mii_floppy_motor_off_cb(
{
mii_card_disk2_t *c = param;
mii_floppy_t *f = &c->floppy[c->selected];
- printf("%s drive %d off\n", __func__, c->selected);
- if (c->drive[c->selected].file && f->tracks_dirty)
+// printf("%s drive %d off\n", __func__, c->selected);
+ if (c->drive[c->selected].file && f->seed_dirty != f->seed_saved)
mii_floppy_update_tracks(f, c->drive[c->selected].file);
f->motor = 0;
return 0;
@@ -103,21 +103,25 @@ _mii_disk2_switch_track(
mii_floppy_t *f = &c->floppy[c->selected];
int qtrack = f->qtrack + delta;
if (qtrack < 0) qtrack = 0;
- if (qtrack >= 35 * 4) qtrack = (35 * 4) -1;
+ if (qtrack >= MII_FLOPPY_TRACK_COUNT * 4)
+ qtrack = (MII_FLOPPY_TRACK_COUNT * 4) -1;
if (qtrack == f->qtrack)
return f->qtrack;
uint8_t track_id = f->track_id[f->qtrack];
- if (track_id != MII_FLOPPY_RANDOM_TRACK_ID)
- printf("NEW TRACK D%d: %d\n", c->selected, track_id);
+// if (track_id != 0xff)
+// printf("NEW TRACK D%d: %d\n", c->selected, track_id);
uint8_t track_id_new = f->track_id[qtrack];
-
+ if (track_id_new >= MII_FLOPPY_TRACK_COUNT)
+ track_id_new = MII_FLOPPY_NOISE_TRACK;
/* adapt the bit position from one track to the others, from WOZ specs */
- uint32_t track_size = f->tracks[track_id].bit_count;
- uint32_t new_size = f->tracks[track_id_new].bit_count;
- uint32_t new_pos = f->bit_position * new_size / track_size;
- f->bit_position = new_pos;
+ if (track_id_new != MII_FLOPPY_NOISE_TRACK) {
+ uint32_t track_size = f->tracks[track_id].bit_count;
+ uint32_t new_size = f->tracks[track_id_new].bit_count;
+ uint32_t new_pos = f->bit_position * new_size / track_size;
+ f->bit_position = new_pos;
+ }
f->qtrack = qtrack;
return f->qtrack;
}
@@ -208,6 +212,13 @@ _mii_disk2_access(
case 0x0C:
case 0x0D:
c->lss_mode = (c->lss_mode & ~(1 << LOAD_BIT)) | (!!on << LOAD_BIT);
+ if (!(c->lss_mode & (1 << WRITE_BIT)) && f->heat) {
+ uint8_t track_id = f->track_id[f->qtrack];
+ uint32_t byte_index = f->bit_position >> 3;
+ unsigned int dstb = byte_index / MII_FLOPPY_HM_HIT_SIZE;
+ f->heat->read.map[track_id][dstb] = 255;
+ f->heat->read.seed++;
+ }
break;
case 0x0E:
case 0x0F:
@@ -230,19 +241,23 @@ static int
_mii_disk2_command(
mii_t * mii,
struct mii_slot_t *slot,
- uint8_t cmd,
+ uint32_t cmd,
void * param)
{
mii_card_disk2_t *c = slot->drv_priv;
+ int res = -1;
switch (cmd) {
case MII_SLOT_DRIVE_COUNT:
- if (param)
+ if (param) {
*(int *)param = 2;
+ res = 0;
+ }
break;
case MII_SLOT_DRIVE_WP ... MII_SLOT_DRIVE_WP + 2 - 1: {
int drive = cmd - MII_SLOT_DRIVE_WP;
int *wp = param;
if (wp) {
+ res = 0;
printf("Drive %d WP: 0x%x set %s\n", drive,
c->floppy[drive].write_protected,
*wp ? "ON" : "OFF");
@@ -271,9 +286,18 @@ _mii_disk2_command(
mii_floppy_init(&c->floppy[drive]);
mii_dd_drive_load(&c->drive[drive], file);
mii_floppy_load(&c->floppy[drive], file);
+ res = 0;
+ } break;
+ case MII_SLOT_D2_GET_FLOPPY: {
+ if (param) {
+ mii_floppy_t ** fp = param;
+ fp[0] = &c->floppy[0];
+ fp[1] = &c->floppy[1];
+ res = 0;
+ }
} break;
}
- return 0;
+ return res;
}
static mii_slot_drv_t _driver = {
@@ -372,16 +396,16 @@ _mii_disk2_lss_tick(
if (c->clock >= f->bit_timing) {
c->clock -= f->bit_timing;
uint8_t track_id = f->track_id[f->qtrack];
-
+ uint8_t * track = f->track_data[track_id];
uint32_t byte_index = f->bit_position >> 3;
uint8_t bit_index = 7 - (f->bit_position & 7);
if (!(c->lss_mode & (1 << WRITE_BIT))) {
- uint8_t bit = f->tracks[track_id].data[byte_index];
+ uint8_t bit = track[byte_index];
bit = (bit >> bit_index) & 1;
c->head = (c->head << 1) | bit;
// see WOZ spec for how we do this here
if ((c->head & 0xf) == 0) {
- bit = f->tracks[MII_FLOPPY_RANDOM_TRACK_ID].data[byte_index];
+ bit = f->track_data[MII_FLOPPY_NOISE_TRACK][byte_index];
bit = (bit >> bit_index) & 1;
// printf("RANDOM TRACK %2d %2d %2d : %d\n",
// track_id, byte_index, bit_index, bit);
@@ -390,16 +414,20 @@ _mii_disk2_lss_tick(
}
c->lss_mode = (c->lss_mode & ~(1 << RP_BIT)) | (bit << RP_BIT);
}
- if ((c->lss_mode & (1 << WRITE_BIT))) {
+ if ((c->lss_mode & (1 << WRITE_BIT)) && track_id != 0xff) {
uint8_t msb = c->data_register >> 7;
-
+#if 0
+ printf("WRITE %2d %4d %d : %d LSS State %x mode %x\n",
+ track_id, byte_index, bit_index, msb,
+ c->lss_state, c->lss_mode);
+#endif
if (!f->tracks[track_id].dirty) {
- printf("DIRTY TRACK %2d \n", track_id);
+ // printf("DIRTY TRACK %2d \n", track_id);
f->tracks[track_id].dirty = 1;
- f->tracks_dirty = 1;
+ f->seed_dirty++;
}
- f->tracks[track_id].data[byte_index] &= ~(1 << bit_index);
- f->tracks[track_id].data[byte_index] |= (msb << bit_index);
+ f->track_data[track_id][byte_index] &= ~(1 << bit_index);
+ f->track_data[track_id][byte_index] |= (msb << bit_index);
}
f->bit_position = (f->bit_position + 1) % f->tracks[track_id].bit_count;
}
@@ -418,9 +446,17 @@ _mii_disk2_lss_tick(
c->data_register = (c->data_register >> 1) |
(!!f->write_protected << 7);
break;
- case 3: // LD
+ case 3: {// LD
+ uint8_t track_id = f->track_id[f->qtrack];
c->data_register = c->write_register;
- break;
+ f->seed_dirty++;
+ if (f->heat) {
+ uint32_t byte_index = f->bit_position >> 3;
+ unsigned int dstb = byte_index/MII_FLOPPY_HM_HIT_SIZE;
+ f->heat->write.map[track_id][dstb] = 255;
+ f->heat->write.seed++;
+ }
+ } break;
}
} else { // CLR
c->data_register = 0;
@@ -448,8 +484,9 @@ _mii_mish_d2(
for (int i = 0; i < 2; i++) {
mii_floppy_t *f = &c->floppy[i];
printf("Drive %d %s\n", f->id, f->write_protected ? "WP" : "RW");
- printf("\tMotor: %3s qtrack:%d Bit %6d\n",
- f->motor ? "ON" : "OFF", f->qtrack, f->bit_position);
+ printf("\tMotor: %3s qtrack:%3d Bit %6d/%6d\n",
+ f->motor ? "ON" : "OFF", f->qtrack,
+ f->bit_position, f->tracks[0].bit_count);
}
return;
}
@@ -480,7 +517,7 @@ _mii_mish_d2(
count = atoi(argv[3]);
mii_card_disk2_t *c = _mish_d2;
mii_floppy_t *f = &c->floppy[sel];
- uint8_t *data = f->tracks[track].data;
+ uint8_t *data = f->track_data[track];
for (int i = 0; i < count; i += 8) {
uint8_t *line = data + i;
@@ -503,6 +540,12 @@ _mii_mish_d2(
}
return;
}
+ if (!strcmp(argv[1], "dirty")) {
+ mii_card_disk2_t *c = _mish_d2;
+ mii_floppy_t *f = &c->floppy[sel];
+ f->seed_dirty = f->seed_saved = rand();
+ return;
+ }
}
#include "mish.h"
diff --git a/src/drivers/mii_disk2.h b/src/drivers/mii_disk2.h
deleted file mode 100644
index a90d993..0000000
--- a/src/drivers/mii_disk2.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * mii_disk2.h
- *
- * Copyright (C) 2023 Michel Pollet
- *
- * SPDX-License-Identifier: MIT
- */
-
-#pragma once
diff --git a/src/drivers/mii_epromcard.c b/src/drivers/mii_epromcard.c
index 8bf663b..3c54499 100644
--- a/src/drivers/mii_epromcard.c
+++ b/src/drivers/mii_epromcard.c
@@ -103,14 +103,17 @@ static int
_mii_ee_command(
mii_t * mii,
struct mii_slot_t *slot,
- uint8_t cmd,
+ uint32_t cmd,
void * param)
{
mii_card_ee_t *c = slot->drv_priv;
+ int res = -1;
switch (cmd) {
case MII_SLOT_DRIVE_COUNT:
- if (param)
+ if (param) {
*(int *)param = 1;
+ res = 0;
+ }
break;
case MII_SLOT_DRIVE_LOAD:
const char *filename = param;
@@ -122,9 +125,10 @@ _mii_ee_command(
}
mii_dd_drive_load(&c->drive[0], file);
c->file = file ? file->map : (uint8_t*)mii_1mb_rom_data;
+ res = 0;
break;
}
- return 0;
+ return res;
}
static mii_slot_drv_t _driver = {
diff --git a/src/drivers/mii_smartport.c b/src/drivers/mii_smartport.c
index e280c57..01b36fd 100644
--- a/src/drivers/mii_smartport.c
+++ b/src/drivers/mii_smartport.c
@@ -292,14 +292,17 @@ static int
_mii_sm_command(
mii_t * mii,
struct mii_slot_t *slot,
- uint8_t cmd,
+ uint32_t cmd,
void * param)
{
mii_card_sm_t *c = slot->drv_priv;
+ int res = -1;
switch (cmd) {
case MII_SLOT_DRIVE_COUNT:
- if (param)
+ if (param) {
*(int *)param = MII_SM_DRIVE_COUNT;
+ res = 0;
+ }
break;
case MII_SLOT_DRIVE_LOAD ... MII_SLOT_DRIVE_LOAD + MII_SM_DRIVE_COUNT - 1:
int drive = cmd - MII_SLOT_DRIVE_LOAD;
@@ -311,9 +314,10 @@ _mii_sm_command(
return -1;
}
mii_dd_drive_load(&c->drive[drive], file);
+ res = 0;
break;
}
- return 0;
+ return res;
}
static uint8_t
diff --git a/src/drivers/mii_ssc.c b/src/drivers/mii_ssc.c
index c3e5bee..2fe5eb7 100644
--- a/src/drivers/mii_ssc.c
+++ b/src/drivers/mii_ssc.c
@@ -119,17 +119,19 @@ static int
_mii_ssc_command(
mii_t * mii,
struct mii_slot_t *slot,
- uint8_t cmd,
+ uint32_t cmd,
void * param)
{
// mii_card_ssc_t *c = slot->drv_priv;
+ int res = -1;
switch (cmd) {
case MII_SLOT_SSC_SET_TTY: {
const char * tty = param;
printf("%s: set tty %s\n", __func__, tty);
+ res = 0;
} break;
}
- return -1;
+ return res;
}
static mii_slot_drv_t _driver = {
diff --git a/src/format/mii_floppy.c b/src/format/mii_floppy.c
index 6fdae72..55afd24 100644
--- a/src/format/mii_floppy.c
+++ b/src/format/mii_floppy.c
@@ -25,7 +25,7 @@ mii_floppy_init(
f->bit_timing = 32;
f->qtrack = 15; // just to see something at seek time
f->bit_position = 0;
- f->tracks_dirty = 0;
+ f->seed_dirty = f->seed_saved = 0;
f->write_protected &= ~MII_FLOPPY_WP_MANUAL;// keep the manual WP bit
/* this will look like this; ie half tracks are 'random'
0: 0 1: 0 2:35 3: 1
@@ -34,9 +34,9 @@ mii_floppy_init(
*/
for (int i = 0; i < (int)sizeof(f->track_id); i++)
f->track_id[i] = ((i + 1) % 4) == 3 ?
- MII_FLOPPY_RANDOM_TRACK_ID : ((i + 2) / 4);
+ MII_FLOPPY_NOISE_TRACK : ((i + 2) / 4);
/* generate a buffer with about 30% one bits */
- uint8_t *random = f->tracks[MII_FLOPPY_RANDOM_TRACK_ID].data;
+ uint8_t *random = f->track_data[MII_FLOPPY_NOISE_TRACK];
memset(random, 0, 256);
uint32_t bits = 256 * 8;
uint32_t ones = bits * 0.3; // 30% ones
@@ -48,29 +48,39 @@ mii_floppy_init(
random[bit >> 3] |= (1 << (bit & 7));
ones--;
}
- // we also fill up the last (ramdom) track with copies of the first
- // 256 uint8_ts of itself, to make it look like a real track
+ // copy all that random stuff across the rest of the 'track'
int rbi = 0;
- for (int i = 0; i < 36; i++) {
+ for (int bi = 256; bi < MII_FLOPPY_DEFAULT_TRACK_SIZE; bi++)
+ random[bi] = random[rbi++ % 256];
+ // important, the +1 means we initialize the random track too
+ for (int i = 0; i < MII_FLOPPY_TRACK_COUNT + 1; i++) {
f->tracks[i].dirty = 0;
- f->tracks[i].bit_count = MII_FLOPPY_DEFAULT_TRACK_SIZE * 8;
+ f->tracks[i].bit_count = 6500 * 8;
// fill the whole array up to the end..
- for (int bi = 0; bi < (int)sizeof(f->tracks[i].data); bi++)
- f->tracks[i].data[bi] = random[rbi++ % 256];
+ uint8_t *track = f->track_data[i];
+ if (i != MII_FLOPPY_NOISE_TRACK) {
+#if 1
+ memset(track, 0, MII_FLOPPY_DEFAULT_TRACK_SIZE);
+#else
+ for (int bi = 0; bi < MII_FLOPPY_DEFAULT_TRACK_SIZE; bi++)
+ track[bi] = random[rbi++ % 256];
+#endif
+ }
}
}
static void
mii_track_write_bits(
mii_floppy_track_t * dst,
+ uint8_t * track_data,
uint8_t bits,
uint8_t count )
{
while (count--) {
- uint32_t uint8_t_index = dst->bit_count >> 3;
+ uint32_t byte_index = dst->bit_count >> 3;
uint8_t bit_index = 7 - (dst->bit_count & 7);
- dst->data[uint8_t_index] &= ~(1 << bit_index);
- dst->data[uint8_t_index] |= (!!(bits >> 7) << bit_index);
+ track_data[byte_index] &= ~(1 << bit_index);
+ track_data[byte_index] |= (!!(bits >> 7) << bit_index);
dst->bit_count++;
bits <<= 1;
}
@@ -91,8 +101,9 @@ static uint8_t _de44(uint8_t a, uint8_t b) {
static void
mii_nib_rebit_track(
- uint8_t *track,
- mii_floppy_track_t * dst)
+ uint8_t *src_track,
+ mii_floppy_track_t * dst,
+ uint8_t * dst_track)
{
dst->bit_count = 0;
uint32_t window = 0;
@@ -100,18 +111,18 @@ mii_nib_rebit_track(
int seccount = 0;
int state = 0; // look for address field
do {
- window = (window << 8) | track[srci++];
+ window = (window << 8) | src_track[srci++];
switch (state) {
case 0: {
if (window != 0xffd5aa96)
break;
for (int i = 0; i < (seccount == 0 ? 40 : 20); i++)
- mii_track_write_bits(dst, 0xff, 10);
- uint8_t * h = track + srci - 4; // incs first 0xff
- int tid = _de44(h[6], h[7]);
- int sid = _de44(h[8], h[9]);
- printf("Track %2d sector %2d\n", tid, sid);
- memcpy(dst->data + (dst->bit_count >> 3), track + srci - 4, 15);
+ mii_track_write_bits(dst, dst_track, 0xff, 10);
+ uint8_t * h = src_track + srci - 4; // incs first 0xff
+ // int tid = _de44(h[6], h[7]);
+ // int sid = _de44(h[8], h[9]);
+ // printf("Track %2d sector %2d\n", tid, sid);
+ memcpy(dst_track + (dst->bit_count >> 3), h, 15);
dst->bit_count += 15 * 8;
srci += 11;
state = 1;
@@ -120,9 +131,9 @@ mii_nib_rebit_track(
if (window != 0xffd5aaad)
break;
for (int i = 0; i < 4; i++)
- mii_track_write_bits(dst, 0xff, 10);
- uint8_t *h = track + srci - 4;
- memcpy(dst->data + (dst->bit_count >> 3), h, 4 + 342 + 4);
+ mii_track_write_bits(dst, dst_track, 0xff, 10);
+ uint8_t *h = src_track + srci - 4;
+ memcpy(dst_track + (dst->bit_count >> 3), h, 4 + 342 + 4);
dst->bit_count += (4 + 342 + 4) * 8;
srci += 4 + 342;
seccount++;
@@ -141,7 +152,7 @@ mii_floppy_load_nib(
printf("%s: loading NIB %s\n", __func__, filename);
for (int i = 0; i < 35; i++) {
uint8_t *track = file->map + (i * 6656);
- mii_nib_rebit_track(track, &f->tracks[i]);
+ mii_nib_rebit_track(track, &f->tracks[i], f->track_data[i]);
if (f->tracks[i].bit_count < 100) {
printf("%s: %s: Invalid track %d has zero bits!\n", __func__,
filename, i);
@@ -185,7 +196,8 @@ mii_floppy_write_track_woz(
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
- memcpy(trks->track[track_id].bits, f->tracks[track_id].data, byte_count);
+ memcpy(trks->track[track_id].bits,
+ f->track_data[track_id], byte_count);
trks->track[track_id].byte_count_le = htole16(byte_count);
} else {
mii_woz2_info_t *info = (mii_woz2_info_t *)(header + 1);
@@ -199,7 +211,7 @@ mii_floppy_write_track_woz(
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
- memcpy(track, f->tracks[track_id].data, byte_count);
+ memcpy(track, f->track_data[track_id], byte_count);
}
f->tracks[track_id].dirty = 0;
return 0;
@@ -213,12 +225,10 @@ mii_floppy_woz_load_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];
+ f->track_id[ti] = tmap->track_id[ti] == 0xff ?
+ MII_FLOPPY_NOISE_TRACK : tmap->track_id[ti];
+ if (tmap->track_id[ti] != 0xff)
+ used_tracks |= 1L << f->track_id[ti];
}
return used_tracks;
}
@@ -259,13 +269,13 @@ mii_floppy_load_woz(
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
#endif
int max_track = le32toh(trks->chunk.size_le) / sizeof(trks->track[0]);
- for (int i = 0; i < 35 && i < max_track; i++) {
+ for (int i = 0; i < MII_FLOPPY_TRACK_COUNT && 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));
+ memcpy(f->track_data[i], track, le16toh(trks->track[i].byte_count_le));
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
}
} else {
@@ -289,7 +299,7 @@ mii_floppy_load_woz(
#endif
/* TODO: this doesn't work yet... */
// f->bit_timing = info->optimal_bit_timing;
- for (int i = 0; i < 35; i++) {
+ for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
if (!(used_tracks & (1L << i))) {
// printf("WOZ: Track %d not used\n", i);
continue;
@@ -297,7 +307,7 @@ mii_floppy_load_woz(
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;
- memcpy(f->tracks[i].data, track, byte_count);
+ memcpy(f->track_data[i], track, byte_count);
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
}
}
@@ -434,7 +444,7 @@ mii_floppy_load_dsk(
mii_floppy_nibblize_sector(VOLUME_NUMBER, i, phys_sector,
&writePtr, track);
}
- mii_nib_rebit_track(nibbleBuf, &f->tracks[i]);
+ mii_nib_rebit_track(nibbleBuf, &f->tracks[i], f->track_data[i]);
}
free(nibbleBuf);
// DSK is read only
@@ -452,23 +462,23 @@ mii_floppy_update_tracks(
return -1;
if (f->write_protected & MII_FLOPPY_WP_RO_FILE)
return -1;
- if (!f->tracks_dirty)
+ if (f->seed_dirty == f->seed_saved)
return 0;
- for (int i = 0; i < 35; i++) {
+ for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
if (!f->tracks[i].dirty)
continue;
- printf("%s: track %d is dirty, saving\n", __func__, i);
+// printf("%s: track %d is dirty, saving\n", __func__, i);
switch (file->format) {
case MII_DD_FILE_NIB:
break;
case MII_DD_FILE_WOZ:
mii_floppy_write_track_woz(f, file, i);
- printf("%s: WOZ track %d updated\n", __func__, i);
+// printf("%s: WOZ track %d updated\n", __func__, i);
break;
}
f->tracks[i].dirty = 0;
}
- f->tracks_dirty = 0;
+ f->seed_saved = f->seed_dirty;
return 0;
}
@@ -498,5 +508,6 @@ mii_floppy_load(
f->write_protected |= MII_FLOPPY_WP_RO_FILE;
else
f->write_protected &= ~MII_FLOPPY_WP_RO_FILE;
+ f->seed_dirty = f->seed_saved = rand();
return res;
}
diff --git a/src/format/mii_floppy.h b/src/format/mii_floppy.h
index 1220248..7e35f6f 100644
--- a/src/format/mii_floppy.h
+++ b/src/format/mii_floppy.h
@@ -13,9 +13,7 @@
// for NIB and others. can be bigger on .WOZ
#define MII_FLOPPY_DEFAULT_TRACK_SIZE 6656
-// track containing random bits
-#define MII_FLOPPY_RANDOM_TRACK_ID 35
-
+#define MII_FLOPPY_TRACK_COUNT 35
/*
* Reasons for write protect. Ie checkbox in the UI, or file format
* doesn't support writes, or the file has no write permissions.
@@ -29,9 +27,31 @@ enum {
typedef struct mii_floppy_track_t {
uint8_t dirty : 1; // track has been written to
uint32_t bit_count;
- uint8_t data[6680]; // max suggested by WOZ spec
} mii_floppy_track_t;
+
+// 32 bytes of track data corresponds to one byte of heatmap
+#define MII_FLOPPY_HM_HIT_SIZE 32
+// thats 208 bytes per track or about 7KB*2 for the whole disk for read+write
+// we align it on 16 bytes to make it easier to use in a shader
+#define MII_FLOPPY_HM_TRACK_SIZE \
+ (((MII_FLOPPY_DEFAULT_TRACK_SIZE / MII_FLOPPY_HM_HIT_SIZE) + 15) & ~15)
+
+typedef struct mii_track_heatmap_t {
+ // 32 bytes of track data corresponds to one byte of heatmap
+ uint32_t seed, tex, cleared;
+ // this needs to be aligned, otherwise SSE code will die horribly
+ uint8_t map[MII_FLOPPY_TRACK_COUNT][MII_FLOPPY_HM_TRACK_SIZE]
+ __attribute__((aligned(16)));
+} mii_track_heatmap_t;
+
+typedef struct mii_floppy_heatmap_t {
+ mii_track_heatmap_t read, write;
+} mii_floppy_heatmap_t;
+
+//
+#define MII_FLOPPY_NOISE_TRACK MII_FLOPPY_TRACK_COUNT
+
typedef struct mii_floppy_t {
uint8_t write_protected : 3, id : 2;
uint8_t bit_timing; // 0=32 (default)
@@ -39,9 +59,16 @@ typedef struct mii_floppy_t {
uint8_t stepper; // last step we did...
uint8_t qtrack; // quarter track we are on
uint32_t bit_position;
- uint8_t tracks_dirty; // needs saving
- uint8_t track_id[35 * 4];
- mii_floppy_track_t tracks[36];
+ // this is incremented each time a track is marked dirty
+ uint32_t seed_dirty;
+ uint32_t seed_saved; // last seed we saved at
+ uint8_t track_id[MII_FLOPPY_TRACK_COUNT * 4];
+ mii_floppy_track_t tracks[MII_FLOPPY_TRACK_COUNT + 1];
+ // keep all the data together, we'll use it to make a texture
+ // the last track is used for noise
+ uint8_t track_data[MII_FLOPPY_TRACK_COUNT + 1][MII_FLOPPY_DEFAULT_TRACK_SIZE];
+ /* This is set by the UI to trakc the head movements, no functional use */
+ mii_floppy_heatmap_t * heat; // optional heatmap
} mii_floppy_t;
/*
@@ -54,8 +81,8 @@ mii_floppy_init(
int
mii_floppy_load(
- mii_floppy_t *f,
- mii_dd_file_t *file );
+ mii_floppy_t *f,
+ mii_dd_file_t *file );
int
mii_floppy_update_tracks(
diff --git a/src/mii_mish.c b/src/mii_mish.c
index 9909a5e..8b0053d 100644
--- a/src/mii_mish.c
+++ b/src/mii_mish.c
@@ -51,7 +51,8 @@ _mii_mish_cmd(
int argc,
const char * argv[])
{
- const char * state[] = { "RUNNING", "STOPPED", "STEP" };
+ const char * state[] = {
+ "INIT", "RUNNING", "STOPPED", "STEP", "TERMINATE"};
mii_t * mii = param;
if (!argv[1]) {
show_state:
diff --git a/src/mii_slot.h b/src/mii_slot.h
index 58732b1..9afc798 100644
--- a/src/mii_slot.h
+++ b/src/mii_slot.h
@@ -53,7 +53,7 @@ typedef struct mii_slot_drv_t {
int (*command)(
mii_t * mii,
struct mii_slot_t *slot,
- uint8_t cmd,
+ uint32_t cmd,
void * param);
} mii_slot_drv_t;
@@ -80,6 +80,8 @@ enum {
MII_SLOT_DRIVE_WP = 0x30, // + drive index 0...n
MII_SLOT_SSC_SET_TTY = 0x10, // param is a pathname, or NULL for a pty
+ // + drive index 0..1. Param is a mii_floppy_t **
+ MII_SLOT_D2_GET_FLOPPY = 0x40,
};
// send a command to a slot/driver. Return >=0 if ok, -1 if error
diff --git a/ui_gl/mii_emu_gl.c b/ui_gl/mii_emu_gl.c
index be426af..9e438a5 100644
--- a/ui_gl/mii_emu_gl.c
+++ b/ui_gl/mii_emu_gl.c
@@ -5,6 +5,9 @@
*
* SPDX-License-Identifier: MIT
*/
+/*
+ * This is the main file for the X11/GLX version of the MII emulator
+ */
#define _GNU_SOURCE // for asprintf
#include
#include
@@ -12,7 +15,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -24,69 +26,38 @@
#include "mish.h"
#include "mii_thread.h"
-#include "mii_mui.h"
-#include "minipt.h"
+#include "mii_mui_gl.h"
#include "miigl_counter.h"
#define MII_ICON64_DEFINE
#include "mii-icon-64.h"
/*
- * Note: This *assumes* that the GL implementation has support for non-power-of-2
- * textures, which is not a given for older implementations. However, I think
- * (by 2024) that's a safe assumption.
+ * Note: This *assumes* that the GL implementation has support for
+ * non-power-of-2 * textures, which is not a given for older
+ * implementations. However, I think (by 2024) that's a safe assumption.
*/
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
-#define POWER_OF_TWO 0
-
-typedef struct mii_gl_tex_t {
-// c2_rect_t frame;
- GLuint id;
-} mii_gl_tex_t;
+#define MII_MUI_GL_POW2 0
typedef struct mii_x11_t {
mii_mui_t video;
pthread_t cpu_thread;
- mui_drawable_t dr; // drawable
- uint32_t dr_padded_y;
-
- union {
- struct {
- mii_gl_tex_t mii_tex, mui_tex;
- };
- mii_gl_tex_t tex[2];
- };
-
- c2_rect_t video_frame; // current video frame
- float mui_alpha;
- void * transision_state;
- struct {
- mui_time_t start, end;
- c2_rect_t from, to;
- } transition;
-
Cursor cursor;
Display * dpy;
Window win;
- long last_button_click;
- struct {
- int ungrab, grab, grabbed, down;
- c2_pt_t pos;
- } mouse;
- mui_event_t key;
XVisualInfo * vis;
Colormap cmap;
XSetWindowAttributes swa;
- XWindowAttributes attr;
GLXFBConfig fbc;
Atom wm_delete_window;
int width, height;
GLXContext glContext;
- miigl_counter_t videoc, redrawc, sleepc;
+// miigl_counter_t videoc, redrawc, sleepc;
} mii_x11_t;
@@ -120,97 +91,6 @@ has_gl_extension(
return false;
}
-c2_rect_t
-c2_rect_interpolate(
- c2_rect_t *a,
- c2_rect_t *b,
- float t)
-{
- c2_rect_t r = {};
- r.l = 0.5 + a->l + (b->l - a->l) * t;
- r.r = 0.5 + a->r + (b->r - a->r) * t;
- r.t = 0.5 + a->t + (b->t - a->t) * t;
- r.b = 0.5 + a->b + (b->b - a->b) * t;
- return r;
-}
-
-static c2_rect_t
-_mii_get_video_position(
- mii_x11_t * ui,
- bool ui_visible )
-{
- c2_rect_t r = C2_RECT(0, 0, MII_VIDEO_WIDTH, MII_VIDEO_HEIGHT);
- if (ui_visible) {
- float fac = (ui->attr.height - 38) / (float)MII_VIDEO_HEIGHT;
- c2_rect_scale(&r, fac);
- c2_rect_offset(&r,
- (ui->attr.width / 2) - (c2_rect_width(&r) / 2), 36);
- } else {
- float fac = (ui->attr.height) / (float)MII_VIDEO_HEIGHT;
- c2_rect_scale(&r, fac);
- c2_rect_offset(&r,
- (ui->attr.width / 2) - (c2_rect_width(&r) / 2),
- (ui->attr.height / 2) - (c2_rect_height(&r) / 2));
- c2_rect_inset(&r, 10, 10);
- }
- return r;
-}
-
-static void
-_mii_transition(
- mii_x11_t * ui )
-{
- pt_start(ui->transision_state);
-
- while (ui->video.transition == MII_MUI_TRANSITION_NONE)
- pt_yield(ui->transision_state);
-
- ui->transition.start = mui_get_time();
- ui->transition.end = ui->transition.start + (MUI_TIME_SECOND / 2);
- ui->transition.from = ui->video_frame;
-
- switch (ui->video.transition) {
- case MII_MUI_TRANSITION_HIDE_UI:
- ui->transition.to = _mii_get_video_position(ui, false);
- ui->video.mui_visible = true;
- break;
- case MII_MUI_TRANSITION_SHOW_UI:
- ui->transition.to = _mii_get_video_position(ui, true);
- ui->video.mui_visible = true;
- break;
- }
- while (1) {
- mui_time_t now = mui_get_time();
- float t = (now - ui->transition.start) /
- (float)(ui->transition.end - ui->transition.start);
- if (t >= 1.0f)
- break;
- switch (ui->video.transition) {
- case MII_MUI_TRANSITION_HIDE_UI:
- ui->mui_alpha = 1.0f - t;
- break;
- case MII_MUI_TRANSITION_SHOW_UI:
- ui->mui_alpha = t;
- break;
- }
- ui->video_frame = c2_rect_interpolate(
- &ui->transition.from, &ui->transition.to, t);
- pt_yield(ui->transision_state);
- }
- switch (ui->video.transition) {
- case MII_MUI_TRANSITION_HIDE_UI:
- ui->video.mui_visible = false;
- ui->mui_alpha = 0.0f;
- break;
- case MII_MUI_TRANSITION_SHOW_UI:
- ui->mui_alpha = 1.0f;
- break;
- }
- ui->video.transition = MII_MUI_TRANSITION_NONE;
-
- pt_end(ui->transision_state);
-}
-
/*
* xmodmap -pke or -pk will print the list of keycodes
*/
@@ -244,6 +124,7 @@ _mii_x11_convert_keycode(
case XK_Alt_R: out->key.key = MUI_KEY_RALT; break;
case XK_Super_L: out->key.key = MUI_KEY_LSUPER; break;
case XK_Super_R: out->key.key = MUI_KEY_RSUPER; break;
+ case XK_Caps_Lock: out->key.key = MUI_KEY_CAPSLOCK; break;
default:
out->key.key = sym & 0xff;
break;
@@ -256,7 +137,7 @@ static int
mii_x11_init(
struct mii_x11_t *ui )
{
- mui_t * mui = &ui->video.mui;
+// mui_t * mui = &ui->video.mui;
if (!setlocale(LC_ALL,"") ||
!XSupportsLocale() ||
@@ -355,6 +236,18 @@ mii_x11_init(
sizeof(mii_icon64) / sizeof(mii_icon64[0]));
XFlush(ui->dpy);
}
+ {
+ XSizeHints *hints = XAllocSizeHints();
+ hints->flags = PMinSize | PAspect;// | PMaxSize;
+ hints->min_width = WINDOW_WIDTH / 2;
+ hints->min_height = WINDOW_HEIGHT / 2;
+ hints->max_aspect.x = WINDOW_WIDTH;
+ hints->max_aspect.y = WINDOW_HEIGHT;
+ hints->min_aspect.x = WINDOW_WIDTH;
+ hints->min_aspect.y = WINDOW_HEIGHT;
+ XSetWMNormalHints(ui->dpy, ui->win, hints);
+ XFree(hints);
+ }
XMapWindow(ui->dpy, ui->win);
ui->wm_delete_window = XInternAtom(ui->dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(ui->dpy, ui->win, &ui->wm_delete_window, 1);
@@ -401,92 +294,15 @@ mii_x11_init(
ui->glContext = create_context(ui->dpy, ui->fbc, 0, True, attr);
}
}
-
XSync(ui->dpy, False);
XSetErrorHandler(old_handler);
if (gl_err || !ui->glContext)
die("[X11]: Failed to create an OpenGL context\n");
glXMakeCurrent(ui->dpy, ui->win, ui->glContext);
}
- { // create the MUI 'screen' at the window size
- mui_pixmap_t* pix = &ui->dr.pix;
- pix->size.y = WINDOW_HEIGHT;
- pix->size.x = WINDOW_WIDTH;
- // annoyingly I have to make it a LOT bigger to handle that the
- // non-power-of-2 texture extension is not avialable everywhere
- // textures, which is a bit of a waste of memory, but oh well.
-
-#if POWER_OF_TWO
- int padded_x = 1;
- int padded_y = 1;
- while (padded_x < pix->size.x)
- padded_x <<= 1;
- while (padded_y < pix->size.y)
- padded_y <<= 1;
-#else
- int padded_x = pix->size.x;
- int padded_y = pix->size.y;
-#endif
- pix->row_bytes = padded_x * 4;
- pix->bpp = 32;
-
- ui->dr_padded_y = padded_y;
- printf("MUI Padded UI size is %dx%d\n", padded_x, padded_y);
-
- pix->pixels = malloc(pix->row_bytes * ui->dr_padded_y);
- mui->screen_size = pix->size;
- }
- {
- XGetWindowAttributes(ui->dpy, ui->win, &ui->attr);
- ui->mui_alpha = 1.0f;
- ui->video.mui_visible = true;
- ui->video_frame = _mii_get_video_position(ui, ui->video.mui_visible);
- }
return 0;
}
-static void
-mii_x11_update_mouse_card(
- mii_x11_t * ui)
-{
- mii_t * mii = &ui->video.mii;
- mui_t * mui = &ui->video.mui;
- /*
- * We can grab the mouse if it is enabled by the driver, it is in the
- * video frame, and there is no active MUI windows (or menus).
- */
- if (mii->mouse.enabled &&
- c2_rect_contains_pt(&ui->video_frame, &ui->mouse.pos) &&
- !(ui->video.mui_visible && mui_has_active_windows(mui))) {
- if (!ui->mouse.grabbed) {
- ui->mouse.grab = 1;
- ui->mouse.grabbed = 1;
- // printf("Grab mouse\n");
- }
- } else {
- if (ui->mouse.grabbed) {
- ui->mouse.ungrab = 1;
- ui->mouse.grabbed = 0;
- // printf("Ungrab mouse\n");
- }
- }
- if (!ui->mouse.grabbed)
- return;
- double x = ui->mouse.pos.x - ui->video_frame.l;
- double y = ui->mouse.pos.y - ui->video_frame.t;
- // get mouse button state
- int button = ui->mouse.down;
- // clamp coordinates inside bounds
- double vw = c2_rect_width(&ui->video_frame);
- double vh = c2_rect_height(&ui->video_frame);
- double mw = mii->mouse.max_x - mii->mouse.min_x;
- double mh = mii->mouse.max_y - mii->mouse.min_y;
- // normalize mouse coordinates
- mii->mouse.x = mii->mouse.min_x + (x * mw / vw) + 0.5;
- mii->mouse.y = mii->mouse.min_y + (y * mh / vh) + 0.5;
- mii->mouse.button = button;
-}
-
static int
mii_x11_handle_event(
mii_x11_t * ui,
@@ -496,15 +312,16 @@ mii_x11_handle_event(
/* We don't actually 'grab' as in warp the pointer, we just show/hide it
* dynamically when we enter/leave the video rectangle */
- if (ui->mouse.grab) {
+ if (ui->video.mouse.grab) {
XDefineCursor(ui->dpy, ui->win, ui->cursor);
- ui->mouse.grab = 0;
- } else if (ui->mouse.ungrab) {
+ ui->video.mouse.grab = 0;
+ } else if (ui->video.mouse.ungrab) {
XUndefineCursor(ui->dpy, ui->win);
- ui->mouse.ungrab = 0;
+ ui->video.mouse.ungrab = 0;
}
mui_t * mui = &ui->video.mui;
mii_t * mii = &ui->video.mii;
+
switch (evt->type) {
case FocusIn:
case FocusOut:
@@ -517,28 +334,31 @@ mii_x11_handle_event(
case KeyRelease:
case KeyPress: {
int ret, down = (evt->type == KeyPress);
- KeySym *code = XGetKeyboardMapping(ui->dpy, (
- KeyCode)evt->xkey.keycode, 1, &ret);
- ui->key.type = down ? MUI_EVENT_KEYDOWN : MUI_EVENT_KEYUP;
- ui->key.key.up = 0;
+ KeySym *code = XGetKeyboardMapping(ui->dpy,
+ (KeyCode)evt->xkey.keycode, 1, &ret);
+ ui->video.key.type = down ? MUI_EVENT_KEYDOWN : MUI_EVENT_KEYUP;
+ ui->video.key.key.up = 0;
bool handled = false;
- bool converted = _mii_x11_convert_keycode(ui, *code, &ui->key);
- bool is_modifier = ui->key.key.key >= MUI_KEY_MODIFIERS &&
- ui->key.key.key <= MUI_KEY_MODIFIERS_LAST;
+ bool converted = _mii_x11_convert_keycode(ui, *code,
+ &ui->video.key);
+ bool is_modifier = ui->video.key.key.key >= MUI_KEY_MODIFIERS &&
+ ui->video.key.key.key <= MUI_KEY_MODIFIERS_LAST;
if (converted) {
// convert keycodes into a bitfields of current modifiers
- if (ui->key.key.key >= MUI_KEY_MODIFIERS &&
- ui->key.key.key <= MUI_KEY_MODIFIERS_LAST) {
+ if (ui->video.key.key.key >= MUI_KEY_MODIFIERS &&
+ ui->video.key.key.key <= MUI_KEY_MODIFIERS_LAST) {
if (down)
- mui->modifier_keys |= (1 << (ui->key.key.key - MUI_KEY_MODIFIERS));
+ mui->modifier_keys |= (1 <<
+ (ui->video.key.key.key - MUI_KEY_MODIFIERS));
else
- mui->modifier_keys &= ~(1 << (ui->key.key.key - MUI_KEY_MODIFIERS));
+ mui->modifier_keys &= ~(1 <<
+ (ui->video.key.key.key - MUI_KEY_MODIFIERS));
}
- ui->key.modifiers = mui->modifier_keys;
- switch (ui->key.key.key) {
+ ui->video.key.modifiers = mui->modifier_keys;
+ switch (ui->video.key.key.key) {
case MUI_KEY_RSUPER:
case MUI_KEY_LSUPER: {
- int apple = ui->key.key.key - MUI_KEY_RSUPER;
+ int apple = ui->video.key.key.key - MUI_KEY_RSUPER;
mii_bank_t *bank = &mii->bank[MII_BANK_MAIN];
uint8_t old = mii_bank_peek(bank, 0xc061 + apple);
mii_bank_poke(bank, 0xc061 + apple, down ? 0x80 : 0);
@@ -548,7 +368,7 @@ mii_x11_handle_event(
}
} break;
}
- handled = mui_handle_event(mui, &ui->key);
+ handled = mui_handle_event(mui, &ui->video.key);
// if not handled and theres a window visible, assume
// it's a dialog and it OUGHT to eat the key
if (!handled)
@@ -556,7 +376,7 @@ mii_x11_handle_event(
// printf("%s key handled %d\n", __func__, handled);
}
if (!handled && down && !is_modifier) {
- uint16_t mii_key = ui->key.key.key;
+ uint16_t mii_key = ui->video.key.key.key;
char buf[32] = "";
KeySym keysym = 0;
if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) {
@@ -583,23 +403,23 @@ mii_x11_handle_event(
case ButtonRelease: {
// printf("%s %s button %d grabbed:%d\n", __func__,
// evt->type == ButtonPress ? "Down":"Up ",
- // evt->xbutton.button, ui->mouse.grabbed);
+ // evt->xbutton.button, ui->video.mouse.grabbed);
switch (evt->xbutton.button) {
case Button1: {
- ui->mouse.down = evt->type == ButtonPress;
- ui->mouse.pos.x = evt->xbutton.x;
- ui->mouse.pos.y = evt->xbutton.y;
+ ui->video.mouse.down = evt->type == ButtonPress;
+ ui->video.mouse.pos.x = evt->xbutton.x;
+ ui->video.mouse.pos.y = evt->xbutton.y;
if (ui->video.mui_visible) {
mui_event_t ev = {
- .type = ui->mouse.down ?
+ .type = ui->video.mouse.down ?
MUI_EVENT_BUTTONDOWN :
MUI_EVENT_BUTTONUP,
- .mouse.where = ui->mouse.pos,
+ .mouse.where = ui->video.mouse.pos,
.modifiers = mui->modifier_keys, // | MUI_MODIFIER_EVENT_TRACE,
};
mui_handle_event(mui, &ev);
}
- mii_x11_update_mouse_card(ui);
+ mii_mui_update_mouse_card(&ui->video);
} break;
case Button4:
case Button5: {
@@ -609,7 +429,7 @@ mii_x11_handle_event(
mui_event_t ev = {
.type = MUI_EVENT_WHEEL,
.modifiers = mui->modifier_keys,// | MUI_MODIFIER_EVENT_TRACE,
- .wheel.where = ui->mouse.pos,
+ .wheel.where = ui->video.mouse.pos,
.wheel.delta = evt->xbutton.button == Button4 ? -1 : 1,
};
mui_handle_event(mui, &ev);
@@ -617,16 +437,27 @@ mii_x11_handle_event(
} break;
}
} break;
+ case ConfigureNotify:
+ if (evt->xconfigure.width != ui->video.window_size.x ||
+ evt->xconfigure.height != ui->video.window_size.y) {
+ // Window is being resized
+ // Handle the resize event here
+ ui->video.window_size.x = evt->xconfigure.width;
+ ui->video.window_size.y = evt->xconfigure.height;
+ ui->video.video_frame = mii_mui_get_video_position(&ui->video);
+ mii_mui_update_mouse_card(&ui->video);
+ }
+ break;
case MotionNotify: {
- ui->mouse.pos.x = evt->xmotion.x;
- ui->mouse.pos.y = evt->xmotion.y;
- mii_x11_update_mouse_card(ui);
- if (ui->mouse.grabbed)
+ ui->video.mouse.pos.x = evt->xmotion.x;
+ ui->video.mouse.pos.y = evt->xmotion.y;
+ mii_mui_update_mouse_card(&ui->video);
+ if (ui->video.mouse.grabbed)
break;
if (ui->video.mui_visible) {
mui_event_t ev = {
.type = MUI_EVENT_DRAG,
- .mouse.where = ui->mouse.pos,
+ .mouse.where = ui->video.mouse.pos,
.modifiers = mui->modifier_keys,
};
mui_handle_event(mui, &ev);
@@ -655,133 +486,6 @@ mii_x11_terminate(
XCloseDisplay(ui->dpy);
}
-void
-mii_x11_prepare_textures(
- mii_x11_t *ui)
-{
- mii_t * mii = &ui->video.mii;
-// mui_t * mui = &ui->video.mui;
- GLuint tex[2];
- glGenTextures(2, tex);
- for (int i = 0; i < 2; i++) {
- mii_gl_tex_t * t = &ui->tex[i];
- memset(t, 0, sizeof(*t));
- t->id = tex[i];
- }
- glEnable(GL_TEXTURE_2D);
- // bind the mii texture using the GL_ARB_texture_rectangle extension
- glBindTexture(GL_TEXTURE_2D, ui->mii_tex.id);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- // disable the repeat of textures
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexImage2D(GL_TEXTURE_2D, 0, 4,
- MII_VRAM_WIDTH,
- MII_VRAM_HEIGHT, 0, GL_BGRA, // note BGRA here, not RGBA
- GL_UNSIGNED_BYTE,
- mii->video.pixels);
- // bind the mui texture using the GL_ARB_texture_rectangle as well
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, ui->mui_tex.id);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexImage2D(GL_TEXTURE_2D, 0, 4,
- ui->dr.pix.row_bytes / 4, // already power of two.
- ui->dr_padded_y, 0, GL_RGBA,
- GL_UNSIGNED_BYTE,
- ui->dr.pix.pixels);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-// printf("%s texture created %d\n", __func__, mii_apple_screen_tex);
-// display opengl error
- GLenum err = glGetError();
- if (err != GL_NO_ERROR) {
- printf("Error creating texture: %d\n", err);
- }
-}
-
-void
-mii_x11_render(
- mii_x11_t *ui)
-{
- glClearColor(
- .6f * ui->mui_alpha,
- .6f * ui->mui_alpha,
- .6f * ui->mui_alpha,
- 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT);
- glDisable(GL_CULL_FACE);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_SCISSOR_TEST);
- glEnable(GL_BLEND);
- glEnable(GL_TEXTURE_2D);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- /* setup viewport/project */
- glViewport(0, 0, (GLsizei)ui->attr.width, (GLsizei)ui->attr.height);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glOrtho(0.0f, ui->attr.width, ui->attr.height, 0.0f, -1.0f, 1.0f);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- // This (was) the recommended way to handle pixel alignment in glOrtho
- // mode, but this seems to have changed -- now it looks like Linear filtering
-// glTranslatef(0.375f, 0.375f, 0.0f);
- {
- /* draw mii texture */
- glColor3f(1.0f, 1.0f, 1.0f);
- glBindTexture(GL_TEXTURE_2D, ui->mii_tex.id);
- glBegin(GL_QUADS);
- c2_rect_t r = ui->video_frame;
- glTexCoord2f(0, 0);
- glVertex2f(r.l, r.t);
- glTexCoord2f(MII_VIDEO_WIDTH / (double)MII_VRAM_WIDTH, 0);
- glVertex2f(r.r, r.t);
- glTexCoord2f(MII_VIDEO_WIDTH / (double)MII_VRAM_WIDTH,
- MII_VIDEO_HEIGHT / (double)MII_VRAM_HEIGHT);
- glVertex2f(r.r, r.b);
- glTexCoord2f(0,
- MII_VIDEO_HEIGHT / (double)MII_VRAM_HEIGHT);
- glVertex2f(r.l, r.b);
- glEnd();
- /* draw mui texture */
- if (ui->mui_alpha > 0.0f) {
- glColor4f(1.0f, 1.0f, 1.0f, ui->mui_alpha);
- glBindTexture(GL_TEXTURE_2D, ui->mui_tex.id);
- glBegin(GL_QUADS);
- glTexCoord2f(0, 0); glVertex2f(0, 0);
- glTexCoord2f(ui->attr.width / (double)(ui->dr.pix.row_bytes / 4), 0);
- glVertex2f(ui->attr.width, 0);
- glTexCoord2f(ui->attr.width / (double)(ui->dr.pix.row_bytes / 4),
- ui->attr.height / (double)(ui->dr_padded_y));
- glVertex2f(ui->attr.width, ui->attr.height);
- glTexCoord2f(0,
- ui->attr.height / (double)(ui->dr_padded_y));
- glVertex2f(0, ui->attr.height);
- glEnd();
- }
- }
- glDisable(GL_CULL_FACE);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glPopAttrib();
-}
-
// TODO factor this into a single table, this is dupped from mii_mui_settings.c!
static const struct {
const char * name;
@@ -885,6 +589,7 @@ mii_x11_reload_config(
_mii_ui_load_config(mii, config, &flags);
mii_prepare(mii, flags);
mii_reset(mii, true);
+ mii_mui_gl_prepare_textures(&ui->video);
/* start the CPU/emulator thread */
ui->cpu_thread = mii_threads_start(mii);
@@ -902,7 +607,8 @@ main(
mkdir(conf_path, 0755);
mii_x11_t * ui = &g_mii;
- mii_t * mii = &g_mii.video.mii;
+ mii_t * mii = &g_mii.video.mii;
+ mui_t * mui = &g_mii.video.mui;
bool no_config_found = false;
if (mii_settings_load(
@@ -940,17 +646,11 @@ main(
printf("MISH_TELNET_PORT = %s\n", getenv("MISH_TELNET_PORT"));
}
mii_x11_init(ui);
- mui_t * mui = &ui->video.mui; // to move to a function later
- mui_init(mui);
- mui->color.clear.value = 0;
+ mii_mui_init(&ui->video, C2_PT(WINDOW_WIDTH, WINDOW_HEIGHT));
+ mii_mui_gl_init(&ui->video);
+
asprintf(&mui->pref_directory, "%s/.local/share/mii", getenv("HOME"));
- mii_mui_menus_init((mii_mui_t*)ui);
- ui->video.mui_visible = 1;
- mii_mui_menu_slot_menu_update(&ui->video);
-
- mii_x11_prepare_textures(ui);
-
// use a 60fps timerfd here as well
int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timerfd < 0) {
@@ -980,53 +680,13 @@ main(
continue;
mii_x11_handle_event(ui, &evt);
}
- mui_run(mui);
- bool draw = false;
- if (pixman_region32_not_empty(&mui->inval)) {
- draw = true;
- mui_draw(mui, &ui->dr, 0);
- glBindTexture(GL_TEXTURE_2D, ui->mui_tex.id);
-
- pixman_region32_intersect_rect(&mui->redraw, &mui->redraw,
- 0, 0, ui->dr.pix.size.x, ui->dr.pix.size.y);
- int rc = 0;
- c2_rect_t *ra = (c2_rect_t*)pixman_region32_rectangles(&mui->redraw, &rc);
- // rc = 1; ra = &C2_RECT(0, 0, mui->screen_size.x, mui->screen_size.y);
- if (rc) {
- // printf("GL: %d rects to redraw\n", rc);
- for (int i = 0; i < rc; i++) {
- c2_rect_t r = ra[i];
- // printf("GL: %d,%d %dx%d\n", r.l, r.t, c2_rect_width(&r), c2_rect_height(&r));
- glPixelStorei(GL_UNPACK_ROW_LENGTH, ui->dr.pix.row_bytes / 4);
- glTexSubImage2D(GL_TEXTURE_2D, 0, r.l, r.t,
- c2_rect_width(&r), c2_rect_height(&r),
- GL_BGRA, GL_UNSIGNED_BYTE,
- ui->dr.pix.pixels + (r.t * ui->dr.pix.row_bytes) + (r.l * 4));
- }
- }
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- pixman_region32_clear(&mui->redraw);
- }
- uint32_t current_frame = mii->video.frame_count;
- if (current_frame != mii->video.frame_drawn) {
- miigl_counter_tick(&ui->videoc, miigl_get_time());
- draw = true;
- mii->video.frame_drawn = current_frame;
- // update the whole texture
- glBindTexture(GL_TEXTURE_2D, ui->mii_tex.id);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
- MII_VRAM_WIDTH,
- MII_VIDEO_HEIGHT, GL_RGBA,
- GL_UNSIGNED_BYTE,
- mii->video.pixels);
- }
- /* Draw */
+ bool draw = mii_mui_gl_run(&ui->video);
if (draw) {
- miigl_counter_tick(&ui->redrawc, miigl_get_time());
- XGetWindowAttributes(ui->dpy, ui->win, &ui->attr);
- glViewport(0, 0, ui->width, ui->height);
- _mii_transition(ui);
- mii_x11_render(ui);
+ // miigl_counter_tick(&ui->redrawc, miigl_get_time());
+ // XGetWindowAttributes(ui->dpy, ui->win, &ui->attr);
+ glViewport(0, 0, ui->video.window_size.x, ui->video.window_size.y);
+ mii_mui_showhide_ui_machine(&ui->video);
+ mii_mui_gl_render(&ui->video);
glFlush();
glXSwapBuffers(ui->dpy, ui->win);
}
diff --git a/ui_gl/mii_loadbin.c b/ui_gl/mii_loadbin.c
index a4ead0f..a013433 100644
--- a/ui_gl/mii_loadbin.c
+++ b/ui_gl/mii_loadbin.c
@@ -6,7 +6,6 @@
* SPDX-License-Identifier: MIT
*/
-
#include
#include
#include
@@ -33,7 +32,7 @@ static void *
mii_thread_loadbin(
void *arg)
{
-
+
return NULL;
}
diff --git a/ui_gl/mii_mui.c b/ui_gl/mii_mui.c
new file mode 100644
index 0000000..6e7cf1b
--- /dev/null
+++ b/ui_gl/mii_mui.c
@@ -0,0 +1,193 @@
+/*
+ * mii_mui.c
+ *
+ * Copyright (C) 2024 Michel Pollet
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+/*
+ * This contains the integration between the MII video and the MUI interface
+ * without any specific windowing system, it should be possible to use this
+ * with a native windowing system, or a portable one like SDL2 or GLFW
+ * This doesn't do anything to draw on screen, it just moves the video
+ * rectangle around, and handles the mouse mapping to the video frame.
+ */
+#include
+#include
+
+#include "mii_mui.h"
+#include "minipt.h"
+
+#define MII_MUI_GL_POW2 0
+
+c2_rect_t
+c2_rect_interpolate(
+ c2_rect_t *a,
+ c2_rect_t *b,
+ float t)
+{
+ c2_rect_t r = {};
+ r.l = 0.5 + a->l + (b->l - a->l) * t;
+ r.r = 0.5 + a->r + (b->r - a->r) * t;
+ r.t = 0.5 + a->t + (b->t - a->t) * t;
+ r.b = 0.5 + a->b + (b->b - a->b) * t;
+ return r;
+}
+
+c2_rect_t
+mii_mui_get_video_position(
+ mii_mui_t * ui)
+{
+ c2_rect_t r = C2_RECT(0, 0, MII_VIDEO_WIDTH, MII_VIDEO_HEIGHT);
+ if (ui->mui_visible) {
+ float fac = (ui->window_size.y - 38) / (float)MII_VIDEO_HEIGHT;
+ c2_rect_scale(&r, fac);
+ c2_rect_offset(&r,
+ (ui->window_size.x / 2) - (c2_rect_width(&r) / 2), 36);
+ } else {
+ float fac = (ui->window_size.y) / (float)MII_VIDEO_HEIGHT;
+ c2_rect_scale(&r, fac);
+ c2_rect_offset(&r,
+ (ui->window_size.x / 2) - (c2_rect_width(&r) / 2),
+ (ui->window_size.y / 2) - (c2_rect_height(&r) / 2));
+ c2_rect_inset(&r, 10, 10);
+ }
+ return r;
+}
+
+void
+mii_mui_showhide_ui_machine(
+ mii_mui_t * ui )
+{
+ pt_start(ui->transision_state);
+
+ while (ui->transition.state == MII_MUI_TRANSITION_NONE)
+ pt_yield(ui->transision_state);
+
+ ui->transition.start = mui_get_time();
+ ui->transition.end = ui->transition.start + (MUI_TIME_SECOND / 2);
+ ui->transition.from = ui->video_frame;
+
+ switch (ui->transition.state) {
+ case MII_MUI_TRANSITION_HIDE_UI:
+ ui->mui_visible = false;
+ ui->transition.to = mii_mui_get_video_position(ui);
+ ui->mui_visible = true;
+ break;
+ case MII_MUI_TRANSITION_SHOW_UI:
+ ui->mui_visible = true;
+ ui->transition.to = mii_mui_get_video_position(ui);
+ break;
+ }
+ while (1) {
+ mui_time_t now = mui_get_time();
+ float t = (now - ui->transition.start) /
+ (float)(ui->transition.end - ui->transition.start);
+ if (t >= 1.0f)
+ break;
+ switch (ui->transition.state) {
+ case MII_MUI_TRANSITION_HIDE_UI:
+ ui->mui_alpha = 1.0f - t;
+ break;
+ case MII_MUI_TRANSITION_SHOW_UI:
+ ui->mui_alpha = t;
+ break;
+ }
+ ui->video_frame = c2_rect_interpolate(
+ &ui->transition.from, &ui->transition.to, t);
+ pt_yield(ui->transision_state);
+ }
+ switch (ui->transition.state) {
+ case MII_MUI_TRANSITION_HIDE_UI:
+ ui->mui_visible = false;
+ ui->mui_alpha = 0.0f;
+ break;
+ case MII_MUI_TRANSITION_SHOW_UI:
+ ui->mui_alpha = 1.0f;
+ break;
+ }
+ ui->transition.state = MII_MUI_TRANSITION_NONE;
+
+ pt_end(ui->transision_state);
+}
+
+void
+mii_mui_update_mouse_card(
+ mii_mui_t * ui)
+{
+ mii_t * mii = &ui->mii;
+ mui_t * mui = &ui->mui;
+ /*
+ * We can grab the mouse if it is enabled by the driver, it is in the
+ * video frame, and there is no active MUI windows (or menus).
+ */
+ if (mii->mouse.enabled &&
+ c2_rect_contains_pt(&ui->video_frame, &ui->mouse.pos) &&
+ !(ui->mui_visible && mui_has_active_windows(mui))) {
+ if (!ui->mouse.grabbed) {
+ ui->mouse.grab = 1;
+ ui->mouse.grabbed = 1;
+ // printf("Grab mouse\n");
+ }
+ } else {
+ if (ui->mouse.grabbed) {
+ ui->mouse.ungrab = 1;
+ ui->mouse.grabbed = 0;
+ // printf("Ungrab mouse\n");
+ }
+ }
+ if (!ui->mouse.grabbed)
+ return;
+ double x = ui->mouse.pos.x - ui->video_frame.l;
+ double y = ui->mouse.pos.y - ui->video_frame.t;
+ // get mouse button state
+ int button = ui->mouse.down;
+ // clamp coordinates inside bounds
+ double vw = c2_rect_width(&ui->video_frame);
+ double vh = c2_rect_height(&ui->video_frame);
+ double mw = mii->mouse.max_x - mii->mouse.min_x;
+ double mh = mii->mouse.max_y - mii->mouse.min_y;
+ // normalize mouse coordinates
+ mii->mouse.x = mii->mouse.min_x + (x * mw / vw) + 0.5;
+ mii->mouse.y = mii->mouse.min_y + (y * mh / vh) + 0.5;
+ mii->mouse.button = button;
+}
+
+void
+mii_mui_init(
+ mii_mui_t * ui,
+ c2_pt_t window_size)
+{
+ mui_drawable_t * dr = &ui->pixels.mui;
+ // annoyingly I have to make it a LOT bigger to handle that the
+ // non-power-of-2 texture extension is not avialable everywhere
+ // textures, which is a bit of a waste of memory, but oh well.
+#if MII_MUI_GL_POW2
+ int padded_x = 1;
+ int padded_y = 1;
+ while (padded_x < window_size.x)
+ padded_x <<= 1;
+ while (padded_y < window_size.y)
+ padded_y <<= 1;
+#else
+ int padded_x = window_size.x;
+ int padded_y = window_size.y;
+#endif
+ mui_drawable_init(dr, C2_PT(padded_x, padded_y), 32, NULL, 0);
+ dr->texture.size = C2_PT(padded_x, padded_y);
+ printf("MUI Padded UI size is %dx%d\n", padded_x, padded_y);
+ ui->mui.screen_size = dr->pix.size;
+
+ ui->window_size = window_size;
+ ui->mui_alpha = 1.0f;
+ ui->mui_visible = true;
+ ui->video_frame = mii_mui_get_video_position(ui);
+
+ mui_t * mui = &ui->mui;
+ mui_init(mui);
+ mii_mui_menus_init(ui);
+ mii_mui_menu_slot_menu_update(ui);
+ // Tell libmui to clear the background with transparency.
+ mui->color.clear.value = 0;
+}
diff --git a/ui_gl/mii_mui.h b/ui_gl/mii_mui.h
index 6be2a47..edbbedd 100644
--- a/ui_gl/mii_mui.h
+++ b/ui_gl/mii_mui.h
@@ -9,8 +9,8 @@
/*
This tries to contains a structure that is the MUI interface over the MII
video, but without any attachment to x11 or opengl. Basically hopefully
- segregating the relevant logic without tying it to a specific windowing system.
-
+ segregating the relevant logic without tying it to a specific windowing
+ system.
Hopefully with a bit more work this OUGHT to allow Windows/macOS port
with a native frontend.
*/
@@ -20,6 +20,7 @@
#include "mii.h"
#include "mui.h"
#include "mii_mui_settings.h"
+#include "mii_floppy.h"
enum mii_mui_transition_e {
MII_MUI_TRANSITION_NONE,
@@ -27,12 +28,46 @@ enum mii_mui_transition_e {
MII_MUI_TRANSITION_SHOW_UI,
};
+#define MII_PIXEL_LAYERS 8
+
typedef struct mii_mui_t {
mui_t mui; // mui interface
mii_t mii; // apple II emulator
+ c2_pt_t window_size;
+ long last_button_click;
+ struct {
+ int ungrab, grab, grabbed, down;
+ c2_pt_t pos;
+ } mouse;
+ mui_event_t key;
+ c2_rect_t video_frame; // current video frame
+ float mui_alpha;
bool mui_visible;
- uint8_t transition;
+ void * transision_state;
+ struct {
+ uint8_t state;
+ mui_time_t start, end;
+ c2_rect_t from, to;
+ } transition;
+ unsigned int tex_id[MII_PIXEL_LAYERS];
+ union {
+ struct {
+ mui_drawable_t mii;
+ mui_drawable_t mui;
+ struct {
+ mui_drawable_t bits;
+ mui_drawable_t hm_read;
+ mui_drawable_t hm_write;
+ } floppy[2];
+ };
+ mui_drawable_t v[MII_PIXEL_LAYERS];
+ } pixels;
+ struct {
+ mii_floppy_t * floppy;
+ uint32_t seed_load;
+ float max_width;
+ } floppy[2];
mii_machine_config_t config;
mii_loadbin_conf_t loadbin_conf;
@@ -40,6 +75,10 @@ typedef struct mii_mui_t {
mii_config_file_t cf;
} mii_mui_t;
+void
+mii_mui_init(
+ mii_mui_t * ui,
+ c2_pt_t window_size);
void
mii_mui_menus_init(
@@ -47,6 +86,17 @@ mii_mui_menus_init(
void
mii_mui_menu_slot_menu_update(
mii_mui_t * ui);
+
+void
+mii_mui_update_mouse_card(
+ mii_mui_t * ui);
+void
+mii_mui_showhide_ui_machine(
+ mii_mui_t * ui );
+c2_rect_t
+mii_mui_get_video_position(
+ mii_mui_t * ui);
+
// slot can be <= 0 to open the machine dialog instead
void
mii_config_open_slots_dialog(
diff --git a/ui_gl/mii_mui_2dsk.c b/ui_gl/mii_mui_2dsk.c
index 20d8cca..12016f5 100644
--- a/ui_gl/mii_mui_2dsk.c
+++ b/ui_gl/mii_mui_2dsk.c
@@ -43,12 +43,12 @@ typedef struct mii_mui_2dsk_t {
#include
#include
-typedef struct mii_floppy_check_t {
+typedef struct mii_imagefile_check_t {
char * error;
char * warning;
int file_ro;
int file_ro_format;
-} mii_floppy_check_t;
+} mii_imagefile_check_t;
#define NIB_SIZE 232960;
#define DSK_SIZE 143360;
@@ -64,7 +64,7 @@ _size_string(
static int
_mii_floppy_check_file(
const char * path,
- mii_floppy_check_t * out)
+ mii_imagefile_check_t * out)
{
char *filename = basename((char*)path);
@@ -162,13 +162,15 @@ mii_mui_2dsk_load_conf(
for (int i = 0; i < 2; i++) {
if (config->drive[i].disk[0]) {
ok = 1;
- mii_floppy_check_t check = {};
- if (_mii_floppy_check_file(config->drive[i].disk, &check) < 0) {
- mui_alert(m->win.ui, C2_PT(0,0),
- "Invalid Disk Image",
- check.error, MUI_ALERT_FLAG_OK);
- free(check.error);
- ok = 0;
+ mii_imagefile_check_t check = {};
+ if (m->drive_kind == MII_2DSK_DISKII) {
+ if (_mii_floppy_check_file(config->drive[i].disk, &check) < 0) {
+ mui_alert(m->win.ui, C2_PT(0,0),
+ "Invalid Disk Image",
+ check.error, MUI_ALERT_FLAG_OK);
+ free(check.error);
+ ok = 0;
+ }
}
config->drive[i].ro_file = check.file_ro;
config->drive[i].ro_format = check.file_ro_format;
@@ -389,6 +391,9 @@ mii_mui_load_2dsk(
cf, MUI_BUTTON_STYLE_CHECKBOX,
"Write Protect",
i == 0 ? MII_2DSK_WP1 : MII_2DSK_WP2);
+ // Smartport don't support write protect right now
+ if (drive_kind == MII_2DSK_SMARTPORT)
+ c->state = MUI_CONTROL_STATE_DISABLED;
c2_rect_right_of(&cf, cf.r, margin * 0.5);
cf.r = c2_rect_width(&w->frame) - margin * 1.2;
m->drive[i].warning = c = mui_textbox_new(w, cf,
diff --git a/ui_gl/mii_mui_gl.c b/ui_gl/mii_mui_gl.c
new file mode 100644
index 0000000..3bade28
--- /dev/null
+++ b/ui_gl/mii_mui_gl.c
@@ -0,0 +1,446 @@
+/*
+ * mii_mui_gl.c
+ *
+ * Copyright (C) 2024 Michel Pollet
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+/*
+ * This contains OpenGL code, no x11 or GLx allowed in here, this is to be
+ * used by a native windowing system, or a portable one like SDL2 or GLFW
+ */
+
+#include
+#include
+#include
+
+#ifdef __SSE2__
+#include // SSE2 intrinsics
+#endif
+
+#include "mii_mui_gl.h"
+#include "mii_floppy.h"
+
+
+typedef struct c2_rect_f {
+ float l,t,r,b;
+} c2_rect_f;
+
+void
+mii_mui_gl_init(
+ mii_mui_t *ui)
+{
+ GLuint tex[MII_PIXEL_LAYERS];
+ glGenTextures(MII_PIXEL_LAYERS, tex);
+ for (int i = 0; i < MII_PIXEL_LAYERS; i++) {
+ printf("Texture %d created %d\n", i, tex[i]);
+ ui->pixels.v[i].texture.id = tex[i];
+ ui->tex_id[i] = tex[i];
+ }
+
+ mii_mui_gl_prepare_textures(ui);
+}
+
+static void
+_prep_grayscale_texture(
+ mui_drawable_t * dr)
+{
+ dr->texture.size = dr->pix.size;
+ printf("Creating texture %4d %4dx%3d row_byte %4d\n",
+ dr->texture.id,
+ dr->pix.size.x, dr->pix.size.y,
+ dr->pix.row_bytes);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, 1,
+ dr->pix.row_bytes, dr->texture.size.y, 0, GL_LUMINANCE,
+ GL_UNSIGNED_BYTE,
+ dr->pix.pixels);
+}
+
+void
+mii_mui_gl_prepare_textures(
+ mii_mui_t *ui)
+{
+ mii_t * mii = &ui->mii;
+
+ glEnable(GL_TEXTURE_2D);
+ mui_drawable_t * dr = &ui->pixels.mii;
+ // bind the mii texture using the GL_ARB_texture_rectangle extension
+ printf("Creating texture %4d %4dx%3d row_byte %4d (MII)\n",
+ dr->texture.id,
+ dr->pix.size.x, dr->pix.size.y,
+ dr->pix.row_bytes);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ // disable the repeat of textures
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, 4,
+ MII_VRAM_WIDTH,
+ MII_VRAM_HEIGHT, 0, GL_BGRA, // note BGRA here, not RGBA
+ GL_UNSIGNED_BYTE,
+ mii->video.pixels);
+
+ // bind the mui texture using the GL_ARB_texture_rectangle as well
+ dr = &ui->pixels.mui;
+ printf("Creating texture %4d %4dx%3d row_byte %4d (MUI)\n",
+ dr->texture.id,
+ dr->pix.size.x, dr->pix.size.y,
+ dr->pix.row_bytes);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, 4,
+ dr->pix.row_bytes / 4, // already power of two.
+ dr->texture.size.y, 0, GL_BGRA,
+ GL_UNSIGNED_BYTE,
+ dr->pix.pixels);
+
+ mii_floppy_t * floppy[2] = {};
+ for (int i = 0; i < 7; i++) {
+ if (mii_slot_command(mii, i, MII_SLOT_D2_GET_FLOPPY, floppy) == 0)
+ break;
+ }
+ if (floppy[0]) {
+ for (int fi = 0; fi < 2; fi++) {
+ mii_floppy_t * f = floppy[fi];
+ ui->floppy[fi].floppy = f;
+
+ dr = &ui->pixels.floppy[fi].bits;
+ // the init() call clears the structure, keep our id around
+ unsigned int tex = dr->texture.id;
+ mui_drawable_init(dr,
+ C2_PT(MII_FLOPPY_DEFAULT_TRACK_SIZE, MII_FLOPPY_TRACK_COUNT),
+ 8, floppy[fi]->track_data, MII_FLOPPY_DEFAULT_TRACK_SIZE);
+ dr->texture.id = tex;
+ _prep_grayscale_texture(dr);
+ if (!f->heat)
+ f->heat = calloc(1, sizeof(*f->heat));
+ dr = &ui->pixels.floppy[fi].hm_read;
+ tex = dr->texture.id;
+ mui_drawable_init(dr,
+ C2_PT(MII_FLOPPY_HM_TRACK_SIZE, MII_FLOPPY_TRACK_COUNT),
+ 8, f->heat->read.map, MII_FLOPPY_HM_TRACK_SIZE);
+ dr->texture.id = tex;
+ _prep_grayscale_texture(dr);
+ dr = &ui->pixels.floppy[fi].hm_write;
+ tex = dr->texture.id;
+ mui_drawable_init(dr,
+ C2_PT(MII_FLOPPY_HM_TRACK_SIZE, MII_FLOPPY_TRACK_COUNT),
+ 8, f->heat->write.map, MII_FLOPPY_HM_TRACK_SIZE);
+ dr->texture.id = tex;
+ _prep_grayscale_texture(dr);
+ }
+ } else {
+ printf("No floppy found\n");
+ for (int fi = 0; fi < 2; fi++) {
+ ui->floppy[fi].floppy = NULL;
+ mui_drawable_clear(&ui->pixels.floppy[fi].bits);
+ mui_drawable_clear(&ui->pixels.floppy[fi].hm_read);
+ mui_drawable_clear(&ui->pixels.floppy[fi].hm_write);
+ }
+ }
+// printf("%s texture created %d\n", __func__, mii_apple_screen_tex);
+// display opengl error
+ GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ printf("Error creating texture: %d\n", err);
+ }
+}
+
+
+static void
+_mii_decay_heatmap_one(
+ mii_track_heatmap_t *hm)
+{
+ uint32_t count = 0;
+#ifdef __SSE2__
+ const int size = (MII_FLOPPY_TRACK_COUNT * MII_FLOPPY_HM_TRACK_SIZE) / 16;
+ __m128i * hmw = (__m128i*)&hm->map[0];
+ const __m128i s = _mm_set1_epi8(2);
+ for (int i = 0; i < size; i++) {
+ __m128i b = _mm_load_si128(hmw + i);
+ __m128i c = _mm_subs_epu8(b, s);
+ hmw[i] = c;
+ count += _mm_movemask_epi8(_mm_cmpgt_epi8(c, _mm_setzero_si128()));
+ }
+#else
+ const int size = MII_FLOPPY_TRACK_COUNT * MII_FLOPPY_HM_TRACK_SIZE;
+ uint8_t * hmb = (uint8_t*)&hm->map[0];
+ for (int i = 0; i < size; i++) {
+ uint8_t b = hmb[i];
+ b = b > 2 ? b - 2 : 0;
+ hmb[i] = b;
+ count += !!b;
+ }
+#endif
+ hm->cleared = count == 0;
+}
+
+static void
+_mii_decay_heatmap(
+ mii_floppy_heatmap_t *h)
+{
+ if (h->read.seed != h->read.tex || !h->read.cleared) {
+ h->read.tex = h->read.tex;
+ _mii_decay_heatmap_one(&h->read);
+ }
+ if (h->write.seed != h->write.tex || !h->write.cleared) {
+ h->write.tex = h->write.tex;
+ _mii_decay_heatmap_one(&h->write);
+ }
+}
+
+bool
+mii_mui_gl_run(
+ mii_mui_t *ui)
+{
+ mii_t * mii = &ui->mii;
+ mui_t * mui = &ui->mui;
+
+ mui_run(mui);
+ bool draw = false;
+ if (pixman_region32_not_empty(&mui->inval)) {
+ draw = true;
+ mui_drawable_t * dr = &ui->pixels.mui;
+ mui_draw(mui, dr, 0);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+
+ pixman_region32_intersect_rect(&mui->redraw, &mui->redraw,
+ 0, 0, dr->pix.size.x, dr->pix.size.y);
+ int rc = 0;
+ c2_rect_t *ra = (c2_rect_t*)pixman_region32_rectangles(&mui->redraw, &rc);
+ // rc = 1; ra = &C2_RECT(0, 0, mui->screen_size.x, mui->screen_size.y);
+ if (rc) {
+ // printf("GL: %d rects to redraw\n", rc);
+ for (int i = 0; i < rc; i++) {
+ c2_rect_t r = ra[i];
+ // printf("GL: %d,%d %dx%d\n", r.l, r.t, c2_rect_width(&r), c2_rect_height(&r));
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, dr->pix.row_bytes / 4);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, r.l, r.t,
+ c2_rect_width(&r), c2_rect_height(&r),
+ GL_BGRA, GL_UNSIGNED_BYTE,
+ dr->pix.pixels + (r.t * dr->pix.row_bytes) + (r.l * 4));
+ }
+ }
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ pixman_region32_clear(&mui->redraw);
+ }
+ uint32_t current_frame = mii->video.frame_count;
+ if (current_frame != mii->video.frame_drawn) {
+ // miigl_counter_tick(&ui->videoc, miigl_get_time());
+ draw = true;
+ mii->video.frame_drawn = current_frame;
+ // update the whole texture
+ mui_drawable_t * dr = &ui->pixels.mii;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ MII_VRAM_WIDTH,
+ MII_VIDEO_HEIGHT, GL_BGRA,
+ GL_UNSIGNED_BYTE,
+ mii->video.pixels);
+ }
+ for (int fi = 0; fi < 2; fi++) {
+ if (!ui->floppy[fi].floppy)
+ continue;
+ mui_drawable_t * dr = NULL;
+ mii_floppy_t * f = ui->floppy[fi].floppy;
+ if (ui->floppy[fi].seed_load != f->seed_dirty) {
+ draw = true;
+ ui->floppy[fi].seed_load = f->seed_dirty;
+ // printf("Floppy %d: Reloading texture\n", fi);
+ dr = &ui->pixels.floppy[fi].bits;
+ int bc = (f->tracks[0].bit_count + 7) / 8;
+ int max = MII_FLOPPY_DEFAULT_TRACK_SIZE;
+ ui->floppy[fi].max_width = (double)bc / (double)max;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ dr->pix.row_bytes, dr->pix.size.y,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ f->track_data);
+ }
+// int rm = f->heat->read.tex != f->heat->read.seed;
+// int wm = f->heat->write.tex != f->heat->write.seed;
+ _mii_decay_heatmap(f->heat);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, MII_FLOPPY_HM_TRACK_SIZE);
+// if (rm) {
+ dr = &ui->pixels.floppy[fi].hm_read;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ dr->pix.row_bytes, dr->pix.size.y,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ f->heat->read.map);
+// }
+// if (wm) {
+ dr = &ui->pixels.floppy[fi].hm_write;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ dr->pix.row_bytes, dr->pix.size.y,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ f->heat->write.map);
+// }
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
+ return draw;
+}
+
+void
+mii_mui_gl_render(
+ mii_mui_t *ui)
+{
+ glClearColor(
+ .6f * ui->mui_alpha,
+ .6f * ui->mui_alpha,
+ .6f * ui->mui_alpha,
+ 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ /* setup viewport/project */
+ glViewport(0, 0,
+ (GLsizei)ui->window_size.x,
+ (GLsizei)ui->window_size.y);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, ui->window_size.x, ui->window_size.y,
+ 0.0f, -1.0f, 1.0f);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ // This (was) the recommended way to handle pixel alignment in glOrtho
+ // mode, but this seems to have changed -- now it looks like Linear filtering
+// glTranslatef(0.375f, 0.375f, 0.0f);
+ {
+ /* draw mii texture */
+ glColor3f(1.0f, 1.0f, 1.0f);
+ mui_drawable_t * dr = &ui->pixels.mii;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glBegin(GL_QUADS);
+ c2_rect_t r = ui->video_frame;
+ glTexCoord2f(0, 0);
+ glVertex2f(r.l, r.t);
+ glTexCoord2f(MII_VIDEO_WIDTH / (double)MII_VRAM_WIDTH, 0);
+ glVertex2f(r.r, r.t);
+ glTexCoord2f(MII_VIDEO_WIDTH / (double)MII_VRAM_WIDTH,
+ MII_VIDEO_HEIGHT / (double)MII_VRAM_HEIGHT);
+ glVertex2f(r.r, r.b);
+ glTexCoord2f(0,
+ MII_VIDEO_HEIGHT / (double)MII_VRAM_HEIGHT);
+ glVertex2f(r.l, r.b);
+ glEnd();
+
+ /* draw floppy textures, floppy 0 is left of the screen,
+ floppy 1 is right */
+ for (int i = 0; i < 2; i++) {
+ dr = &ui->pixels.floppy[i].bits;
+ mii_floppy_t *f = ui->floppy[i].floppy;
+ if (!f || !dr->pix.pixels)
+ continue;
+ if (f->motor) {
+ dr->texture.opacity = 1.0f;
+ } else {
+ if (dr->texture.opacity > 0.0f)
+ dr->texture.opacity -= 0.01f;
+ if (dr->texture.opacity < 0.0f)
+ dr->texture.opacity = 0.0f;
+ }
+ if (dr->texture.opacity <= 0.0f)
+ continue;
+ c2_rect_t r = C2_RECT_WH( 0, 0,
+ ui->video_frame.l - 20,
+ c2_rect_height(&ui->video_frame) - 22);
+ c2_rect_f tr = { 0, 0, ui->floppy[i].max_width, 1 };
+ if (i == 0)
+ c2_rect_offset(&r,
+ ui->video_frame.l - c2_rect_width(&r) - 10,
+ ui->video_frame.t + 10);
+ else
+ c2_rect_offset(&r, ui->video_frame.r + 10,
+ ui->video_frame.t + 10);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(1.0f, 1.0f, 1.0f, dr->texture.opacity);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glBegin(GL_QUADS);
+ // rotate texture 90 clockwise, and mirror left-right
+ glTexCoord2f(tr.l, tr.t); glVertex2f(r.l, r.t);
+ glTexCoord2f(tr.l, tr.b); glVertex2f(r.r, r.t);
+ glTexCoord2f(tr.r, tr.b); glVertex2f(r.r, r.b);
+ glTexCoord2f(tr.r, tr.t); glVertex2f(r.l, r.b);
+ glEnd();
+
+ if (f->heat) {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR);
+ dr = &ui->pixels.floppy[i].hm_read;
+ glColor4f(0.0f, 1.0f, 0.0f, 1.0);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glBegin(GL_QUADS);
+ // rotate texture 90 clockwise, and mirror left-right
+ glTexCoord2f(tr.l, tr.t); glVertex2f(r.l, r.t);
+ glTexCoord2f(tr.l, tr.b); glVertex2f(r.r, r.t);
+ glTexCoord2f(tr.r, tr.b); glVertex2f(r.r, r.b);
+ glTexCoord2f(tr.r, tr.t); glVertex2f(r.l, r.b);
+ glEnd();
+ dr = &ui->pixels.floppy[i].hm_write;
+ glColor4f(1.0f, 0.0f, 0.0f, 1.0);
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glBegin(GL_QUADS);
+ // rotate texture 90 clockwise, and mirror left-right
+ glTexCoord2f(tr.l, tr.t); glVertex2f(r.l, r.t);
+ glTexCoord2f(tr.l, tr.b); glVertex2f(r.r, r.t);
+ glTexCoord2f(tr.r, tr.b); glVertex2f(r.r, r.b);
+ glTexCoord2f(tr.r, tr.t); glVertex2f(r.l, r.b);
+ glEnd();
+ }
+ }
+ /* draw mui texture */
+ if (ui->mui_alpha > 0.0f) {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(1.0f, 1.0f, 1.0f, ui->mui_alpha);
+ dr = &ui->pixels.mui;
+ glBindTexture(GL_TEXTURE_2D, dr->texture.id);
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0); glVertex2f(0, 0);
+ glTexCoord2f(
+ ui->window_size.x / (double)(dr->pix.row_bytes / 4), 0);
+ glVertex2f(ui->window_size.x, 0);
+ glTexCoord2f(ui->window_size.x / (double)(dr->pix.row_bytes / 4),
+ ui->window_size.y / (double)(dr->texture.size.y));
+ glVertex2f(ui->window_size.x, ui->window_size.y);
+ glTexCoord2f(0,
+ ui->window_size.y / (double)(dr->texture.size.y));
+ glVertex2f(0, ui->window_size.y);
+ glEnd();
+ }
+ }
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glPopAttrib();
+}
diff --git a/ui_gl/mii_mui_gl.h b/ui_gl/mii_mui_gl.h
new file mode 100644
index 0000000..086caa8
--- /dev/null
+++ b/ui_gl/mii_mui_gl.h
@@ -0,0 +1,23 @@
+/*
+ * mii_mui_gl.h
+ *
+ * Copyright (C) 2023 Michel Pollet
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#pragma once
+
+#include "mii_mui.h"
+
+void
+mii_mui_gl_init(
+ mii_mui_t *ui);
+void
+mii_mui_gl_prepare_textures(
+ mii_mui_t *ui);
+void
+mii_mui_gl_render(
+ mii_mui_t *ui);
+bool
+mii_mui_gl_run(
+ mii_mui_t *ui);
diff --git a/ui_gl/mii_mui_menus.c b/ui_gl/mii_mui_menus.c
index c9075da..b50cb0c 100644
--- a/ui_gl/mii_mui_menus.c
+++ b/ui_gl/mii_mui_menus.c
@@ -39,12 +39,12 @@ mii_quit_confirm_cb(
void * param)
{
mii_mui_t * ui = cb_param;
- printf("%s %4.4s\n", __func__, (char*)&what);
+// printf("%s %4.4s\n", __func__, (char*)&what);
if (what == MUI_CONTROL_ACTION_SELECT) {
mui_control_t * c = param;
- printf("%s %4.4s\n", __func__, (char*)&c->uid);
+// printf("%s %4.4s\n", __func__, (char*)&c->uid);
if (c->uid == MUI_ALERT_BUTTON_OK) {
- printf("%s Quit\n", __func__);
+// printf("%s Quit\n", __func__);
mii_t * mii = &ui->mii;
mii->state = MII_TERMINATE;
}
@@ -92,25 +92,6 @@ mii_config_open_slots_dialog(
mii_config_save_cb, ui);
}
-
-static void
-_mii_show_about(
- mii_mui_t * ui)
-{
- mui_t * mui = &ui->mui;
- mui_window_t *w = mui_window_get_by_id(mui, FCC('a','b','o','t'));
- if (w) {
- mui_window_select(w);
- return;
- }
- w = mui_alert(mui, C2_PT(0,0),
- "About MII",
- "Version " MII_VERSION "\n"
- "Build " __DATE__ " " __TIME__,
- MUI_ALERT_INFO);
- mui_window_set_id(w, FCC('a','b','o','t'));
-}
-
static int
mii_menubar_action(
mui_window_t *win, // window (menubar)
@@ -198,7 +179,7 @@ mii_menubar_action(
case FCC('s','h','m','b'): {
items[i].disabled =
(mui_window_front(mui) != NULL) ||
- (ui->transition != MII_MUI_TRANSITION_NONE);
+ (ui->transition.state != MII_MUI_TRANSITION_NONE);
} break;
}
}
@@ -209,14 +190,12 @@ mii_menubar_action(
// (char*)&item->uid, item->title);
switch (item->uid) {
case FCC('a','b','o','t'): {
-// _mii_show_about(ui);
mii_mui_about(&ui->mui);
} break;
case FCC('q','u','i','t'): {
-// printf("%s Quit?\n", __func__);
if (!ui->mui_visible &&
- ui->transition == MII_MUI_TRANSITION_NONE)
- ui->transition = MII_MUI_TRANSITION_SHOW_UI;
+ ui->transition.state == MII_MUI_TRANSITION_NONE)
+ ui->transition.state = MII_MUI_TRANSITION_SHOW_UI;
mui_window_t * really = mui_window_get_by_id(
&ui->mui, FCC('q','u','i','t'));
if (really)
@@ -231,12 +210,12 @@ mii_menubar_action(
}
} break;
case FCC('s','h','m','b'): {
- if (ui->transition != MII_MUI_TRANSITION_NONE)
+ if (ui->transition.state != MII_MUI_TRANSITION_NONE)
break;
if (ui->mui_visible) {
- ui->transition = MII_MUI_TRANSITION_HIDE_UI;
+ ui->transition.state = MII_MUI_TRANSITION_HIDE_UI;
} else {
- ui->transition = MII_MUI_TRANSITION_SHOW_UI;
+ ui->transition.state = MII_MUI_TRANSITION_SHOW_UI;
}
} break;
case FCC('d','s','k','0'):