dmd_gtk/src/dmd.c

304 lines
7.6 KiB
C

#include <gtk/gtk.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdint.h>
#include <pthread.h>
static cairo_surface_t *surface = NULL;
static pthread_t dmd_thread;
uint8_t last_kb_char;
uint8_t kb_pending = 0;
volatile int dmd_thread_run = 1;
extern int dmd_init();
extern int dmd_reset();
extern uint8_t *dmd_video_ram();
extern int dmd_step();
extern uint32_t dmd_get_pc();
extern uint8_t dmd_get_duart_output_port();
extern int dmd_rx_char(uint8_t c);
extern int dmd_rx_keyboard(uint8_t c);
extern int dmd_mouse_move(uint16_t x, uint16_t y);
static void
clear_surface()
{
cairo_t *cr;
cr = cairo_create(surface);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_destroy(cr);
}
static gboolean
configure_event_cb(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
printf("[configure_event_cb]\n");
if (surface) {
cairo_surface_destroy(surface);
}
surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget),
CAIRO_CONTENT_COLOR,
gtk_widget_get_allocated_width(widget),
gtk_widget_get_allocated_height(widget));
clear_surface();
return TRUE;
}
static void
close_window(void)
{
printf("[close_window]\n");
dmd_thread_run = 0;
}
static gboolean
draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
return FALSE;
}
long get_current_time_ms()
{
long ms;
time_t s;
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
s = spec.tv_sec;
ms = round(spec.tv_nsec / 1.0e6);
return (s * 1000) + ms;
}
static gboolean
refresh_display(gpointer data)
{
GtkWidget *widget = (GtkWidget *)data;
if (widget == NULL || !GTK_IS_WIDGET(widget)) {
return FALSE;
}
GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
TRUE,
8,
800,
1024);
int rowstride = gdk_pixbuf_get_rowstride(pixbuf);
guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
int n_channels = gdk_pixbuf_get_n_channels(pixbuf);
guchar *p = pixels;
uint32_t p_index = 0;
uint8_t *vram = dmd_video_ram();
if (vram == NULL) {
printf("[WARNING] Video Ram is NULL!");
} else {
for (int y = 0; y < 1024; y++) {
for (int x = 0; x < 100; x++) {
// Get the byte
uint8_t b = vram[y * 100 + x];
for (int i = 0; i < 8; i++) {
uint8_t bit = (b >> (7-i)) & 1;
if (bit) {
p[p_index++] = 0;
p[p_index++] = 0xff;
p[p_index++] = 0;
p[p_index++] = 0xff;
} else {
p[p_index++] = 0;
p[p_index++] = 0;
p[p_index++] = 0;
p[p_index++] = 0xff;
}
}
}
}
}
cairo_t *cr;
cr = cairo_create(surface);
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
cairo_paint(cr);
cairo_fill(cr);
cairo_destroy(cr);
gtk_widget_queue_draw(widget);
return TRUE;
}
static gboolean
mouse_moved(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
dmd_mouse_move((uint16_t) event->x, (uint16_t) (1024 - event->y));
return TRUE;
}
static gboolean
button_press_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
return TRUE;
}
void *dmd_run(void *threadid)
{
long double steps = 0;
int rs;
struct timespec sleep_time_req, sleep_time_rem;
sleep_time_req.tv_sec = 0;
sleep_time_req.tv_nsec = 10000000;
printf("[DMD thread starting]");
dmd_init();
dmd_reset();
while (dmd_thread_run) {
dmd_step();
// Stop every once in a while to poll for I/O and idle.
if (steps++ == 250000) {
if (kb_pending && dmd_rx_keyboard(last_kb_char)) {
printf("[DMD thread] Sent char 0x%02x\n", last_kb_char);
kb_pending = 0;
}
steps = 0;
rs = nanosleep(&sleep_time_req, &sleep_time_rem);
if (rs) {
printf("[DMD thread] SLEEP FAILED. Result=%d\n", rs);
break;
}
}
}
printf("[DMD thread exiting]\n");
pthread_exit(NULL);
}
static gboolean
keydown_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
last_kb_char = 0xae;
kb_pending = 1;
return TRUE;
}
/* Called on startup as a callback */
static void
activate(GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
GtkWidget *frame;
GtkWidget *drawing_area;
printf("[activate]\n");
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "AT&T DMD 5620");
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
g_signal_connect(window, "destroy", G_CALLBACK(close_window), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(window), frame);
drawing_area = gtk_drawing_area_new();
gtk_widget_set_size_request(drawing_area, 800, 1024);
gtk_container_add(GTK_CONTAINER(frame), drawing_area);
g_timeout_add(20, refresh_display, drawing_area);
/* Signals used to handle the backing surface */
g_signal_connect(drawing_area, "draw",
G_CALLBACK(draw_cb), NULL);
g_signal_connect(drawing_area, "configure-event",
G_CALLBACK(configure_event_cb), NULL);
/* UI signals */
g_signal_connect(drawing_area, "button-press-event",
G_CALLBACK(button_press_event_cb), NULL);
g_signal_connect(G_OBJECT(window), "key_press_event",
G_CALLBACK(keydown_event), NULL);
g_signal_connect(drawing_area, "motion-notify-event",
G_CALLBACK(mouse_moved), NULL);
gtk_widget_set_events(drawing_area,
gtk_widget_get_events(drawing_area)
| GDK_BUTTON_PRESS_MASK
| GDK_KEY_PRESS_MASK
| GDK_POINTER_MOTION_MASK);
gtk_widget_show_all(window);
/* Hide the cursor */
/* GdkWindow *gdk_window = gtk_widget_get_window(window); */
/* GdkDisplay *gdk_display = gdk_display_get_default(); */
/* gdk_window_set_cursor(gdk_window, gdk_cursor_new_for_display(gdk_display, GDK_BLANK_CURSOR)); */
}
int
main(int argc, char *argv[])
{
GtkApplication *app;
int status;
long thread_id;
int rc;
rc = pthread_create(&dmd_thread, NULL, dmd_run, (void *)thread_id);
if (rc) {
printf("ERROR: Could not create main DMD cpu thread. Status=%d\n", rc);
exit(-1);
}
app = gtk_application_new("com.loomcom.dmd", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
void *join_status;
rc = pthread_join(dmd_thread, &join_status);
if (rc) {
printf("ERROR: Could not join thread. Status=%d\n", rc);
exit(-1);
}
printf("Main: DMD thread is done.\n");
/* if (surface) { */
/* cairo_surface_destroy(surface); */
/* } */
g_object_unref(app);
return status;
}