Add drag-and-drop floppies, fix cold-start

On cold-start, force Button0/OpenApple on for a short period
    This is how cold-start (disk boot) is differentiated from plain RESET
Add padding widget that pads another widget on all sides
Add drag-and-drop functionality to widgets and manage through GLFW
Honor current time through all widget methods
Fix character encoding in text_widget (especially for ' ')
Add floppy_icon widget that manages drag-and-drop and drive motor (activity) light
    Later move to vector graphic or bitmap
Add floppy eject and insert notification from APPLE2Einterface
Add activity notification through APPLE2Einterface
Optimize with -O2
This commit is contained in:
Brad Grantham 2016-12-02 10:42:22 -08:00
parent 04fc305a36
commit 96835e2400
4 changed files with 223 additions and 46 deletions

View File

@ -1,5 +1,5 @@
INCFLAGS += -I/opt/local/include
CXXFLAGS += $(INCFLAGS) -g -Wall --std=c++11 -O
CXXFLAGS += $(INCFLAGS) -g -Wall --std=c++11 -O2
LDFLAGS += -L/opt/local/lib
LDLIBS += -lglfw -lao -framework OpenGL -framework Cocoa -framework IOkit

View File

@ -329,7 +329,11 @@ struct DISKIIboard : board_base
}
}
DISKIIboard(unsigned char diskII_rom[256], char *floppy0_name, char *floppy1_name)
typedef std::function<void (int number, bool activity)> floppy_activity_func;
floppy_activity_func floppy_activity;
DISKIIboard(unsigned char diskII_rom[256], char *floppy0_name, char *floppy1_name, floppy_activity_func floppy_activity_) :
floppy_activity(floppy_activity_)
{
std::copy(diskII_rom, diskII_rom + 0x100, rom_C600.memory.begin());
set_floppy(0, floppy0_name);
@ -426,12 +430,14 @@ struct DISKIIboard : board_base
} else if(addr == ENABLE) {
if(debug & DEBUG_FLOPPY) printf("floppy switch off\n");
drive_motor_enabled[drive_selected] = false;
floppy_activity(drive_selected, false);
// go disable reading
// disable other drive?
return true;
} else if(addr == ENABLE + 1) {
if(debug & DEBUG_FLOPPY) printf("floppy switch on\n");
drive_motor_enabled[drive_selected] = true;
floppy_activity(drive_selected, true);
// go enable reading
// disable other drive?
return true;
@ -560,6 +566,12 @@ struct MAINboard : board_base
audio_buffer_next_sample = current_sample;
}
clk_t open_apple_down_ends = 0;
void momentary_open_apple(clk_t how_long)
{
open_apple_down_ends = clk + how_long;
}
// flush anything needing flushing
void sync()
{
@ -722,6 +734,10 @@ struct MAINboard : board_base
return true;
} else if(addr >= 0xC061 && addr <= 0xC063) {
int num = addr - 0xC061;
if(num == 0 && (open_apple_down_ends > clk)) {
data = 0xff;
return true;
}
float value;
bool button;
tie(value, button) = get_paddle(num);
@ -2429,6 +2445,8 @@ map<int, key_to_ascii> interface_key_to_apple2e =
{' ', {' ', ' ', 0, 0}},
};
DISKIIboard *diskIIboard;
enum APPLE2Einterface::EventType process_events(MAINboard *board, bus_frontend& bus, CPU6502& cpu)
{
static bool shift_down = false;
@ -2437,7 +2455,12 @@ enum APPLE2Einterface::EventType process_events(MAINboard *board, bus_frontend&
while(APPLE2Einterface::event_waiting()) {
APPLE2Einterface::event e = APPLE2Einterface::dequeue_event();
if(e.type == APPLE2Einterface::PASTE) {
if(e.type == APPLE2Einterface::EJECT_FLOPPY) {
diskIIboard->set_floppy(e.value, NULL);
} else if(e.type == APPLE2Einterface::INSERT_FLOPPY) {
diskIIboard->set_floppy(e.value, e.str);
free(e.str);
} else if(e.type == APPLE2Einterface::PASTE) {
for(int i = 0; i < strlen(e.str); i++)
if(e.str[i] == '\n')
board->enqueue_key('\r');
@ -2500,7 +2523,9 @@ enum APPLE2Einterface::EventType process_events(MAINboard *board, bus_frontend&
cpu.reset(bus);
} else if(e.type == APPLE2Einterface::REBOOT) {
bus.reset();
cpu.nmi(bus);
board->momentary_open_apple(machine_clock_rate / 5);
cpu.reset(bus);
// cpu.nmi(bus);
} else if(e.type == APPLE2Einterface::PAUSE) {
pause_cpu = e.value;
} else if(e.type == APPLE2Einterface::SPEED) {
@ -2648,8 +2673,9 @@ int main(int argc, char **argv)
floppy2_name = NULL;
try {
DISKIIboard *diskII = new DISKIIboard(diskII_rom, floppy1_name, floppy2_name);
mainboard->boards.push_back(diskII);
DISKIIboard::floppy_activity_func activity = [](int num, bool activity){APPLE2Einterface::show_floppy_activity(num, activity);};
diskIIboard = new DISKIIboard(diskII_rom, floppy1_name, floppy2_name, activity);
mainboard->boards.push_back(diskIIboard);
} catch(const char *msg) {
cerr << msg << endl;
exit(EXIT_FAILURE);
@ -2669,7 +2695,7 @@ int main(int argc, char **argv)
deque<saved_inst> previous_instructions;
APPLE2Einterface::start(run_fast, diskII_rom_name != NULL);
APPLE2Einterface::start(run_fast, diskII_rom_name != NULL, floppy1_name != NULL, floppy2_name != NULL);
chrono::time_point<chrono::system_clock> then = std::chrono::system_clock::now();

View File

@ -188,22 +188,6 @@ static const char *hires_vertex_shader = "\n\
gl_Position = vec4(screen_coords.x, screen_coords.y, .5, 1);\n\
}\n";
static const char *text_vertex_shader = "\n\
uniform mat3 to_screen;\n\
in vec2 vertex_coords;\n\
uniform float x_offset;\n\
uniform float y_offset;\n\
out vec2 text_coords;\n\
out vec2 raster_coords;\n\
\n\
void main()\n\
{\n\
raster_coords = vertex_coords;\n\
text_coords = vec2(int(vertex_coords.x / 7), int(vertex_coords.y / 8));\n\
vec3 screen_coords = to_screen * vec3(vertex_coords + vec2(x_offset, y_offset), 1);\n\
gl_Position = vec4(screen_coords.x, screen_coords.y, .5, 1);\n\
}\n";
static const char *hires_fragment_shader = "\n\
in vec2 raster_coords;\n\
uniform usampler2DRect hires_texture;\n\
@ -221,6 +205,20 @@ static const char *hires_fragment_shader = "\n\
color = vec4(value, value, value, value);\n\
}\n";
static const char *text_vertex_shader = "\n\
uniform mat3 to_screen;\n\
in vec2 vertex_coords;\n\
uniform float x_offset;\n\
uniform float y_offset;\n\
out vec2 raster_coords;\n\
\n\
void main()\n\
{\n\
raster_coords = vertex_coords;\n\
vec3 screen_coords = to_screen * vec3(vertex_coords + vec2(x_offset, y_offset), 1);\n\
gl_Position = vec4(screen_coords.x, screen_coords.y, .5, 1);\n\
}\n";
// 0-31 is inverse 32-63
// 32-63 is inverse 0-31
// 64-95 is blink 32-63
@ -232,7 +230,6 @@ static const char *hires_fragment_shader = "\n\
static const char *text_fragment_shader = "\n\
in vec2 raster_coords;\n\
in vec2 text_coords;\n\
uniform int blink;\n\
uniform vec4 foreground;\n\
uniform vec4 background;\n\
@ -280,7 +277,6 @@ static const char *text_fragment_shader = "\n\
static const char *lores_fragment_shader = "\n\
in vec2 raster_coords;\n\
in vec2 text_coords;\n\
uniform usampler2DRect font_texture;\n\
uniform usampler2DRect lores_texture;\n\
\n\
@ -570,6 +566,55 @@ struct placed_widget
float x, y, w, h;
};
struct padding : public widget
{
widget* child;
float w, h;
float left_pad, right_pad, top_pad, bottom_pad;
float cw, ch;
padding(float left_pad_, float right_pad_, float top_pad_, float bottom_pad_, widget* child_) :
child(child_),
left_pad(left_pad_),
right_pad(right_pad_),
top_pad(top_pad_),
bottom_pad(bottom_pad_)
{
tie(cw, ch) = child->get_min_dimensions();
w = cw + left_pad_ + right_pad_;
h = ch + top_pad_ + bottom_pad_;
}
virtual tuple<float, float> get_min_dimensions() const
{
return make_tuple(w, h);
}
virtual void draw(double now, float to_screen[9], float x, float y, float w_, float h_)
{
child->draw(now, to_screen, x + left_pad, y + top_pad, w_ - left_pad - right_pad, h_ - top_pad - bottom_pad);
}
virtual bool drop(double now, float x, float y, int count, const char **paths)
{
return child->drop(now, x + left_pad, y + top_pad, count, paths);
}
virtual bool click(double now, float x, float y)
{
return child->click(now, x + left_pad, y + top_pad);
}
virtual void hover(double now, float x, float y)
{
child->hover(now, x + left_pad, y + top_pad);
}
virtual void drag(double now, float x, float y)
{
child->drag(now, x + left_pad, y + top_pad);
}
virtual void release(double now, float x, float y)
{
child->release(now, x + left_pad, y + top_pad);
}
};
struct centering : public widget
{
float w, h;
@ -794,11 +839,11 @@ struct text_widget : public widget
int i = 0;
for(auto it = content.begin(); it != content.end(); it++) {
unsigned char c = *it;
if(c > ' ' && c <= '?')
if(c >= ' ' && c <= '?')
bytes.get()[i] = c - ' ' + 160;
else if(c > '@' && c <= '_')
else if(c >= '@' && c <= '_')
bytes.get()[i] = c - '@' + 128;
else if(c > '`' && c <= '~')
else if(c >= '`' && c <= '~')
bytes.get()[i] = c - '`' + 224;
else
bytes.get()[i] = 255;
@ -1024,7 +1069,79 @@ void initialize_gl(void)
CheckOpenGL(__FILE__, __LINE__);
}
void initialize_widgets(bool run_fast, bool add_floppies)
struct floppy_icon : public widget
{
int number;
bool inserted;
bool active;
switcher *switched;
floppy_icon(int number_, bool inserted_) :
number(number_),
inserted(inserted_),
active(false)
{
widget *disk_out = new padding(3, 3, 3, 3, new centering(new text_widget(" DROP DSK " + to_string(number + 1) + " HERE ")));
widget *disk_in = new padding(3, 3, 3, 3, new centering(new text_widget("DRIVE " + to_string(number + 1) + " OK ")));
widget *disk_in_active = new padding(3, 3, 3, 3, new centering(new text_widget("DRIVE " + to_string(number + 1) + " OK*")));
switched = new switcher({disk_out, disk_in, disk_in_active});
switched->which = inserted_ ? 1 : 0;
}
virtual tuple<float, float> get_min_dimensions() const
{
return switched->get_min_dimensions();
}
virtual void draw(double now, float to_screen[9], float x, float y, float w, float h)
{
switched->draw(now, to_screen, x, y, w, h);
}
virtual bool click(double now, float x, float y)
{
float w, h;
tie(w, h) = get_min_dimensions();
if(x >= 0 && y >= 0 & x < w && y < h)
return true;
return false;
}
virtual void hover(double now, float x, float y)
{
}
virtual void drag(double now, float x, float y)
{
}
virtual void release(double now, float x, float y)
{
// eject
if(inserted)
event_queue.push_back({EJECT_FLOPPY, number});
switched->which = 0;
}
virtual bool drop(double now, float x, float y, int count, const char** paths)
{
// insert
float w, h;
tie(w, h) = get_min_dimensions();
if(x >= 0 && y >= 0 & x < w && y < h) {
event_queue.push_back({INSERT_FLOPPY, number, strdup(paths[0])});
switched->which = 1;
return true;
}
return false;
}
void change_state(double now, bool inserted_, bool active_)
{
switched->which = inserted_ ? (active_ ? 2 : 1) : 0;
active = active_;
inserted = inserted_;
}
};
floppy_icon *floppy0_icon;
floppy_icon *floppy1_icon;
void initialize_widgets(bool run_fast, bool add_floppies, bool floppy0_inserted, bool floppy1_inserted)
{
momentary *reset_momentary = new momentary("RESET", [](){event_queue.push_back({RESET, 0});});
momentary *reboot_momentary = new momentary("REBOOT", [](){event_queue.push_back({REBOOT, 0});});
@ -1033,19 +1150,36 @@ void initialize_widgets(bool run_fast, bool add_floppies)
toggle *color_toggle = new toggle("COLOR", false, [](){draw_using_color = true;}, [](){draw_using_color = false;});
toggle *pause_toggle = new toggle("PAUSE", false, [](){event_queue.push_back({PAUSE, 1});}, [](){event_queue.push_back({PAUSE, 0});});
vector<widget*> buttons = {reset_momentary, reboot_momentary, fast_toggle, caps_toggle, color_toggle, pause_toggle};
vector<widget*> buttons_centered;
for(auto b : buttons)
buttons_centered.push_back(new centering(b));
vector<widget*> controls = {reset_momentary, reboot_momentary, fast_toggle, caps_toggle, color_toggle, pause_toggle};
if(add_floppies) {
floppy0_icon = new floppy_icon(0, floppy0_inserted);
floppy1_icon = new floppy_icon(1, floppy1_inserted);
controls.push_back(floppy0_icon);
controls.push_back(floppy1_icon);
}
vector<widget*> controls_centered;
for(auto b : controls)
controls_centered.push_back(new centering(b));
widget *screen = new apple2screen();
widget *padding = new spacer(20, 0);
widget *buttonpanel = new centering(new widgetbox(widgetbox::VERTICAL, buttons_centered));
widget *padding = new spacer(10, 0);
widget *buttonpanel = new centering(new widgetbox(widgetbox::VERTICAL, controls_centered));
vector<widget*> panels_centered = {new centering(screen), padding, new centering(buttonpanel)};
ui = new centering(new widgetbox(widgetbox::HORIZONTAL, panels_centered));
}
void show_floppy_activity(int number, bool activity)
{
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
chrono::duration<double> elapsed = now - start_time;
if(number == 0)
floppy0_icon->change_state(elapsed.count(), 1, activity);
else if(number == 1)
floppy1_icon->change_state(elapsed.count(), 1, activity);
}
const float widget_scale = 4;
float to_screen_transform[9];
@ -1073,12 +1207,11 @@ tuple<float, float> window_to_widget(float x, float y)
static void redraw(GLFWwindow *window)
{
chrono::time_point<chrono::system_clock> now;
now = std::chrono::system_clock::now();
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
chrono::duration<double> elapsed = now - start_time;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
chrono::duration<double> elapsed = now - start_time;
ui->draw(elapsed.count(), to_screen_transform, 0, 0, gWindowWidth / widget_scale, gWindowHeight / widget_scale);
@ -1142,6 +1275,8 @@ static void button(GLFWwindow *window, int b, int action, int mods)
{
double x, y;
glfwGetCursorPos(window, &x, &y);
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
chrono::duration<double> elapsed = now - start_time;
if(b == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
gButtonPressed = 1;
@ -1150,7 +1285,7 @@ static void button(GLFWwindow *window, int b, int action, int mods)
float wx, wy;
tie(wx, wy) = window_to_widget(x, y);
if(ui->click(0.0, wx, wy)) {
if(ui->click(elapsed.count(), wx, wy)) {
widget_clicked = ui;
}
} else {
@ -1158,7 +1293,7 @@ static void button(GLFWwindow *window, int b, int action, int mods)
if(widget_clicked) {
float wx, wy;
tie(wx, wy) = window_to_widget(x, y);
widget_clicked->release(0.0, wx, wy);
widget_clicked->release(elapsed.count(), wx, wy);
}
widget_clicked = nullptr;
}
@ -1175,6 +1310,8 @@ static void motion(GLFWwindow *window, double x, double y)
gOldMouseX = x;
gOldMouseY = y;
}
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
chrono::duration<double> elapsed = now - start_time;
double dx, dy;
@ -1189,10 +1326,10 @@ static void motion(GLFWwindow *window, double x, double y)
if(gButtonPressed == 1) {
if(widget_clicked) {
widget_clicked->drag(0.0, wx, wy);
widget_clicked->drag(elapsed.count(), wx, wy);
}
} else {
ui->hover(0.0, wx, wy);
ui->hover(elapsed.count(), wx, wy);
}
redraw(window);
}
@ -1220,7 +1357,18 @@ void load_joystick_setup()
fclose(fp);
}
void start(bool run_fast, bool add_floppies)
void drop_callback(GLFWwindow* window, int count, const char** paths)
{
double x, y;
glfwGetCursorPos(window, &x, &y);
float wx, wy;
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
chrono::duration<double> elapsed = now - start_time;
tie(wx, wy) = window_to_widget(x, y);
ui->drop(elapsed.count(), wx, wy, count, paths);
}
void start(bool run_fast, bool add_floppies, bool floppy0_inserted, bool floppy1_inserted)
{
load_joystick_setup();
@ -1251,7 +1399,7 @@ void start(bool run_fast, bool add_floppies)
glViewport(0, 0, gWindowWidth, gWindowHeight);
make_to_screen_transform();
initialize_gl();
initialize_widgets(run_fast, add_floppies);
initialize_widgets(run_fast, add_floppies, floppy0_inserted, floppy1_inserted);
CheckOpenGL(__FILE__, __LINE__);
glfwSetKeyCallback(my_window, key);
@ -1260,6 +1408,7 @@ void start(bool run_fast, bool add_floppies)
glfwSetScrollCallback(my_window, scroll);
glfwSetFramebufferSizeCallback(my_window, resize);
glfwSetWindowRefreshCallback(my_window, redraw);
glfwSetDropCallback(my_window, drop_callback);
CheckOpenGL(__FILE__, __LINE__);
}

View File

@ -2,7 +2,7 @@
namespace APPLE2Einterface
{
enum EventType {NONE, KEYDOWN, KEYUP, RESET, REBOOT, PASTE, SPEED, QUIT, PAUSE};
enum EventType {NONE, KEYDOWN, KEYUP, RESET, REBOOT, PASTE, SPEED, QUIT, PAUSE, EJECT_FLOPPY, INSERT_FLOPPY};
const int LEFT_SHIFT = 340;
const int LEFT_CONTROL = 341;
@ -39,7 +39,7 @@ struct event {
{}
};
void start(bool run_fast, bool add_floppies);
void start(bool run_fast, bool add_floppies, bool floppy0_inserted, bool floppy1_inserted);
void iterate(); // display
void shutdown();
@ -52,4 +52,6 @@ bool write(int addr, unsigned char data);
std::tuple<float,bool> get_paddle(int num);
void show_floppy_activity(int number, bool activity);
};