gui/gui_init [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

      gui_init

SYNOPSIS

static gui_t*
gui_init(
                gui_t* gui, GtkApplication* app,
                int* pargc, char** pargv[],
                gint screen_width, gint screen_height
                )

FUNCTION

Initialize the GUI module.

Start by parsing command-line arguments that are intended for it. Assign default values to various members of the supplied gui_t structure. Calculates the initial base address for the virtual video frame buffer.

Currently, this function also initializes the resources found in the virtual machine environment (e.g., it places ROM at address 0, RAM at high memory addresses, etc.). This is a matter of convenience; future versions of this module may isolate this behavior into a separate module.

INPUTS

      gui -- pointer to the gui_t to initialize.
      pargc, pargv -- Pointers to argc and argv, so GTK look for CLI
              arguments unique to it.
      screen_width, screen_height -- Default display resolution for the
              Kestrel-2 environment.

 RESULT
      If successful, it will return the gui_t that was supplied;
      otherwise, NULL will be returned.

SOURCE

{
        if (gui) {
                memset(gui, 0xCC, sizeof(gui_t));

                gtk_init(pargc, pargv);

                gui->screen_width = screen_width;
                gui->screen_height = screen_height;

                gui->app = app;

                fprintf(stderr, "K2INI001 screen dimensions (%d x %d) 1bpp\n",
                                gui->screen_width, gui->screen_height
                                );

                // Calculate base address of display framebuffer;
                // by default, it is placed at the very top of RAM.
                //
                // Round up to the nearest 16-bit word, since that's
                // what the 2EX's RAM bus width is.
                const int32_t hwords_per_row = (screen_width + 15) >> 4;
                const int32_t hwords_per_screen =
                        hwords_per_row * screen_height;
                const int32_t bytes_per_screen = hwords_per_screen << 1;
                gui->screen_base = (uint32_t)(-bytes_per_screen);
                fprintf(stderr, "K2INI002 screen_base = $%08X\n",
                                gui->screen_base
                                );

                gui_init_layout(gui, app);

                // Map out the virtual memory resources
                slots_init(&gui->slots);

        }

        return gui;
}

gui/gui_init_layout [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

      gui_init_layout

SYNOPSIS

static void
gui_init_layout(gui_t* gui, GtkApplication* app)

FUNCTION

Creates the GUI layout; it computes where things appear in the desktop window.

INPUTS

      gui -- Pointer to the application's gui_t structure.

SEE ALSO

      gui_init

SOURCE

{
        // Build main window

        GtkWindow* window = gui->window = (GtkWindow*)
                gtk_application_window_new(app);

        gtk_window_set_title(
                        GTK_WINDOW(window),
                        "Kestrel-2EX Emulator V" VERSION
                        );

        gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

        g_signal_connect_swapped(
                        G_OBJECT(window), "destroy",
                        G_CALLBACK(gtk_widget_destroy), (void*)window
                        );

        // Put a VBox inside the main window

        GtkWidget* vbox = gui->vbox = (GtkWidget*)gtk_box_new(
                        GTK_ORIENTATION_VERTICAL, 0
                        );

        gtk_container_add(GTK_CONTAINER(window), vbox);

        // Build the toolbar

        GtkToolbar* toolbar = gui->toolbar = (GtkToolbar*)gtk_toolbar_new();
        
        gui->tool_quit = gtk_tool_button_new(NULL, "Quit");
        GtkToolItem* sep = (GtkToolItem*)gtk_separator_tool_item_new();

        gtk_toolbar_insert(toolbar, gui->tool_quit, -1);
        gtk_toolbar_insert(toolbar, sep, -1);

        gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(toolbar), FALSE, FALSE, 0);

        g_signal_connect_swapped(
                        G_OBJECT(gui->tool_quit), "clicked",
                        G_CALLBACK(gtk_widget_destroy), (void*)window
                        );

        // Attach the emulator's screen output drawing area widget

        GtkDrawingArea* screen = gui->screen = (GtkDrawingArea*)
                gtk_drawing_area_new();
        gtk_widget_set_size_request(
                        GTK_WIDGET(screen),
                        gui->screen_width, gui->screen_height
                        );
        gtk_widget_set_can_focus(GTK_WIDGET(screen), TRUE);
        gtk_widget_add_events(GTK_WIDGET(screen),
                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | 
                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
        gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(screen), FALSE, FALSE, 0);
        g_signal_connect(
                        G_OBJECT(screen), "key-press-event",
                        G_CALLBACK(on_screen_key_down), (void*)gui
                        );
        g_signal_connect(
                        G_OBJECT(screen), "key-release-event",
                        G_CALLBACK(on_screen_key_up), (void*)gui
                        );
        g_signal_connect(
                        G_OBJECT(screen), "draw",
                        G_CALLBACK(on_screen_draw), (void*)gui
                        );
        g_signal_connect(
                        G_OBJECT(screen), "button-press-event",
                        G_CALLBACK(on_screen_button_pressed), (void*)gui
                        );
        gtk_widget_grab_focus(GTK_WIDGET(screen));

        gtk_widget_show_all(GTK_WIDGET(window));
        gtk_window_present(GTK_WINDOW(gui->window));
}

gui/gui_refresh_screen [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

      gui_refresh_screen

SYNOPSIS

void
gui_refresh_screen(gui_t* gui)

FUNCTION

Causes the emulator's window to redraw, eventually causing on_screen_draw to be called.

INPUTS

      gui     Pointer to a gui_t structure.
 
 RESULT

SEE ALSO

      on_screen_draw

SOURCE

{
        gtk_widget_queue_draw(GTK_WIDGET(gui->screen));
}

gui/gui_terminate [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

      gui_terminate

SYNOPSIS

void
gui_terminate(gui_t* gui)

FUNCTION

Cleans up all the resources acquired during the processing in gui_init().

This function is expected to ALWAYS succeed, even if initialization is incomplete.

INPUTS

      gui -- pointer to the gui_t for the application.

 RESULT

SOURCE

{
        slots_terminate(&gui->slots);
        // GTK uses atexit() to automatically clean itself up.
}

gui/gui_terminate_emulator [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

      gui_terminate_emulator

SYNOPSIS

void
gui_terminate_emulator(int rc)

FUNCTION

Requests the emulator to shutdown cleanly, returning result code rc to the OS that launched it.

NOTE Not to be confused with gui_terminate()!

INPUTS

      rc      Result code; typically 0 indicates success, while values
              between 1..127 inclusive indicate some kind of error.
              Values 128 and above may be supported as well, depending on
              host OS.

 RESULT

SEE ALSO

      gui_terminate

SOURCE

{
        g_result_code = rc;
        g_application_quit(G_APPLICATION(g_gui->app));
}

gui/on_screen_button_pressed [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

   on_screen_button_pressed

SYNOPSIS

static gboolean
on_screen_button_pressed(GtkWidget* widget, GdkEventButton* e, gpointer data)

FUNCTION

Handle mouse button presses in the virtual screen area.

INPUTS

   widget -- set by GTK to the GtkDrawingArea containing the video output.
   e -- set by GTK to the GdkEventButton describing the specific event.
   data -- A gpointer back to the gui_t for the application.

 RESULT
   Answers whether or not the event has been "handled".  Return FALSE
   to allow other GTK event handlers to run; or TRUE to denote all further
   processing is unnecessary.

   This procedure will always return FALSE.

SEE ALSO

   on_screen_button_released

SOURCE

{
        gui_t* gui = (gui_t*)data;
        (void)gui;
        (void)e;

        gtk_widget_grab_focus(widget);

        return FALSE;
}

gui/on_screen_button_released [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

   on_screen_button_released

SYNOPSIS

static gboolean
on_screen_button_released(GtkWidget* widget, GdkEventButton* e, gpointer data)

FUNCTION

Handle mouse button releases in the virtual screen area.

INPUTS

   widget -- set by GTK to the GtkDrawingArea containing the video output.
   e -- set by GTK to the GdkEventButton describing the specific event.
   data -- A gpointer back to the gui_t for the application.

 RESULT
   Answers whether or not the event has been "handled".  Return FALSE
   to allow other GTK event handlers to run; or TRUE to denote all further
   processing is unnecessary.

   This procedure will always return FALSE.

SEE ALSO

   on_screen_button_pressed

SOURCE

{
        gui_t* gui = (gui_t*)data;
        (void)gui;
        (void)e;

        return FALSE;
}

gui/on_screen_draw [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

   on_screen_draw

SYNOPSIS

static gboolean
on_screen_draw(GtkWidget* widget, cairo_t* cr, gpointer data)

FUNCTION

Redraw the Kestrel 2's MGIA (or, in later models, CGIA) video output.

INPUTS

   widget -- set by GTK to the GtkDrawingArea containing the video output.
   cr -- set by GTK to the Cairo drawing context.
   data -- A gpointer back to the gui_t for the application.

 RESULT
   Answers whether or not the event has been "handled".  Return FALSE
   to allow other GTK event handlers to run; or TRUE to denote all further
   processing is unnecessary.

   This procedure will always return FALSE.

SEE ALSO

   gui_init

SOURCE

{
        // Get our own widget dimensions

        gui_t* gui = (gui_t*)data;
        gint width = gtk_widget_get_allocated_width(widget);
        gint height = gtk_widget_get_allocated_height(widget);

        // Draw monitor "bezel", if the widget is large enough to show it.
        
        if (gui->screen_width < width) {
                cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
                cairo_rectangle(cr, 0, 0, width, height);
                cairo_fill(cr);
        }

        // Zero pixels are, by default, black.
        
        cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
        cairo_rectangle(cr, 0, 0, gui->screen_width, gui->screen_height);
        cairo_fill(cr);

        // Draw all the white pixels.

        cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);

        bool error = false;
        uint8_t* bitmap_data;
        slots_translate_address_from_virtual(
                        &gui->slots,
                        gui->screen_base, &bitmap_data,
                        &error
                        );
        if (!error) {
                const int stride = cairo_format_stride_for_width(
                                CAIRO_FORMAT_A1, gui->screen_width
                                );
                cairo_surface_t* bitmap_surface =
                        cairo_image_surface_create_for_data(
                                bitmap_data,
                                CAIRO_FORMAT_A1,
                                gui->screen_width,
                                gui->screen_height,
                                stride
                                );
                cairo_mask_surface(cr, bitmap_surface, 0, 0);
                cairo_surface_destroy(bitmap_surface);
        }
        else {
                fprintf(stderr,
                        "K2GUI001 Attempt to refresh display from "
                        "something not RAM: $%08X\n", gui->screen_base
                        );
        }

        return FALSE;
}

gui/on_screen_key_down [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

   on_screen_key_down

SYNOPSIS

static bool
on_screen_key_down(GtkEventControllerKey *controller, GdkEventKey* event, gpointer data)

FUNCTION

Handle keyboard button presses in the virtual screen area.

INPUTS

   controller -- set by GTK to the GtkDrawingArea's keyboard controller.
   event -- set by GTK to the GdkEventKey describing the specific event.
   data -- A gpointer back to the gui_t for the application.

 RESULT
   Answers whether or not the event has been "handled".  Return FALSE
   to allow other GTK event handlers to run; or TRUE to denote all further
   processing is unnecessary.

SEE ALSO

   on_screen_key_up

SOURCE

{
        return rose_event_key_down((gui_t*)data, event->keyval);
}

gui/on_screen_key_up [ Functions ]

[ Top ] [ gui ] [ Functions ]

NAME

   on_screen_key_up

SYNOPSIS

static bool
on_screen_key_up(GtkEventControllerKey *controller, GdkEventKey* event, gpointer data)

FUNCTION

Handle keyboard button releases in the virtual screen area.

INPUTS

   controller -- set by GTK to the GtkDrawingArea's keyboard controller.
   event -- set by GTK to the GdkEventKey describing the specific event.
   data -- A gpointer back to the gui_t for the application.

 RESULT
   Answers whether or not the event has been "handled".  Return FALSE
   to allow other GTK event handlers to run; or TRUE to denote all further
   processing is unnecessary.

SEE ALSO

   on_screen_key_down

SOURCE

{
        return rose_event_key_up((gui_t*)data, event->keyval);
}