mirror of
https://github.com/buserror/mii_emu.git
synced 2025-01-02 11:29:45 +00:00
Floppy bitstream view, also major refactor of UI code
Split the UI into separate bits. + Bits that are X11/GLX only + Bits that are 'pure' opengl + Bits that are UI related but not renderer related Hopefully with help porting to other platformsI Signed-off-by: Michel Pollet <buserror@gmail.com>
This commit is contained in:
parent
ab8a746bc8
commit
2e12a37bce
7
Makefile
7
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
|
||||
|
10
README.md
10
README.md
@ -3,6 +3,16 @@
|
||||
</p>
|
||||
|
||||
# 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).
|
||||
|
BIN
docs/heat_map.png
Normal file
BIN
docs/heat_map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <fcntl.h>
|
||||
#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"
|
||||
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* mii_disk2.h
|
||||
*
|
||||
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -12,7 +15,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <locale.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/stat.h>
|
||||
@ -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);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -33,7 +32,7 @@ static void *
|
||||
mii_thread_loadbin(
|
||||
void *arg)
|
||||
{
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
193
ui_gl/mii_mui.c
Normal file
193
ui_gl/mii_mui.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* mii_mui.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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(
|
||||
|
@ -43,12 +43,12 @@ typedef struct mii_mui_2dsk_t {
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
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,
|
||||
|
446
ui_gl/mii_mui_gl.c
Normal file
446
ui_gl/mii_mui_gl.c
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* mii_mui_gl.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <GL/gl.h>
|
||||
|
||||
#ifdef __SSE2__
|
||||
#include <emmintrin.h> // 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();
|
||||
}
|
23
ui_gl/mii_mui_gl.h
Normal file
23
ui_gl/mii_mui_gl.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* mii_mui_gl.h
|
||||
*
|
||||
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* 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);
|
@ -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'):
|
||||
|
Loading…
Reference in New Issue
Block a user