mirror of
https://github.com/rricharz/Tek4010.git
synced 2026-01-13 07:19:59 +00:00
Version 1.8 with improved handling of bright drawing spot and fading released for all systems
This commit is contained in:
parent
51ac8ac44d
commit
9ae8b458cf
BIN
Manual.odt
BIN
Manual.odt
Binary file not shown.
BIN
Manual.pdf
BIN
Manual.pdf
Binary file not shown.
20
README.md
20
README.md
@ -32,10 +32,22 @@ Dave Ault using tek4010.
|
||||
|
||||

|
||||
|
||||
All instructions can now be found in [Manual.pdf](https://github.com/rricharz/Tek4010/blob/master/Manual.pdf)
|
||||
All instructions can be found in [Manual.pdf](https://github.com/rricharz/Tek4010/blob/master/Manual.pdf)
|
||||
in the main Tek4010 directory.
|
||||
|
||||
**Version 1.8 for the Raspberry Pi 5 includes a somewhat improved handling of the fading of the bright
|
||||
drawing spot at the expense of requiring much more computer power. See chapter 14 of the manual on how
|
||||
to switch to it. The standard version is 1.7, which works on all Raspberry Pi models.**
|
||||
The storage tube emulator and the Tektronix 4010/4014 decoder were witten by Rene Richarz. The ARDS
|
||||
decoder was written by Lars Brinkhoff. He also provided some interesting historical documents and
|
||||
the ARDS plot files. Teunis van Beelen has written the helper program “rs232-console” to connect
|
||||
to a host using a serial link. Dave Ault tested the serial link to his PDP-11/73. The historical
|
||||
plot data for the Tektronix 4014 was obtained from Jos Dreesen. The special plot mode pictures
|
||||
with variable brightness were obtained from Monty McGraw. He also helped to debug the special
|
||||
plot point mode. The historical plot data of the spacelab from the ICEMDDN CAD package on a CDC
|
||||
Cyber 175 mainframe emulator was obtained from Nick Glazzard. He also helped to improve the GIN
|
||||
mode substantially. The CP/M GSXBASIC plot files are from Udo Munk. Thanks to Ian Schofield for
|
||||
his critical comments and a code snippet for drawing dashed and dotted lines, and to Oscar Vermeulen
|
||||
and Mark Matlock for their support. The manuals were obtained from bitsavers.org. Thanks also to all
|
||||
others who contributed important ideas, helped with the debugging and preserved the historical data.
|
||||
This program is the result of a community effort.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
240
RPi5/ards.c
240
RPi5/ards.c
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* ards.c
|
||||
*
|
||||
* ARDS option for tek4010 graphics emulator
|
||||
*
|
||||
* Copyright 2019 Lars Brinkhoff
|
||||
*
|
||||
* https://github.com/rricharz/Tek4010
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "tube.h"
|
||||
|
||||
static long startPaintTime;
|
||||
|
||||
// mode handles the current state of the emulator:
|
||||
//
|
||||
// mode 0 symbol mode
|
||||
// mode 1 set point mode
|
||||
// mode 2 extended vector mode
|
||||
// mode 3 short vector mode
|
||||
|
||||
static int mode;
|
||||
|
||||
static double efactor;
|
||||
|
||||
static int args;
|
||||
static unsigned char data[4];
|
||||
|
||||
static int x0 = -485, y0 = 450, x2, y2;
|
||||
|
||||
static void draw_char (cairo_t *cr, cairo_t *cr2, char ch)
|
||||
{
|
||||
tube_x0 = (int)(efactor * (x0 + 540));
|
||||
tube_y0 = (int)(efactor * (y0 + 707));
|
||||
//fprintf (stderr, "[tube: %d,%d]", tube_x0, tube_y0);
|
||||
tube_drawCharacter(cr, cr2, ch);
|
||||
x0 += (int)(hDotsPerChar / efactor);
|
||||
}
|
||||
|
||||
static void draw_vector (cairo_t *cr, cairo_t *cr2)
|
||||
{
|
||||
tube_x0 = (int)(efactor * (x0 + 540));
|
||||
tube_y0 = (int)(efactor * (y0 + 707));
|
||||
tube_x2 = (int)(efactor * (x2 + 540));
|
||||
tube_y2 = (int)(efactor * (y2 + 707));
|
||||
//fprintf (stderr, "[tube: %d,%d - %d,%d]", tube_x0, tube_y0, tube_x2, tube_y2);
|
||||
tube_drawVector(cr, cr2);
|
||||
}
|
||||
|
||||
void ards_draw(cairo_t *cr, cairo_t *cr2, int first)
|
||||
// draw onto the main window using cairo
|
||||
// cr is used for persistent drawing, cr2 for temporary drawing
|
||||
|
||||
{
|
||||
int ch;
|
||||
|
||||
refreshCount++; // to calculate the average refresh rate
|
||||
|
||||
if (first) {
|
||||
first = 0;
|
||||
efactor = windowWidth / 1080.0;
|
||||
// fprintf (stderr, "efactor: %0.2f\n", efactor);
|
||||
refresh_interval = 30;
|
||||
tube_changeCharacterSize(cr, cr2, 80, 50, efactor * 1.1);
|
||||
|
||||
}
|
||||
|
||||
startPaintTime = tube_mSeconds(); // start to measure time for this draw operation
|
||||
|
||||
showCursor = 1;
|
||||
isBrightSpot = 0;
|
||||
|
||||
// clear the second surface
|
||||
tube_clearSecond(cr2);
|
||||
|
||||
// clear persistent surface, if necessary
|
||||
if (tube_doClearPersistent) {
|
||||
tube_clearPersistent(cr,cr2);
|
||||
}
|
||||
|
||||
tube_setupPainting(cr, cr2, STANDARD_FONT);
|
||||
|
||||
do {
|
||||
ch = tube_getInputChar();
|
||||
|
||||
if (tube_isInput() == 0) {
|
||||
}
|
||||
|
||||
if (ch == -1) {
|
||||
return; // no char available, need to allow for updates
|
||||
}
|
||||
|
||||
//fprintf (stderr, "\n[INPUT %03o [%d/%d]]", ch, mode, args);
|
||||
|
||||
if (args > 0) {
|
||||
data[--args] = ch;
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case 007:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
break;
|
||||
case 010:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
x0 -= hDotsPerChar;
|
||||
break;
|
||||
case 012:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
y0 -= vDotsPerChar;
|
||||
break;
|
||||
case 014:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
x0 = -485;
|
||||
y0 = 450;
|
||||
tube_clearPersistent(cr, cr2);
|
||||
break;
|
||||
case 015:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
x0 = -479;
|
||||
break;
|
||||
case 034:
|
||||
mode = 0;
|
||||
args = 0;
|
||||
break;
|
||||
case 035:
|
||||
//fprintf (stderr, "[POINT]");
|
||||
mode = 1;
|
||||
args = 4;
|
||||
break;
|
||||
case 036:
|
||||
//fprintf (stderr, "[E VEC]");
|
||||
mode = 2;
|
||||
args = 4;
|
||||
break;
|
||||
case 037:
|
||||
//fprintf (stderr, "[S VEC]");
|
||||
mode = 3;
|
||||
args = 2;
|
||||
break;
|
||||
default:
|
||||
if (args == 0) {
|
||||
switch (mode) {
|
||||
case 0:
|
||||
//fprintf (stderr, "[SYMBOL %c @ %d, %d]", ch, x0, y0);
|
||||
draw_char (cr, cr2, ch);
|
||||
break;
|
||||
case 1:
|
||||
args = 4;
|
||||
x0 = (data[3] & 076) >> 1;
|
||||
x0 |= (data[2] & 037) << 5;
|
||||
if (data[3] & 1)
|
||||
x0 = -x0;
|
||||
y0 = (data[1] & 076) >> 1;
|
||||
y0 |= (data[0] & 037) << 5;
|
||||
if (data[1] & 1)
|
||||
y0 = -y0;
|
||||
//fprintf (stderr, "[POINT @ %d, %d]", x0, y0);
|
||||
break;
|
||||
case 2:
|
||||
args = 4;
|
||||
x2 = (data[3] & 076) >> 1;
|
||||
x2 |= (data[2] & 037) << 5;
|
||||
if (data[3] & 1)
|
||||
x2 = -x2;
|
||||
x2 += x0;
|
||||
y2 = (data[1] & 076) >> 1;
|
||||
y2 |= (data[0] & 037) << 5;
|
||||
if (data[1] & 1)
|
||||
y2 = -y2;
|
||||
y2 += y0;
|
||||
//fprintf (stderr, "[E VEC @ %d,%d - %d,%d]", x0, y0, x2, y2);
|
||||
if (data[2] & 040)
|
||||
; //fprintf (stderr, "[INVISIBLE]");
|
||||
else
|
||||
draw_vector(cr, cr2);
|
||||
if (data[0] & 040)
|
||||
; //fprintf (stderr, "[DOTTED]");
|
||||
x0 = x2;
|
||||
y0 = y2;
|
||||
break;
|
||||
case 3:
|
||||
args = 2;
|
||||
x2 = (data[1] & 076) >> 1;
|
||||
if (data[1] & 1)
|
||||
x2 = -x2;
|
||||
x2 += x0;
|
||||
y2 = (data[0] & 076) >> 1;
|
||||
if (data[0] & 1)
|
||||
y2 = -y2;
|
||||
y2 += y0;
|
||||
draw_vector(cr, cr2);
|
||||
//fprintf (stderr, "[S VEC @ %d,%d - %d,%d]", x0, y0, x2, y2);
|
||||
x0 = x2;
|
||||
y0 = y2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (((tube_mSeconds() - startPaintTime) < refresh_interval));
|
||||
|
||||
// display cursor
|
||||
|
||||
if (showCursor && (tube_isInput() == 0)) tube_doCursor(cr2);
|
||||
|
||||
}
|
||||
11
RPi5/install
11
RPi5/install
@ -1,11 +0,0 @@
|
||||
# install tek4010 in ~/bin
|
||||
|
||||
# create ~/bin directory, if it does not exist
|
||||
[ ! -d ~/bin ] && mkdir ~/bin
|
||||
|
||||
# copy the file to ~/bin, make it executable
|
||||
cp ../tek4010 ~/bin
|
||||
chmod +x ~/bin/tek4010
|
||||
|
||||
echo "tek4010 version 1.8 for Raspberry Pi 5 installed."
|
||||
echo "Reboot if this is the first time tek4010 is installed"
|
||||
332
RPi5/main.c
332
RPi5/main.c
@ -1,332 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* main.c
|
||||
* provides a simple framework for basic drawing with cairo and gtk+ 3.0
|
||||
*
|
||||
* Version adapted for tek4010
|
||||
*
|
||||
* Copyright 2016,2019 rricharz
|
||||
*
|
||||
* https://github.com/rricharz/Tek4010
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define TIME_INTERVAL 35 // time interval for timer function in msec (after last refresh)
|
||||
|
||||
#define GDK_DISABLE_DEPRECATION_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "tube.h"
|
||||
|
||||
extern FILE *putKeys;
|
||||
|
||||
char *windowName;
|
||||
|
||||
static int global_firstcall;
|
||||
|
||||
extern int argFull;
|
||||
extern int argFullV;
|
||||
extern int argARDS;
|
||||
|
||||
static GtkWidget *window;
|
||||
int windowWidth;
|
||||
int windowHeight;
|
||||
static double aspectRatio;
|
||||
static int windowHeightOffset = 0;
|
||||
static int windowWidthOffset = 0;
|
||||
|
||||
guint global_timeout_ref;
|
||||
|
||||
extern int tube_doClearPersistent;
|
||||
|
||||
static void do_drawing(cairo_t *, GtkWidget *);
|
||||
|
||||
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
|
||||
gpointer user_data)
|
||||
{
|
||||
do_drawing(cr, widget);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean on_timer_event(GtkWidget *widget)
|
||||
{
|
||||
if (tube_on_timer_event())
|
||||
gtk_widget_queue_draw(widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
||||
{
|
||||
if (tube_clicked(event->button, event->x - windowWidthOffset, event->y - windowHeightOffset))
|
||||
gtk_widget_queue_draw(widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void on_quit_event()
|
||||
{
|
||||
tube_quit();
|
||||
gtk_main_quit();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void do_drawing(cairo_t *cr, GtkWidget *widget)
|
||||
{
|
||||
static cairo_surface_t *permanent_surface, *temporary_surface;
|
||||
|
||||
g_source_remove(global_timeout_ref); // stop timer, in case do_drawing takes too long
|
||||
|
||||
if (global_firstcall) {
|
||||
// force aspect ratio by making black stripes at left and right, or top and bottom
|
||||
gtk_window_get_size(GTK_WINDOW(window), &windowWidth, &windowHeight);
|
||||
// gtk_window_set_resizable(GTK_WINDOW(window), 0); // do not allow further resizing
|
||||
|
||||
if ((windowWidth != 1024) || (windowHeight != 768)) {
|
||||
if (windowWidth > (int)((double)windowHeight * aspectRatio + 0.5)) {
|
||||
windowWidthOffset = (windowWidth - (int)((double)windowHeight * aspectRatio)) / 2;
|
||||
windowWidth = (int)((double)windowHeight * aspectRatio + 0.5);
|
||||
}
|
||||
if (windowHeight > (int)((double)windowWidth / aspectRatio + 0.5) ) {
|
||||
windowHeightOffset = (windowHeight - (int)((double)windowWidth / aspectRatio)) / 2;
|
||||
windowHeight = (int)((double)windowWidth / aspectRatio + 0.5);
|
||||
}
|
||||
}
|
||||
printf("Window dimensions: %d x %d\n", windowWidth, windowHeight);
|
||||
|
||||
permanent_surface = cairo_surface_create_similar(cairo_get_target(cr),
|
||||
CAIRO_CONTENT_COLOR, windowWidth, windowHeight);
|
||||
temporary_surface = cairo_surface_create_similar(cairo_get_target(cr),
|
||||
CAIRO_CONTENT_COLOR_ALPHA, windowWidth, windowHeight);
|
||||
|
||||
if (argHideCursor) { // hide cursor (does not allow GIN mode)
|
||||
GdkCursor* Cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
|
||||
GdkWindow* win = gtk_widget_get_window(window);
|
||||
gdk_window_set_cursor((win), Cursor);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_t *permanent_cr = cairo_create(permanent_surface);
|
||||
cairo_t *temporary_cr = cairo_create(temporary_surface);
|
||||
if ((permanent_cr == NULL) || (temporary_cr == NULL)) {
|
||||
printf("Cannot create drawing surfaces\n");
|
||||
exit(1);
|
||||
}
|
||||
if (argARDS)
|
||||
ards_draw(permanent_cr, temporary_cr, global_firstcall);
|
||||
else
|
||||
tek4010_draw(permanent_cr, temporary_cr, global_firstcall);
|
||||
global_firstcall = FALSE;
|
||||
|
||||
cairo_set_source_surface(cr, permanent_surface, windowWidthOffset, windowHeightOffset);
|
||||
cairo_paint(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_LIGHTEN);
|
||||
cairo_set_source_surface(cr, temporary_surface, windowWidthOffset, windowHeightOffset);
|
||||
cairo_paint(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
cairo_destroy(permanent_cr);
|
||||
cairo_destroy(temporary_cr);
|
||||
global_timeout_ref = g_timeout_add(TIME_INTERVAL, (GSourceFunc) on_timer_event,
|
||||
(gpointer) window);
|
||||
}
|
||||
|
||||
static void on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
|
||||
{
|
||||
int ch;
|
||||
// printf("key pressed, state =%04X, keyval=%04X, isGinMode = %d\r\n", event->state, event->keyval, isGinMode);
|
||||
|
||||
if ((event->keyval == 0xFF50) || // "home" key
|
||||
(event->keyval == 0xFF55) || // "page up" key
|
||||
(event->keyval == 0xFF56)) // "page down" key
|
||||
{
|
||||
tube_doClearPersistent = 1;
|
||||
gtk_widget_queue_draw(widget);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->keyval == 0x8BF) { // "option b" sends break code to target
|
||||
if (putKeys) {
|
||||
printf("sending break code\n");
|
||||
putc(0xFF, putKeys);
|
||||
putc(0xF3, putKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// control keys
|
||||
else if ((event->keyval >= 0xFF00) && (event->keyval <= 0xFF1F))
|
||||
ch = event->keyval & 0x1F;
|
||||
else if (event->state & GDK_CONTROL_MASK) {
|
||||
if ((event->keyval == 0xFF51) || // "<ctrl>left arrow" key
|
||||
(event->keyval == 0xFF52)) { // "<ctrl>up arrow" key
|
||||
tube_doClearPersistent = 1;
|
||||
gtk_widget_queue_draw(widget);
|
||||
return;
|
||||
}
|
||||
else if (event->keyval == 0x0077) { // "<ctrl>w" makes screendump
|
||||
system("scrot --focussed");
|
||||
return;
|
||||
}
|
||||
else if (event->keyval == 0x0071) { // "<ctrl>q" quits tek4010
|
||||
on_quit_event();
|
||||
return;
|
||||
}
|
||||
else if (argAPL && (event->keyval == 0x006E)) { // "<ctrl>n" switch to alternative character set
|
||||
aplMode = 1;
|
||||
// printf("Setting APL mode to 1 from keyboard\n");
|
||||
return;
|
||||
}
|
||||
else if (argAPL && (event->keyval == 0x006F)) { // "<ctrl>o" switch to normalcharacter set
|
||||
aplMode = 0;
|
||||
// printf("Setting APL mode to 0 from keyboard\n");
|
||||
return;
|
||||
}
|
||||
else
|
||||
ch = event->keyval & 0x1F;
|
||||
}
|
||||
else if (event->keyval == 0xFF52) ch = 16; // arrow up for history up
|
||||
else if (event->keyval == 0xFF54) ch = 14; // arrow down for history down
|
||||
|
||||
else if ((event->state & GDK_MOD1_MASK) && (aplMode)) { // alt key
|
||||
printf("alt key, ch = %4X\n", event->keyval & 0x7F);
|
||||
ch = (event->keyval & 0x7F) + 128;
|
||||
}
|
||||
|
||||
// normal keys
|
||||
else if ((event->keyval >= 0x0020) && (event->keyval <= 0x007F))
|
||||
ch = event->keyval & 0x7F;
|
||||
|
||||
else return;
|
||||
|
||||
if (isGinMode) { // need to pass key to GIN mode handling, not to child process
|
||||
isGinMode = ch;
|
||||
gtk_widget_queue_draw(widget);
|
||||
}
|
||||
|
||||
else if (putKeys) {
|
||||
// pipe key to child process, if stream open
|
||||
if (aplMode) {
|
||||
ch = tube_translateKeyCode(ch);
|
||||
putc(ch & 0xFF, putKeys);
|
||||
if (ch >> 8) { // overstrike
|
||||
putc(8, putKeys);
|
||||
putc(ch >> 8, putKeys);
|
||||
}
|
||||
}
|
||||
else
|
||||
putc(ch,putKeys);
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *darea;
|
||||
|
||||
int askWindowWidth;
|
||||
int askWindowHeight;
|
||||
|
||||
tube_init(argc, argv);
|
||||
|
||||
if (argARDS) {
|
||||
askWindowWidth = 1080;
|
||||
askWindowHeight = 1414;
|
||||
}
|
||||
|
||||
else if (argFull) {
|
||||
askWindowWidth = 4096;
|
||||
askWindowHeight = 3120;
|
||||
}
|
||||
else {
|
||||
askWindowWidth = 1024;
|
||||
askWindowHeight = 780;
|
||||
}
|
||||
|
||||
aspectRatio = (double)askWindowWidth / (double)askWindowHeight;
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
global_firstcall = TRUE;
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
// set the background color
|
||||
GdkColor color;
|
||||
color.red = 0;
|
||||
color.green = 0;
|
||||
color.blue = 0;
|
||||
gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
|
||||
|
||||
darea = gtk_drawing_area_new();
|
||||
gtk_container_add(GTK_CONTAINER(window), darea);
|
||||
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
|
||||
gtk_widget_add_events(window, GDK_KEY_PRESS_MASK);
|
||||
|
||||
gtk_widget_set_events(darea,gtk_widget_get_events(darea) | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
|
||||
g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);
|
||||
g_signal_connect(G_OBJECT(darea), "destroy", G_CALLBACK(on_quit_event), NULL);
|
||||
g_signal_connect(G_OBJECT(darea), "button-press-event", G_CALLBACK(clicked), NULL);
|
||||
g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(on_key_press), NULL);
|
||||
|
||||
GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(window));
|
||||
int screenWidth = gdk_screen_get_width(screen);
|
||||
int screenHeight = gdk_screen_get_height(screen);
|
||||
printf("Screen dimensions: %d x %d\n", screenWidth, screenHeight);
|
||||
|
||||
if (argFull) {
|
||||
// DISPLAY UNDECORATED FULL SCREEN WINDOW
|
||||
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
|
||||
gtk_window_fullscreen(GTK_WINDOW(window));
|
||||
gtk_window_set_keep_above(GTK_WINDOW(window), FALSE);
|
||||
windowWidth = screenWidth;
|
||||
windowHeight = screenHeight;
|
||||
}
|
||||
|
||||
else {
|
||||
// DISPLAY DECORATED WINDOW
|
||||
if (argFullV || (askWindowHeight > (screenHeight - BORDER))) {
|
||||
askWindowWidth = (int)((double)(screenHeight - BORDER) * aspectRatio);
|
||||
askWindowHeight = screenHeight - BORDER;
|
||||
}
|
||||
gtk_window_set_decorated(GTK_WINDOW(window), TRUE);
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), askWindowWidth, askWindowHeight);
|
||||
windowWidth = askWindowWidth;
|
||||
windowHeight = askWindowHeight;
|
||||
}
|
||||
// printf("Requested window dimensions: %d x %d\n", windowWidth, windowHeight);
|
||||
|
||||
if (TIME_INTERVAL > 0) {
|
||||
// Add timer event
|
||||
// Register the timer and set time in mS.
|
||||
// The timer_event() function is called repeatedly until it returns FALSE.
|
||||
global_timeout_ref = g_timeout_add(TIME_INTERVAL, (GSourceFunc) on_timer_event,
|
||||
(gpointer) window);
|
||||
}
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(window), windowName);
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
RPi5/main.h
20
RPi5/main.h
@ -1,20 +0,0 @@
|
||||
|
||||
#include <cairo.h>
|
||||
|
||||
#define BORDER 80 // we need to make an educated guess to make sure that the full vertical
|
||||
// space is used if required
|
||||
// if BORDER is too small, we might end up with a too large window
|
||||
// if BORDER is too large, the decorated window will be smaller than possible
|
||||
// with a reasonable size BORDER, both are acceptable
|
||||
// ideally, one could force the window manager to use a certain aspect ratio
|
||||
|
||||
void tube_init(int argc, char* argv[]);
|
||||
void tek4010_draw(cairo_t *cr, cairo_t *cr2, int first);
|
||||
void tek4010_clicked(int x, int y);
|
||||
int tube_on_timer_event();
|
||||
int tube_clicked(int button, int x, int y);
|
||||
void tube_quit();
|
||||
|
||||
void ards_draw(cairo_t *cr, cairo_t *cr2, int first);
|
||||
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
LIBS = `pkg-config --libs gtk+-3.0`
|
||||
|
||||
CFLAGS = -std=c99 `pkg-config --cflags gtk+-3.0`
|
||||
|
||||
all: ../tek4010
|
||||
|
||||
../tek4010: main.c main.h tube.c tube.h tek4010.c ards.c
|
||||
$(CC) -o ../tek4010 main.c tube.c tek4010.c ards.c $(LIBS) $(CFLAGS)
|
||||
|
||||
install: ../tek4010
|
||||
./install
|
||||
|
||||
clean : ../tek4010
|
||||
-rm -f ../tek4010
|
||||
710
RPi5/tek4010.c
710
RPi5/tek4010.c
@ -1,710 +0,0 @@
|
||||
/*
|
||||
* tek4010.c
|
||||
*
|
||||
* A tek4010/4014 graphics emulator
|
||||
*
|
||||
* Copyright 2019 rricharz
|
||||
*
|
||||
* https://github.com/rricharz/Tek4010
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG 0 // print debug info
|
||||
|
||||
|
||||
#define TODO (long)(16) // draw multiple objects until screen updates
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "tube.h"
|
||||
|
||||
// mode handles the current state of the emulator:
|
||||
//
|
||||
// mode 0 alpha mode
|
||||
//
|
||||
// mode 1 expecting address byte of dark mode (move to) address
|
||||
// mode 2 expecting second byte of dark mode (move to) address
|
||||
// mode 3 expecting third byte of dark mode (move to) address
|
||||
// mode 4 expecting fourth byte of dark mode (move to) address
|
||||
// mode 5 expecting first byte of persistent vector end point address
|
||||
// mode 6 expecting second byte of persistent vector end point address
|
||||
// mode 7 expecting third byte of persistent vector end point address
|
||||
// mode 8 expecting fourth byte of persistent vector end point address
|
||||
//
|
||||
// mode 30 expecting escape sequence, escape code received
|
||||
// mode 31 received in ANSI escape sequence, escape sequence continues if next char is digit
|
||||
//
|
||||
// mode 40 incremental plot mode; is ignored until exit from incremental plot received
|
||||
// mode 50 special point plot mode
|
||||
// mode 60 crosshair mode
|
||||
// mode 101 ignore until group separator
|
||||
|
||||
int mode, savemode;
|
||||
int penDown = 1;
|
||||
|
||||
extern int leftmargin;
|
||||
extern FILE *putKeys;
|
||||
|
||||
static long startPaintTime;
|
||||
static int xh,xl,yh,yl,xy4014;
|
||||
static long todo;
|
||||
static double efactor;
|
||||
|
||||
int ginCharacter[6] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
// table for special plot point mode
|
||||
// 4014 manual page F-9
|
||||
int intensityTable[] = {14,16,17,19, 20,22,23,25, 28,31,34,38, 41,33,47,50,
|
||||
56,62,69,75, 81,88,94,100, 56,62,69,75, 81,88,96,100,
|
||||
0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||
4, 4, 4, 5, 5, 5, 6, 6, 7, 8, 9,10, 11,12,12,13,
|
||||
14,16,17,19, 20,22,23,25, 28,31,34,38, 41,44,47,50,
|
||||
56,62,69,75, 81,88,94,100, 56,63,69,75, 81,88};
|
||||
|
||||
|
||||
void tek4010_checkLimits(cairo_t *cr, cairo_t *cr2)
|
||||
/* check whether char is in visibel space */
|
||||
{
|
||||
/* don't check here for leftmargin, graphics needs to write characters to the whole screen */
|
||||
if (tube_x0 < 0) tube_x0 = 0;
|
||||
|
||||
if (tube_x0 > windowWidth - hDotsPerChar) {
|
||||
tube_x0 = leftmargin; tube_y0 -= vDotsPerChar;
|
||||
}
|
||||
if (tube_y0 < 4) { // new line at bottom of page
|
||||
tube_y0 = windowHeight - vDotsPerChar;
|
||||
if (leftmargin) leftmargin = 0;
|
||||
else leftmargin = windowWidth / 2;
|
||||
if (argAutoClear) {
|
||||
leftmargin = 0;
|
||||
tube_clearPersistent(cr,cr2);
|
||||
}
|
||||
/* check here for leftmargin */
|
||||
if (tube_x0 < leftmargin) tube_x0 = leftmargin;
|
||||
}
|
||||
if (tube_y0 > (windowHeight - vDotsPerChar)) tube_y0 = windowHeight - vDotsPerChar;
|
||||
}
|
||||
|
||||
void tek4010_bell()
|
||||
{
|
||||
// bell function, delay 0.05 sec
|
||||
tube_u100ResetSeconds(1);
|
||||
usleep(50000);
|
||||
showCursor=0;
|
||||
todo = 0;
|
||||
}
|
||||
|
||||
void sendCoordinates()
|
||||
{
|
||||
// send 4 coordinate bytes
|
||||
int x,y,ch;
|
||||
x = (int)((double)tube_x0 / efactor);
|
||||
y = (int)((double)tube_y0 / efactor);
|
||||
|
||||
if (DEBUG) printf("sendCoordinates, x=%d, y=%d\n", x, y);
|
||||
|
||||
ch = (x >> 5) + 0x20;
|
||||
putc(ch, putKeys);
|
||||
ginCharacter[4] = ch; // save to check for echo
|
||||
ch = (x & 31) + 0x20;
|
||||
putc(ch,putKeys);
|
||||
ginCharacter[3] = ch; // save to check for echo
|
||||
ch = (y >> 5) + 0x20;
|
||||
putc(ch, putKeys);
|
||||
ginCharacter[2] = ch; // save to check for echo
|
||||
ch = (y & 31) + 0x20;
|
||||
putc(ch,putKeys);
|
||||
ginCharacter[1] = ch; // save to check for echo
|
||||
}
|
||||
|
||||
void enqMode()
|
||||
{
|
||||
// activated by sending ESC ENQ
|
||||
int status;
|
||||
status = 0x20;
|
||||
if (leftmargin == 0) status += 2;
|
||||
if (mode == 0) status += 4;
|
||||
putc(status,putKeys); // send status byte
|
||||
sendCoordinates();
|
||||
putc(13, putKeys); // cannot send a EOT here
|
||||
}
|
||||
|
||||
void ginMode(cairo_t *cr, cairo_t *cr2)
|
||||
{
|
||||
// activated by sending ESC SUB
|
||||
if (DEBUG) printf("GIN, mode = %d, isGinMode = %d\n", mode, isGinMode);
|
||||
tube_crosshair(cr, cr2);
|
||||
mode = 60;
|
||||
todo = 0;
|
||||
showCursor = 0;
|
||||
isGinMode = 1;
|
||||
}
|
||||
|
||||
void ginSend(int ch)
|
||||
{
|
||||
// user has stoken a key during GIN mode
|
||||
if (DEBUG) printf("ginSend, ch = %d\n", ch);
|
||||
putc(ch,putKeys); // user key stroke character
|
||||
ginCharacter[5] = ch; // save to check for echo
|
||||
sendCoordinates(); // 4 characters of packed coordinates
|
||||
// cannot send a EOT here
|
||||
// wait 5 ms, then send CR.
|
||||
usleep(5000);
|
||||
putc(13,putKeys); // cr
|
||||
ginCharacter[0] = 13;
|
||||
// prepare to suppress unwanted echoed characters.
|
||||
isGinSuppress = 6;
|
||||
}
|
||||
|
||||
void tek4010_clicked(int x, int y)
|
||||
{
|
||||
if (DEBUG) printf("Clicked, mode = %d\n", mode);
|
||||
if (mode == 60) {
|
||||
tube_x0 = x;
|
||||
tube_y0 = y;
|
||||
}
|
||||
}
|
||||
|
||||
void tek4010_escapeCodeHandler(cairo_t *cr, cairo_t *cr2, int ch)
|
||||
// handle escape sequencies, see 4014 user manual, table page G-1
|
||||
// codes identical for all modes are handled elsewhere
|
||||
{
|
||||
if (DEBUG) printf("Escape %02X, savemode=%d\n",ch,savemode);
|
||||
switch (ch) {
|
||||
case 0: break; // ignore filler 0
|
||||
|
||||
case 5: // ENQ: ask for status and position
|
||||
enqMode();
|
||||
mode = 0; break;
|
||||
case 6: break;
|
||||
|
||||
case 8: // backspace during ESC
|
||||
tube_x0 -= hDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
mode = 0; break;
|
||||
case 9: // tab during ESC
|
||||
if (argTab1)
|
||||
tube_x0 += hDotsPerChar;
|
||||
else
|
||||
tube_x0 = tube_x0 - (tube_x0 % (8 * hDotsPerChar)) + 8 * hDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
mode = 0; break;
|
||||
|
||||
case 11:// VT during ESC, move one line up
|
||||
tube_y0 += vDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
mode = 0; break;
|
||||
case 12:// FF during ESC
|
||||
if (!argKeepSize)
|
||||
tube_changeCharacterSize(cr, cr2, 74, 35, efactor);
|
||||
tube_clearPersistent(cr,cr2);
|
||||
mode = 0; break;
|
||||
case 13:mode = 0; break;
|
||||
case 14: // SO activate alternative char set
|
||||
if (argAPL) { // switch only of argAPL is set
|
||||
aplMode = 1;
|
||||
// printf("Setting APL mode to 1 from computer\n");
|
||||
}
|
||||
mode = 0;
|
||||
todo = 0;
|
||||
break;
|
||||
case 15: // SI deactivate alternative char set
|
||||
aplMode = 0;
|
||||
mode = 0;
|
||||
todo = 0;
|
||||
// printf("Setting APL mode to 0 from computer\n");
|
||||
break;
|
||||
|
||||
case 23: system("scrot --focussed"); mode= 0; break;
|
||||
|
||||
case 26: // sub
|
||||
ginMode(cr, cr2);
|
||||
break;
|
||||
|
||||
// modes 27 and 29 - 31 are identical in all modes
|
||||
case 28: // record separator
|
||||
if (DEBUG) printf("Special point plot mode, mode=%d\n",savemode);
|
||||
mode = 50; // for the intensity/focus character
|
||||
plotPointMode = 1;
|
||||
specialPlotMode = 1;
|
||||
double intensity = 1.0;
|
||||
int defocussed = 0;
|
||||
break;
|
||||
|
||||
case '8': tube_changeCharacterSize(cr, cr2, 74, 35, efactor); mode = 0; break;
|
||||
case '9': tube_changeCharacterSize(cr, cr2, 81, 38, efactor * 0.9); mode = 0; break;
|
||||
case ':': tube_changeCharacterSize(cr, cr2, 121, 58, efactor * 0.65); mode = 0; break;
|
||||
case ';': tube_changeCharacterSize(cr, cr2, 133, 64, efactor * 0.55); mode = 0; break;
|
||||
|
||||
case '[': printf("Ignoring ANSI escape sequence: [");
|
||||
mode=31;
|
||||
break;
|
||||
|
||||
// normal mode
|
||||
case '`': ltype = SOLID; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'a': ltype = DOTTED; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'b': ltype = DOTDASH; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'c': ltype = SHORTDASH;writeThroughMode = 0; mode = savemode; break;
|
||||
case 'd': ltype = LONGDASH; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'e': ltype = SOLID; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'f': ltype = SOLID; writeThroughMode = 0; mode = savemode; break;
|
||||
case 'g': ltype = SOLID; writeThroughMode = 0; mode = savemode; break;
|
||||
|
||||
// defocussed mode
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'n':
|
||||
case 'o': if (DEBUG) printf("Defocussed mode ESC %c not supported, ignored\n", ch);
|
||||
mode = 101; break;
|
||||
|
||||
// write-trough mode
|
||||
case 'p': ltype = SOLID; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 'q': ltype = DOTTED; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 'r': ltype = DOTDASH; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 's': ltype = SHORTDASH;writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 't': ltype = LONGDASH; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 'u': ltype = SOLID; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 'v': ltype = SOLID; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
case 'w': ltype = SOLID; writeThroughMode = 1; mode = 101; showCursor = 0; break;
|
||||
|
||||
default:
|
||||
printf("Ignoring escape code: 0x%02x\n",ch);
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int tek4010_checkReturnToAlpha(int ch)
|
||||
// test for return to alpha character set
|
||||
// see 4014 manual, page F-10, note 1
|
||||
{
|
||||
if (ch == 27)
|
||||
savemode = mode;
|
||||
if ((ch==31) || (ch==13) || (ch==27) /*|| (ch==12)*/) {
|
||||
if (DEBUG && mode) printf("Going to alpha mode\n");
|
||||
mode = 0;
|
||||
showCursor = 0;
|
||||
//if (ch == 12) {
|
||||
// tube_doClearPersistent = 1;
|
||||
// todo = 0;
|
||||
//}
|
||||
if (ch == 27) {
|
||||
mode = 30;
|
||||
todo = 0;
|
||||
}
|
||||
plotPointMode = 0;
|
||||
specialPlotMode = 0;
|
||||
if (isGinMode && DEBUG)
|
||||
printf("clearing isGinMode, char = %d\n", ch);
|
||||
isGinMode = 0;
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int digit(char ch)
|
||||
{
|
||||
return ((ch>='0') && (ch<='9'));
|
||||
}
|
||||
|
||||
void tek4010_draw(cairo_t *cr, cairo_t *cr2, int first)
|
||||
// draw onto the main window using cairo
|
||||
// cr is used for persistent drawing, cr2 for temporary drawing
|
||||
|
||||
{
|
||||
int ch, tag;
|
||||
|
||||
refreshCount++;
|
||||
|
||||
if (first) {
|
||||
first = 0;
|
||||
efactor = windowWidth / 1024.0;
|
||||
refresh_interval = 30;
|
||||
tube_changeCharacterSize(cr, cr2, 74, 35, efactor);
|
||||
if (efactor > 0.8) pensize = efactor * 1.25;
|
||||
if (windowWidth != 1024) printf("Scaling: %0.3f\n", efactor / 4.0);
|
||||
}
|
||||
|
||||
startPaintTime = tube_mSeconds(); // start to measure time for this draw operation
|
||||
|
||||
showCursor = 1;
|
||||
isBrightSpot = 0;
|
||||
|
||||
// clear the second surface
|
||||
tube_clearSecond(cr2);
|
||||
|
||||
// clear persistent surface, if necessary
|
||||
if (tube_doClearPersistent) {
|
||||
tube_clearPersistent(cr,cr2);
|
||||
if (!argKeepSize)
|
||||
tube_changeCharacterSize(cr, cr2, 74, 35, efactor);
|
||||
}
|
||||
|
||||
if (aplMode)
|
||||
tube_setupPainting(cr, cr2, APL_FONT);
|
||||
else
|
||||
tube_setupPainting(cr, cr2, STANDARD_FONT);
|
||||
|
||||
if (plotPointMode)
|
||||
todo = 100 * TODO;
|
||||
else if (writeThroughMode)
|
||||
todo = 8 * TODO;
|
||||
else if (mode == 0)
|
||||
todo = 4 * TODO; // for text speed
|
||||
else
|
||||
todo = TODO;
|
||||
|
||||
do {
|
||||
ch = tube_getInputChar();
|
||||
|
||||
if (tube_isInput() == 0) {
|
||||
todo = 0;
|
||||
}
|
||||
|
||||
if (ch == -1) {
|
||||
if ((mode == 0) && showCursor) tube_doCursor(cr2);
|
||||
if (mode != 60) return; // no char available, need to allow for updates
|
||||
}
|
||||
|
||||
// Try suppressing GIN echoed characters here.
|
||||
if (isGinSuppress){
|
||||
if (ch == 10) // additional line feed may be echoed if cr is typed
|
||||
return;
|
||||
if ((ch & 0x7F) == ginCharacter[isGinSuppress - 1]) {
|
||||
if (DEBUG) printf( "isGinSuppress (%d): suppressing: %d\n",
|
||||
isGinSuppress, ch);
|
||||
isGinSuppress --;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (DEBUG) printf("isGinSuppress, characters are different (%d,%d)\n",
|
||||
ch & 0x7F, ginCharacter[isGinSuppress - 1]);
|
||||
isGinSuppress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// if (aplMode) printf("Receiving character %d from host\n", ch);
|
||||
|
||||
if (DEBUG) {
|
||||
printf("mode=%d, ch code %02X",mode,ch);
|
||||
if ((ch>0x20)&&(ch<=0x7E)) printf(" (%c)",ch);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (tek4010_checkReturnToAlpha(ch)) {
|
||||
todo = todo - 4;
|
||||
goto endDo;
|
||||
}
|
||||
|
||||
// the following chars are identical in all modes (with exception: 13,28)
|
||||
// see 4014 user manual, table on page G1 ff
|
||||
|
||||
switch (ch) {
|
||||
case 7: tek4010_bell();
|
||||
goto endDo;
|
||||
case 10: // new line
|
||||
tube_y0 -= vDotsPerChar;
|
||||
if (!argRaw) tube_x0 = leftmargin;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
goto endDo;
|
||||
case 13: // return
|
||||
if (mode != 30) { // special handling in ESC mode
|
||||
mode = 0; tube_x0 = leftmargin;
|
||||
goto endDo;
|
||||
}
|
||||
break;
|
||||
case 27: // escape code, all modes
|
||||
savemode = mode;
|
||||
mode = 30;
|
||||
goto endDo;
|
||||
case 28: // file separator >> point plot mode
|
||||
if (mode != 30) { // special handling in ESC mode
|
||||
mode = 5;
|
||||
plotPointMode= 1;
|
||||
goto endDo;
|
||||
}
|
||||
break;
|
||||
case 29: // group separator >> graphics mode
|
||||
mode = 1;
|
||||
plotPointMode = 0;
|
||||
goto endDo;
|
||||
case 30: // record separator >> incremental plot mode
|
||||
if (DEBUG) printf("Incremental point plot mode\n");
|
||||
penDown = 1;
|
||||
mode = 40;
|
||||
goto endDo;
|
||||
case 31: // US, normal mode
|
||||
mode = 0;
|
||||
goto endDo;
|
||||
}
|
||||
|
||||
|
||||
// handle skipping coordinate bytes
|
||||
// this cannot be done in switch(mode) below, because multiple bytes
|
||||
// can be skipped and the current byte must be executed after a mode change
|
||||
|
||||
tag = (ch >> 5) & 3;
|
||||
|
||||
if ((mode >= 1) && (mode <= 8)) {
|
||||
|
||||
if ((mode == 5) && (ch == 29)) {
|
||||
if (DEBUG) printf("group separator, go from mode 5 to mode 1\n");
|
||||
mode = 1;
|
||||
goto endDo; // goto end of do loop
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
if (mode & 1)
|
||||
printf(" mode=%d,tag=%d-H-val=%d,",
|
||||
mode,tag, 32 * (ch & 31));
|
||||
else
|
||||
printf(" mode=%d,tag=%d-L-val=%d,",
|
||||
mode,tag, ch & 31);
|
||||
printf("xh=%d,xl=%d,yh=%d,yl=%d\n",xh,xl,yh,yl);
|
||||
}
|
||||
|
||||
if (tag != 0) {
|
||||
|
||||
if ((mode == 1) && (tag != 1)) mode = 2;
|
||||
|
||||
if ((mode == 3) && (tag == 3)) {
|
||||
// this overwrites the extra data byte of the 4014 for the
|
||||
// persistent mode ccordinates and stores it for further use
|
||||
mode = 2;
|
||||
xy4014 = yl;
|
||||
if (DEBUG)
|
||||
printf("4014 coordinates, overwrite last value\n");
|
||||
}
|
||||
|
||||
if ((mode == 2) && (tag != 3)) mode = 3;
|
||||
if ((mode == 3) && (tag != 1)) mode = 4;
|
||||
|
||||
if ((mode == 5) && (tag != 1)) mode = 6;
|
||||
|
||||
if ((mode == 7) && (tag == 3)) {
|
||||
// this overwrites the extra data byte of the 4014 for the
|
||||
// persistent mode ccordinates and stores it for further use
|
||||
mode = 6;
|
||||
xy4014 = yl;
|
||||
if (DEBUG)
|
||||
printf("4014 coordinates, overwrite last value\n");
|
||||
}
|
||||
|
||||
if ((mode == 6) && (tag != 3)) mode = 7;
|
||||
if ((mode == 7) && (tag != 1)) mode = 8;
|
||||
}
|
||||
else {
|
||||
if (ch == 0) return;
|
||||
if (ch == 29) mode = 1; // group separator
|
||||
else if (ch == 28) { plotPointMode = 1; todo = 16 * todo; }
|
||||
else if (DEBUG) printf("Plot mode, unknown char %d, plotPointMode = %d\n",ch,plotPointMode);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// handling anything specific to a mode
|
||||
|
||||
switch (mode) {
|
||||
case 1: plotPointMode = 0; // normal graphics mode, starting coordinates
|
||||
yh = 32 * (ch & 31); mode++;
|
||||
if (DEBUG) printf("setting yh to %d\n", yh);
|
||||
break;
|
||||
case 2: yl = (ch & 31);
|
||||
mode++;
|
||||
if (DEBUG) printf("setting yl to %d\n", yl);
|
||||
break;
|
||||
case 3: if (tag == 1) xh = 32 * (ch & 31); mode++;
|
||||
if (DEBUG) printf("setting xh to %d\n", xh);
|
||||
break;
|
||||
case 4: xl = (ch & 31);
|
||||
if (windowWidth != 1024) {
|
||||
int xb = xy4014 & 3;
|
||||
tube_x0 = (int)(efactor * (double)(((xh+xl) << 2) + xb) / 4.0);
|
||||
int yb = (xy4014 >> 2) & 3;
|
||||
tube_y0 = (int)(efactor * (double)(((yh+yl) << 2) + yb) / 4.0);
|
||||
}
|
||||
else {
|
||||
tube_x0 = xh + xl;
|
||||
tube_y0 = yh + yl;
|
||||
}
|
||||
mode++;
|
||||
tube_emulateDeflectionTime();
|
||||
if (DEBUG) printf("setting xl to %d\n", xl);
|
||||
if (DEBUG) printf("******************************************** Moving to (%d,%d)\n",tube_x0,tube_y0);
|
||||
break;
|
||||
case 5: if (ch == 29) {
|
||||
if (DEBUG) printf("setting mode to 1\n");
|
||||
mode = 1;
|
||||
}
|
||||
else if (tag != 0) {
|
||||
yh = 32 * (ch & 31); mode++;
|
||||
}
|
||||
else if (DEBUG) printf("case 5: tag is 0\n");
|
||||
if (DEBUG) printf(">>>>>yh=%d\n",yh);
|
||||
break;
|
||||
case 6: yl = (ch & 31); mode++;
|
||||
break;
|
||||
if (DEBUG) printf(">>>>>yl=%d\n",yl);
|
||||
case 7: xh = 32 * (ch & 31); mode++;
|
||||
break;
|
||||
if (DEBUG) printf(">>>>>xh=%d\n",xh);
|
||||
case 8: xl = (ch & 31);
|
||||
if (windowWidth != 1024) {
|
||||
int xb = xy4014 & 3;
|
||||
tube_x2 = (int)(efactor * (double)(((xh+xl) << 2) + xb) / 4.0);
|
||||
int yb = (xy4014 >> 2) & 3;
|
||||
tube_y2 = (int)(efactor * (double)(((yh+yl) << 2) + yb) / 4.0);
|
||||
}
|
||||
else {
|
||||
tube_x2 = xh + xl;
|
||||
tube_y2 = yh + yl;
|
||||
}
|
||||
if (DEBUG) printf(">>>>>xl=%d\n",xl);
|
||||
|
||||
if (plotPointMode>0.0) {
|
||||
|
||||
// draw the point
|
||||
tube_drawPoint(cr, cr2);
|
||||
|
||||
todo--;
|
||||
}
|
||||
|
||||
else {
|
||||
tube_drawVector(cr,cr2);
|
||||
|
||||
todo--;
|
||||
}
|
||||
|
||||
showCursor = 0;
|
||||
|
||||
tube_x0 = tube_x2; // prepare for additional vectors
|
||||
tube_y0 = tube_y2;
|
||||
if (specialPlotMode) mode = 50; // another intensity/focus char follows
|
||||
else mode = 5;
|
||||
break;
|
||||
case 30: // escape code handler
|
||||
tek4010_escapeCodeHandler(cr, cr2, ch);
|
||||
break;
|
||||
case 31: // ANSI CSI sequence
|
||||
printf("%c",ch);
|
||||
if ((ch<0x20) || (ch>0x3F)) {
|
||||
mode=0;
|
||||
printf("\n");
|
||||
}
|
||||
case 40: // incremental plot mode
|
||||
tek4010_checkReturnToAlpha(ch); // check for exit
|
||||
if (DEBUG) printf("Incremental plot mode, ch = %d, penDown = %d\n",ch, penDown);
|
||||
if (ch == 32) penDown = 0;
|
||||
else if (ch == 80) penDown = 1;
|
||||
else if ((ch & 0x70) == 0x40){
|
||||
if (ch & 4) tube_y0++;
|
||||
if (ch & 1) tube_x0++;
|
||||
if (ch & 8) tube_y0--;
|
||||
if (ch & 2) tube_x0--;
|
||||
if (DEBUG) printf("point (%d,%d)\n", tube_x0, tube_y0);
|
||||
tube_x2 = tube_x0;
|
||||
tube_y2 = tube_y0;
|
||||
if (penDown) tube_drawPoint(cr, cr2);
|
||||
}
|
||||
else if (DEBUG) printf("Illegal byte 0x%02X in incremental plot\n", ch);
|
||||
break;
|
||||
case 50:// special plot mode
|
||||
tag = ch >> 5;
|
||||
if ((ch < 32) || (ch >= 126)) return;
|
||||
if (DEBUG) printf("intensity/focus control = %c: %d: ", ch, tag);
|
||||
defocussed = (tag == 1);
|
||||
intensity = intensityTable[ch - 32];
|
||||
if (DEBUG) printf("defocussed = %d, intensity = %d%%\n", defocussed, intensity);
|
||||
mode = 5; // coordinates follow
|
||||
break;
|
||||
case 60:// crosshair mode
|
||||
if (isGinMode > 1) { // key stroken by user
|
||||
ginSend(isGinMode);
|
||||
mode = 0;
|
||||
todo = 0;
|
||||
if (DEBUG) printf("GIN: key stroken by user, exiting GIN mode\n");
|
||||
isGinMode = 0;
|
||||
}
|
||||
else
|
||||
ginMode(cr, cr2);
|
||||
break;
|
||||
case 101:
|
||||
if (DEBUG) printf("Ignore until group separator, ch = %02x\n", ch);
|
||||
if (ch == 29) mode = 1;
|
||||
break;
|
||||
case 0: // handle ALPHA mode; 4014 user manual, table page G-1
|
||||
// some characters are indentical for all modes and handled elsewhere
|
||||
switch (ch) {
|
||||
case 0: break;
|
||||
case 8: // backspace
|
||||
tube_x0 -= hDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
break;
|
||||
case 9: // tab
|
||||
if (argTab1)
|
||||
tube_x0 += hDotsPerChar;
|
||||
else
|
||||
tube_x0 = tube_x0 - (tube_x0 % (8 * hDotsPerChar)) + 8 * hDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
break;
|
||||
case 11: // VT, move one line up
|
||||
tube_y0 += vDotsPerChar;
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
break;
|
||||
case 23: // ctrl-w screen dump
|
||||
system("scrot --focussed");
|
||||
break;
|
||||
|
||||
default: if ((ch >= 32) && (ch <127)) { // printable character
|
||||
tek4010_checkLimits(cr, cr2);
|
||||
tube_drawCharacter(cr,cr2, ch);
|
||||
todo-= 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: if (DEBUG) printf("Illegal mode - this is a tek4010decoder error and should not happen\n");
|
||||
break;
|
||||
}
|
||||
endDo:;
|
||||
}
|
||||
while ((todo > 0) && ((tube_mSeconds() - startPaintTime) < refresh_interval));
|
||||
|
||||
// display cursor
|
||||
|
||||
if (showCursor && (tube_isInput() == 0)) tube_doCursor(cr2);
|
||||
|
||||
}
|
||||
922
RPi5/tube.c
922
RPi5/tube.c
@ -1,922 +0,0 @@
|
||||
/*
|
||||
* tube.c
|
||||
*
|
||||
* A tek4010/4014 graphics emulator
|
||||
*
|
||||
* Copyright 2019 rricharz
|
||||
*
|
||||
* https://github.com/rricharz/Tek4010
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG 0 // print debug info
|
||||
#define DEBUGMAX 0 // exit after DEBUGMAX chars, 0 means no exit
|
||||
|
||||
#define WRITE_TROUGH_INTENSITY 0.5 // green only
|
||||
#define NORMAL_INTENSITY 0.7
|
||||
#define CURSOR_INTENSITY 0.7
|
||||
#define BRIGHT_SPOT_COLOR 1.0
|
||||
#define BRIGHT_SPOT_COLOR_HALF 0.6
|
||||
#define BLACK_COLOR 0.08 // effect of flood gun
|
||||
#define FADE 0.3 // lower value means slower fading
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <locale.h>
|
||||
#include <pwd.h>
|
||||
// Cygwin defines FIONREAD in <sys/socket.h>. #26
|
||||
#ifndef FIONREAD
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include "tube.h"
|
||||
|
||||
extern void gtk_main_quit();
|
||||
extern int windowWidth;
|
||||
extern int windowHeight;
|
||||
extern char *windowName;
|
||||
|
||||
|
||||
// variables for APL keycode translator
|
||||
#define MAXKEYCODES 256
|
||||
struct keyCode {
|
||||
int inputCode;
|
||||
int outputCode;
|
||||
};
|
||||
|
||||
struct keyCode keyTable[MAXKEYCODES];
|
||||
|
||||
int argNoexit = 0; // options
|
||||
int argRaw = 0;
|
||||
int argBaud = 19200;
|
||||
int argTab1 = 0;
|
||||
int argFull = 0;
|
||||
int argFullV = 0;
|
||||
int argARDS = 0;
|
||||
int argAPL = 0;
|
||||
int argAutoClear = 0;
|
||||
int argKeepSize = 0;
|
||||
int argHideCursor = 0;
|
||||
int argWait = 0;
|
||||
|
||||
int refresh_interval; // after this time in msec next refresh is done
|
||||
|
||||
int showCursor; // set of cursor is shown (not set in graphics mode)
|
||||
int isBrightSpot = 0; // set if there is currently a bright spot on the screen
|
||||
|
||||
int xlast, ylast;
|
||||
|
||||
enum LineType ltype;
|
||||
double dashset[] = {2,6,2,2,6,3,3,3,6,6};
|
||||
|
||||
int plotPointMode = 0; // plot point mode
|
||||
int writeThroughMode = 0; // write through mode
|
||||
int isGinMode = 0; // set if GIN mode is active
|
||||
int isGinSuppress = 0; // suppressing characters after GIN.
|
||||
int tube_doClearPersistent;
|
||||
|
||||
int specialPlotMode = 0;
|
||||
int defocussed = 0;
|
||||
int intensity = 100;
|
||||
int aplMode = 0;
|
||||
|
||||
int tube_x0, tube_x2,tube_y0, tube_y2;
|
||||
|
||||
double pensize = 1.0;
|
||||
|
||||
static int debugCount = 0;
|
||||
|
||||
long refreshCount = 0; // variables for baud rate and refresh rate measurements
|
||||
|
||||
static long charCount = 0;
|
||||
static long charResetCount = 0;
|
||||
static long characterInterval = 0;
|
||||
static int currentFontSize = 18;
|
||||
static int currentCharacterOffset = 0;
|
||||
|
||||
long startPaintTime;
|
||||
long firstWait;
|
||||
|
||||
int leftmargin;
|
||||
|
||||
int hDotsPerChar;
|
||||
int vDotsPerChar;
|
||||
|
||||
FILE *getData;
|
||||
int getDataPipe[2];
|
||||
|
||||
FILE *putKeys;
|
||||
int putKeysPipe[2];
|
||||
|
||||
long tube_mSeconds()
|
||||
// return time in msec since start of program
|
||||
{
|
||||
static int initialized = 0;
|
||||
static long startTime;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
long t = (1000 * tv.tv_sec) + (tv.tv_usec/1000);
|
||||
if (!initialized) startTime = t;
|
||||
initialized = 1;
|
||||
t = t - startTime;
|
||||
if (t < 0) t += 86400000;
|
||||
return t;
|
||||
}
|
||||
|
||||
long tube_u100ResetSeconds(int reset)
|
||||
// returns time in 100 usec since last reset
|
||||
{
|
||||
static long startTime;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
long t = (10000 * tv.tv_sec) + (tv.tv_usec/100);
|
||||
if (reset) {
|
||||
startTime = t;
|
||||
charResetCount = 0;
|
||||
}
|
||||
t = t - startTime;
|
||||
if (t < 0) t += 864000000;
|
||||
return t;
|
||||
}
|
||||
|
||||
int tube_isInput()
|
||||
// is char available on getDataPipe?
|
||||
{
|
||||
int bytesWaiting;
|
||||
if (isGinMode)
|
||||
// delay any input from host during GIN mode
|
||||
// this allows to test .plt files with GIN mode
|
||||
return 0;
|
||||
ioctl(getDataPipe[0], FIONREAD, &bytesWaiting);
|
||||
if (DEBUG) {
|
||||
debugCount++;
|
||||
if (DEBUGMAX && (debugCount > DEBUGMAX)) return 0;
|
||||
}
|
||||
if (bytesWaiting == 0) {
|
||||
// reset the baud rate counter
|
||||
// without this, baud rate goal would not work after waiting for chars
|
||||
tube_u100ResetSeconds(1);
|
||||
}
|
||||
return bytesWaiting;
|
||||
}
|
||||
|
||||
int tube_getInputChar()
|
||||
// get a char from getDataPipe, if available, otherwise return -1
|
||||
{
|
||||
static long lastTime = 0;
|
||||
|
||||
long t = tube_u100ResetSeconds(0);
|
||||
if (tube_isInput()) {
|
||||
// handle baud rate since last no input available
|
||||
if (t < charResetCount * characterInterval)
|
||||
return -1; // there is time to refresh the screen
|
||||
int c = getc(getData) & 0x7F;
|
||||
charCount++;
|
||||
charResetCount++;
|
||||
lastTime = t;
|
||||
return c;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void checkFont(char *fontName)
|
||||
// check whether font has been installed
|
||||
{
|
||||
#define MAXL 255
|
||||
FILE *f;
|
||||
int i, ch;
|
||||
char line[MAXL];
|
||||
sprintf(line,"fc-list \'%s\'", fontName); // prepare system call
|
||||
f = popen(line, "r");
|
||||
if (f != NULL) {
|
||||
i = 0;
|
||||
line[0] = 0;
|
||||
while (((ch=fgetc(f))!=EOF) && (i<MAXL-1)) {
|
||||
line[i++] = ch;
|
||||
}
|
||||
line[i-1]=0;
|
||||
// printf("%s\n",line);
|
||||
if (strstr(line, fontName) > 0) {
|
||||
pclose(f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pclose(f);
|
||||
printf("Error: APL font \'%s\' not installed. This font is required for the APL mode.\n", fontName);
|
||||
printf("See github.com/rricharz/tek4010 paragraph \'APL mode\'\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void readKeyTranslationTable()
|
||||
// read keyboard translation table from ~/.tekaplkeys
|
||||
{
|
||||
FILE *confFile;
|
||||
int code1, code2, i;
|
||||
char *homedir = getpwuid(getuid())->pw_dir;
|
||||
char s[255];
|
||||
|
||||
memset(keyTable, 0, sizeof(keyTable));
|
||||
|
||||
strcpy(s, homedir);
|
||||
strcat(s, "/.tek4010conf/aplkeys" );
|
||||
// printf("Looking for conf file %s\n",s);
|
||||
confFile = fopen(s,"r");
|
||||
if (confFile) {
|
||||
// printf("confFile open\n");
|
||||
i = 0;
|
||||
while (!feof(confFile)) {
|
||||
if (fscanf(confFile, "%d %d\n", &code1, & code2) == 2) {
|
||||
// printf("%d %d\n", code1, code2);
|
||||
keyTable[i].inputCode = code1;
|
||||
keyTable[i].outputCode = code2;
|
||||
i++;
|
||||
if (i >= MAXKEYCODES) {
|
||||
printf("Error: APL key code table too large, max %d entries\n",MAXKEYCODES);
|
||||
fclose(confFile);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
fscanf(confFile,"%s\n",s); // skip comment line
|
||||
}
|
||||
fclose(confFile);
|
||||
}
|
||||
}
|
||||
|
||||
int tube_translateKeyCode(int ch)
|
||||
{
|
||||
int i = 0;
|
||||
// printf("TranslateKeyCode %d ", ch);
|
||||
while ((i < MAXKEYCODES) && (keyTable[i].inputCode != 0)) {
|
||||
if (keyTable[i].inputCode == ch) {
|
||||
// printf("%d\n", keyTable[i].outputCode);
|
||||
return keyTable[i].outputCode;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
printf("\n");
|
||||
return ch;
|
||||
}
|
||||
|
||||
void tube_init(int argc, char* argv[])
|
||||
// put any code here to initialize the tek4010
|
||||
{
|
||||
char *argv2[20];
|
||||
size_t bufsize = 127;
|
||||
int firstArg = 1;
|
||||
printf("tek4010 version 1.8\n");
|
||||
windowName = "Tektronix 4010/4014 emulator";
|
||||
if ((argc<2) || (argc>19)) {
|
||||
printf("Error:number of arguments\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// this stays here for compatibility with early versions of tek4010
|
||||
if (strcmp(argv[argc-1],"-noexit") == 0) {
|
||||
argNoexit = 1;
|
||||
argc--;
|
||||
}
|
||||
|
||||
while ((argv[firstArg][0] == '-') && (firstArg < argc-1)) {
|
||||
if (strcmp(argv[firstArg],"-raw") == 0)
|
||||
argRaw = 1;
|
||||
else if (strcmp(argv[firstArg],"-noexit") == 0)
|
||||
argNoexit = 1;
|
||||
else if (strcmp(argv[firstArg],"-b100000") == 0)
|
||||
argBaud = 100000;
|
||||
else if (strcmp(argv[firstArg],"-b38400") == 0)
|
||||
argBaud = 38400;
|
||||
else if (strcmp(argv[firstArg],"-b19200") == 0)
|
||||
argBaud = 19200;
|
||||
else if (strcmp(argv[firstArg],"-b9600") == 0)
|
||||
argBaud = 9600;
|
||||
else if (strcmp(argv[firstArg],"-b4800") == 0)
|
||||
argBaud = 4800;
|
||||
else if (strcmp(argv[firstArg],"-b2400") == 0)
|
||||
argBaud = 2400;
|
||||
else if (strcmp(argv[firstArg],"-b1200") == 0)
|
||||
argBaud = 1200;
|
||||
else if (strcmp(argv[firstArg],"-b600") == 0)
|
||||
argBaud = 600;
|
||||
else if (strcmp(argv[firstArg],"-b300") == 0)
|
||||
argBaud = 300;
|
||||
else if (strcmp(argv[firstArg],"-tab1") == 0)
|
||||
argTab1 = 1;
|
||||
else if (strcmp(argv[firstArg],"-full") == 0)
|
||||
argFull = 1;
|
||||
else if (strcmp(argv[firstArg],"-fullv") == 0)
|
||||
argFullV = 1;
|
||||
else if (strcmp(argv[firstArg],"-autoClear") == 0)
|
||||
argAutoClear = 1;
|
||||
else if (strcmp(argv[firstArg],"-keepsize") == 0)
|
||||
argKeepSize = 1;
|
||||
else if (strcmp(argv[firstArg],"-hidecursor") == 0)
|
||||
argHideCursor = 1;
|
||||
else if (strcmp(argv[firstArg],"-APL") == 0) {
|
||||
argAPL = 1;
|
||||
windowName = "Tektronix 4013/4015 emulator (APL)";
|
||||
checkFont(APL_FONT);
|
||||
readKeyTranslationTable();
|
||||
}
|
||||
else if (strcmp(argv[firstArg],"-ARDS") == 0) {
|
||||
argARDS = 1;
|
||||
windowName = "ARDS emulator";
|
||||
}
|
||||
else if (strcmp(argv[firstArg],"-wait") == 0) {
|
||||
argWait = 3;
|
||||
if (firstArg < argc-2) {
|
||||
if ((argv[firstArg+1][0] >= '0') &&
|
||||
(argv[firstArg+1][0] <= '9')) {
|
||||
firstArg++;
|
||||
argWait = atoi(argv[firstArg]);
|
||||
}
|
||||
}
|
||||
// printf("Waiting %d seconds after end of plot\n", argWait);
|
||||
}
|
||||
else {
|
||||
printf("tek4010: unknown argument %s\n", argv[firstArg]);
|
||||
exit(1);
|
||||
}
|
||||
firstArg++;
|
||||
|
||||
}
|
||||
|
||||
// A child process for rsh is forked and communication
|
||||
// between parent and child are established
|
||||
|
||||
// expand argv[firstArg] to full path and check, whether it exists
|
||||
char *str = (char *) malloc(bufsize * sizeof(char));
|
||||
if (str == NULL) {
|
||||
printf("Cannot allocate memory for absolute path\n");
|
||||
exit(1);
|
||||
}
|
||||
strcpy(str,"which ");
|
||||
strcat(str, argv[firstArg]);
|
||||
FILE *fullPath = popen(str,"r");
|
||||
if (fullPath) {
|
||||
getline(&str, &bufsize,fullPath);
|
||||
|
||||
// remove the endline character
|
||||
str[strlen(str)-1] = 0;
|
||||
|
||||
if (strncmp(str,"which",5) == 0) {
|
||||
printf("Unknown command %s\n", argv[firstArg]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
argv[firstArg] = str;
|
||||
pclose(fullPath);
|
||||
}
|
||||
else {
|
||||
printf("Unknown command %s\n", argv[firstArg]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
characterInterval = 100000 / argBaud; // in 100 usecs, assuming 1 start and 1 stop bit.
|
||||
|
||||
if (DEBUG) printf("character_interval = %0.1f msec\n",(double)characterInterval/10.0);
|
||||
|
||||
tube_doClearPersistent = 1;
|
||||
|
||||
// create pipes for communication between parent and child
|
||||
if (pipe(getDataPipe) == -1) {
|
||||
printf("Cannot initialize data pipe\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pipe(putKeysPipe) == -1) {
|
||||
printf("Cannot initialize key pipe\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// now fork a child process
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
printf("Cannot fork child process\n");
|
||||
exit(1);
|
||||
}
|
||||
else if (pid == 0) { // child process
|
||||
|
||||
// we need a second string array with an empty string as last item!
|
||||
argv2[0] = argv[firstArg];
|
||||
for (int i = 1; i < argc; i++)
|
||||
argv2[i] = argv[firstArg+i-1];
|
||||
argv2[argc-firstArg+1] = (char*) NULL;
|
||||
|
||||
// int i = 0;
|
||||
// do {
|
||||
// printf("argv2[%d] = %s\n",i,argv2[i]);
|
||||
// i++;
|
||||
// }
|
||||
// while (argv2[i] != (char*) NULL);
|
||||
|
||||
// set stdout of child process to getDataPipe
|
||||
while ((dup2(getDataPipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
|
||||
close(getDataPipe[1]); // not used anymore
|
||||
close(getDataPipe[0]); // not used
|
||||
|
||||
// set stdin of child process to putKeysPipe
|
||||
while ((dup2(putKeysPipe[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
|
||||
close(putKeysPipe[1]); // not used
|
||||
close(putKeysPipe[0]); // not used anymore
|
||||
|
||||
// run rsh in the child process
|
||||
execv(argv2[0],argv2+1);
|
||||
free(str);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// parent process
|
||||
|
||||
free(str);
|
||||
|
||||
close(getDataPipe[1]); // not used
|
||||
close(putKeysPipe[0]); // not used
|
||||
|
||||
// use termios to turn off line buffering for both pipes
|
||||
// struct termios term;
|
||||
// tcgetattr(getDataPipe[0], &term);
|
||||
// term.c_lflag &= ~ICANON ;
|
||||
// tcsetattr(getDataPipe[0], TCSANOW,&term);
|
||||
// tcgetattr(putKeysPipe[1], &term);
|
||||
// tcsetattr(putKeysPipe[0], TCSANOW,&term);
|
||||
|
||||
// open now a stream from the getDataPipe descriptor
|
||||
getData = fdopen(getDataPipe[0],"r");
|
||||
if (getData == 0) {
|
||||
printf("Parent: Cannot open input stream\n");
|
||||
exit(1);
|
||||
}
|
||||
setbuf(getData,0);
|
||||
|
||||
// open now a stream from the putKeysPipe descriptor
|
||||
putKeys = fdopen(putKeysPipe[1],"w");
|
||||
if (putKeys == 0) {
|
||||
printf("Parent: Cannot open output stream\n");
|
||||
exit(1);
|
||||
}
|
||||
setbuf(putKeys,0);
|
||||
|
||||
tube_mSeconds(); // initialize the timer
|
||||
tube_u100ResetSeconds(1);
|
||||
}
|
||||
|
||||
int tube_on_timer_event()
|
||||
// if TIMER_INTERVAL in tek4010.h is larger than zero, this function
|
||||
// is called every TIMER-INTERVAL milliseconds
|
||||
// if the function returns 1, the window is redrawn by calling applicatin_draw
|
||||
{
|
||||
// if there is a char available on the imput stream
|
||||
// or there is still a bright spot, return 1 to ask for
|
||||
// one more redraw
|
||||
|
||||
// is child process still running?
|
||||
|
||||
int status;
|
||||
if (argWait && (tube_isInput() == 0) && (waitpid(-1, &status, WNOHANG))) {
|
||||
if (firstWait == 0)
|
||||
firstWait = tube_mSeconds();
|
||||
else {
|
||||
if ((int)((tube_mSeconds() - firstWait) / 1000) > argWait) {
|
||||
tube_quit();
|
||||
gtk_main_quit();
|
||||
printf("Process has been terminated after %d seconds\n", argWait);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((!argNoexit) && (tube_isInput() == 0) && (waitpid(-1, &status, WNOHANG))) {
|
||||
long t = tube_mSeconds();
|
||||
// printf("Execution time: %0.3f sec\n", (double)t/1000.0);
|
||||
// if (t > 0) {
|
||||
// printf("Average screen refresh rate: %0.1f Hz\n",(double)(1000.0*refreshCount)/t);
|
||||
// printf("Average character rate: %0.0f baud\n",(double)(8000.0*charCount)/t);
|
||||
// }
|
||||
tube_quit();
|
||||
gtk_main_quit();
|
||||
printf("Process has been terminated\n");
|
||||
exit(0);
|
||||
}
|
||||
return (isBrightSpot || tube_isInput());
|
||||
}
|
||||
|
||||
int tube_clicked(int button, int x, int y)
|
||||
// is called if a mouse button is clicked in the window
|
||||
// button = 1: means left mouse button; button = 3 means right mouse button
|
||||
// x and y are the coordinates
|
||||
// if the function returns 1, the window is redrawn by calling applicatin_draw
|
||||
{
|
||||
if (argARDS) return 0;
|
||||
if (button == 1) {
|
||||
tek4010_clicked(x, windowHeight - y);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tube_quit()
|
||||
// is called if the main window is quit bevore the tek4010 exits
|
||||
// put any code here which needs to be called on exit
|
||||
{
|
||||
pclose(getData);
|
||||
system("pkill rs232-console");
|
||||
}
|
||||
|
||||
void tube_doCursor(cairo_t *cr2)
|
||||
{
|
||||
cairo_set_source_rgb(cr2, 0, CURSOR_INTENSITY, 0);
|
||||
cairo_set_line_width (cr2, 1);
|
||||
cairo_rectangle(cr2, tube_x0, windowHeight - tube_y0 - vDotsPerChar + 6 + currentCharacterOffset,
|
||||
hDotsPerChar - 3, vDotsPerChar - 3);
|
||||
cairo_fill(cr2);
|
||||
cairo_stroke (cr2);
|
||||
}
|
||||
|
||||
void tube_clearPersistent(cairo_t *cr, cairo_t *cr2)
|
||||
// clear the persistant surface
|
||||
// flash using the second surface
|
||||
{
|
||||
cairo_set_source_rgb(cr, 0.0, BLACK_COLOR, 0.0);
|
||||
cairo_paint(cr);
|
||||
tube_doClearPersistent = 0;
|
||||
tube_x0 = 0;
|
||||
tube_y0 = windowHeight - vDotsPerChar;
|
||||
tube_x2 = tube_x0;
|
||||
tube_y2 = tube_y0;
|
||||
leftmargin = 0;
|
||||
cairo_set_source_rgb(cr, 0, NORMAL_INTENSITY, 0);
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR / 2, 0);
|
||||
cairo_paint(cr2);
|
||||
isBrightSpot = 1;
|
||||
plotPointMode = 0;
|
||||
ltype = SOLID;
|
||||
xlast = 0;
|
||||
ylast = 0;
|
||||
specialPlotMode = 0;
|
||||
defocussed = 0;
|
||||
intensity = 100;
|
||||
}
|
||||
|
||||
void tube_clearSecond(cairo_t *cr2)
|
||||
// clear second surface
|
||||
{
|
||||
cairo_set_source_rgba(cr2, 0, 0, 0, FADE);
|
||||
cairo_set_operator(cr2, CAIRO_OPERATOR_MULTIPLY);
|
||||
cairo_paint(cr2);
|
||||
cairo_set_operator(cr2, CAIRO_OPERATOR_OVER);
|
||||
isBrightSpot = 1;
|
||||
}
|
||||
|
||||
void tube_line_type(cairo_t *cr, cairo_t *cr2, enum LineType ln)
|
||||
{
|
||||
int ndash,ndx;
|
||||
double ofs = 0.5;
|
||||
|
||||
switch (ln) {
|
||||
case SOLID:
|
||||
ndx = 0;
|
||||
ndash = 0;
|
||||
break;
|
||||
case DOTTED:
|
||||
ndx = 0;
|
||||
ndash = 2;
|
||||
break;
|
||||
case DOTDASH:
|
||||
ndx = 2;
|
||||
ndash = 4;
|
||||
break;
|
||||
case LONGDASH:
|
||||
ndx = 8;
|
||||
ndash = 2;
|
||||
break;
|
||||
case SHORTDASH:
|
||||
ndx = 6;
|
||||
ndash = 2;
|
||||
break;
|
||||
}
|
||||
cairo_set_dash (cr,&dashset[ndx],ndash,ofs);
|
||||
cairo_set_dash (cr2,&dashset[ndx],ndash,ofs);
|
||||
}
|
||||
|
||||
void tube_drawCharacter(cairo_t *cr, cairo_t *cr2, char ch)
|
||||
{
|
||||
char s[8];
|
||||
|
||||
if (ch < 32) return; // non printable control character
|
||||
|
||||
if (aplMode) {
|
||||
switch (ch) {
|
||||
case 32: sprintf(s," ");
|
||||
break;
|
||||
case 33: sprintf(s,"\u00A8");
|
||||
break;
|
||||
case 34: sprintf(s,")");
|
||||
break;
|
||||
case 35: sprintf(s,"<");
|
||||
break;
|
||||
case 36: sprintf(s,"\u2264");
|
||||
break;
|
||||
case 37: sprintf(s,"=");
|
||||
break;
|
||||
case 38: sprintf(s,">");
|
||||
break;
|
||||
case 39: sprintf(s,"]");
|
||||
break;
|
||||
case 40: sprintf(s,"\u2228");
|
||||
break;
|
||||
case 41: sprintf(s,"\u2227");
|
||||
break;
|
||||
case 42: sprintf(s,"\u2260");
|
||||
break;
|
||||
case 43: sprintf(s,"\u00F7");
|
||||
break;
|
||||
case 44: sprintf(s,",");
|
||||
break;
|
||||
case 45: sprintf(s,"+");
|
||||
break;
|
||||
case 46: sprintf(s,".");
|
||||
break;
|
||||
case 47: sprintf(s,"/");
|
||||
break;
|
||||
|
||||
// 48 - 57: Digits
|
||||
|
||||
case 58: sprintf(s,"(");
|
||||
break;
|
||||
case 59: sprintf(s,"[");
|
||||
break;
|
||||
case 60: sprintf(s,";");
|
||||
break;
|
||||
case 61: sprintf(s,"\u00D7");
|
||||
break;
|
||||
case 62: sprintf(s,":");
|
||||
break;
|
||||
case 63: sprintf(s,"\\");
|
||||
break;
|
||||
case 64: sprintf(s,"\u00AF");
|
||||
break;
|
||||
case 65: sprintf(s,"\u237A");
|
||||
break;
|
||||
case 66: sprintf(s,"\u22A5");
|
||||
break;
|
||||
case 67: sprintf(s,"\u2229");
|
||||
break;
|
||||
case 68: sprintf(s,"\u230A");
|
||||
break;
|
||||
case 69: sprintf(s,"\u220A");
|
||||
break;
|
||||
case 70: sprintf(s,"_");
|
||||
break;
|
||||
case 71: sprintf(s,"\u2207");
|
||||
break;
|
||||
case 72: sprintf(s,"\u2206");
|
||||
break;
|
||||
case 73: sprintf(s,"\u2373");
|
||||
break;
|
||||
case 74: sprintf(s,"\u2218");
|
||||
break;
|
||||
case 75: sprintf(s,"'");
|
||||
break;
|
||||
case 76: sprintf(s,"\u2395");
|
||||
break;
|
||||
case 77: sprintf(s,"\u2223");
|
||||
break;
|
||||
case 78: sprintf(s,"\u22A4");
|
||||
break;
|
||||
case 79: sprintf(s,"\u25CB");
|
||||
break;
|
||||
|
||||
case 80: sprintf(s,"\u22c6");
|
||||
break;
|
||||
case 81: sprintf(s,"?");
|
||||
break;
|
||||
case 82: sprintf(s,"\u2374");
|
||||
break;
|
||||
case 83: sprintf(s,"\u2308");
|
||||
break;
|
||||
case 84: sprintf(s,"\u223C");
|
||||
break;
|
||||
case 85: sprintf(s,"\u2193");
|
||||
break;
|
||||
case 86: sprintf(s,"\u222A");
|
||||
break;
|
||||
case 87: sprintf(s,"\u03C9");
|
||||
break;
|
||||
case 88: sprintf(s,"\u2283");
|
||||
break;
|
||||
case 89: sprintf(s,"\u2191");
|
||||
break;
|
||||
case 90: sprintf(s,"\u2282");
|
||||
break;
|
||||
case 91: sprintf(s,"\u2190");
|
||||
break;
|
||||
case 92: sprintf(s,"\u22A2");
|
||||
break;
|
||||
case 93: sprintf(s,"\u2192");
|
||||
break;
|
||||
case 94: sprintf(s,"\u2265");
|
||||
break;
|
||||
case 95: sprintf(s,"-");
|
||||
break;
|
||||
case 96: sprintf(s,"\u22C4");
|
||||
break;
|
||||
|
||||
// 97 - 122 capital letters
|
||||
|
||||
case 123: sprintf(s,"{");
|
||||
break;
|
||||
case 124: sprintf(s,"\u22A3");
|
||||
break;
|
||||
case 125: sprintf(s,"}");
|
||||
break;
|
||||
case 126: sprintf(s,"$");
|
||||
break;
|
||||
|
||||
default: if ((ch>=48) && (ch<=57)) sprintf(s,"%c", ch); // digits
|
||||
else if ((ch>=97) && (ch<=122)) sprintf(s,"%c", ch - 32); // capital letters
|
||||
else sprintf(s," ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
s[0] = ch;
|
||||
s[1] = 0;
|
||||
}
|
||||
cairo_set_font_size(cr, currentFontSize);
|
||||
cairo_set_font_size(cr2,currentFontSize);
|
||||
|
||||
if (writeThroughMode) { // draw the write-through character
|
||||
cairo_set_source_rgb(cr2, 0, WRITE_TROUGH_INTENSITY, 0);
|
||||
cairo_move_to(cr2, tube_x0, windowHeight - tube_y0 + currentCharacterOffset);
|
||||
cairo_show_text(cr2, s);
|
||||
}
|
||||
|
||||
else {
|
||||
// draw the character
|
||||
cairo_set_source_rgb(cr, 0, BLACK_COLOR + ((NORMAL_INTENSITY - BLACK_COLOR) * intensity) / 100, 0);
|
||||
cairo_move_to(cr, tube_x0, windowHeight - tube_y0 + currentCharacterOffset);
|
||||
cairo_show_text(cr, s);
|
||||
|
||||
// draw the bright spot
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR, 0);
|
||||
cairo_move_to(cr2, tube_x0, windowHeight - tube_y0 + currentCharacterOffset);
|
||||
cairo_show_text(cr2, s);
|
||||
}
|
||||
|
||||
tube_x0 += hDotsPerChar;
|
||||
isBrightSpot = 1;
|
||||
}
|
||||
|
||||
void tube_emulateDeflectionTime()
|
||||
{
|
||||
// find length of longer component
|
||||
int l = tube_x2 - tube_x0;
|
||||
if ((tube_x0-tube_x2) > l) l = tube_x0 - tube_x2;
|
||||
if ((tube_y2-tube_y0) > l) l = tube_y2 - tube_y0;
|
||||
if ((tube_y0-tube_y2) > l) l = tube_y0 - tube_y2;
|
||||
if (l > 300) { // the 300 accounts for other overheads
|
||||
usleep((l - 300) * 2); // roughly 2 usec per dot
|
||||
}
|
||||
}
|
||||
|
||||
void tube_drawPoint(cairo_t *cr, cairo_t *cr2)
|
||||
{
|
||||
#define PI2 6.283185307
|
||||
int i1;
|
||||
cairo_set_line_width (cr, pensize + defocussed);
|
||||
cairo_set_source_rgb(cr, 0, BLACK_COLOR + ((1.0 - BLACK_COLOR) * intensity) / 100, 0);
|
||||
cairo_move_to(cr, tube_x2, windowHeight - tube_y2);
|
||||
cairo_line_to(cr, tube_x2 + 1, windowHeight - tube_y2 + 1);
|
||||
cairo_stroke (cr);
|
||||
|
||||
// speed is a problem here
|
||||
// do not draw adjacent bright spots
|
||||
|
||||
if (((tube_x2 - xlast) > 2) || ((xlast - tube_x2) > 2) ||
|
||||
((tube_y2 - ylast) > 2) || ((ylast - tube_y2) > 2)) {
|
||||
|
||||
// draw the bright spot
|
||||
cairo_set_line_width (cr2, 0.1);
|
||||
double bsc = (BRIGHT_SPOT_COLOR * intensity) / 100;
|
||||
|
||||
cairo_set_source_rgb(cr2, 0, bsc, 0);
|
||||
cairo_arc(cr2, tube_x2, windowHeight - tube_y2, 2 + defocussed, 0, PI2);
|
||||
cairo_fill(cr2);
|
||||
|
||||
xlast = tube_x2;
|
||||
ylast = tube_y2;
|
||||
}
|
||||
|
||||
isBrightSpot = 1;
|
||||
}
|
||||
|
||||
void tube_crosshair(cairo_t *cr, cairo_t *cr2)
|
||||
{
|
||||
// printf("crosshair at %d,%d\n", tube_x0, tube_y0);
|
||||
cairo_set_line_width (cr2, 1);
|
||||
cairo_set_source_rgb(cr2, 0.0, WRITE_TROUGH_INTENSITY, 0.0);
|
||||
cairo_move_to(cr2, tube_x0, 0);
|
||||
cairo_line_to(cr2, tube_x0, windowHeight);
|
||||
cairo_move_to(cr2, 0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr2, windowWidth, windowHeight - tube_y0);
|
||||
cairo_stroke (cr2);
|
||||
}
|
||||
|
||||
void tube_drawVector(cairo_t *cr, cairo_t *cr2)
|
||||
{
|
||||
if (DEBUG) {
|
||||
printf("********************************************");
|
||||
printf("Drawing from (%d,%d) to (%d,%d), writethrough = %d\n",
|
||||
tube_x0, tube_y0, tube_x2, tube_y2, writeThroughMode);
|
||||
}
|
||||
tube_emulateDeflectionTime();
|
||||
|
||||
if ((tube_x2 == tube_x0) && (tube_y2 == tube_y0)) tube_x0++; // cairo cannot draw a dot
|
||||
|
||||
if (writeThroughMode) {
|
||||
cairo_set_line_width (cr2, pensize + 1);
|
||||
cairo_set_source_rgb(cr2, 0.0, WRITE_TROUGH_INTENSITY, 0.0);
|
||||
cairo_move_to(cr2, tube_x0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr2, tube_x2, windowHeight - tube_y2);
|
||||
cairo_stroke (cr2);
|
||||
}
|
||||
|
||||
else {
|
||||
// draw the actual vector on permanent surface
|
||||
cairo_set_line_width (cr, pensize + defocussed);
|
||||
cairo_set_source_rgb(cr, 0, BLACK_COLOR + ((NORMAL_INTENSITY - BLACK_COLOR) * intensity) / 100, 0);
|
||||
tube_line_type(cr, cr2, ltype);
|
||||
cairo_move_to(cr, tube_x0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr, tube_x2, windowHeight - tube_y2);
|
||||
cairo_stroke (cr);
|
||||
|
||||
|
||||
// draw the bright spot, high intensity
|
||||
cairo_set_line_width (cr, (pensize+1) + defocussed);
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR, 0);
|
||||
cairo_move_to(cr2, tube_x0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr2, tube_x2, windowHeight - tube_y2);
|
||||
cairo_stroke(cr2);
|
||||
}
|
||||
|
||||
isBrightSpot = 1; // also to be set if writeThroughMode
|
||||
}
|
||||
|
||||
void tube_setupPainting(cairo_t *cr, cairo_t *cr2, char *fontName)
|
||||
{
|
||||
cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
|
||||
cairo_set_line_width (cr, pensize);
|
||||
cairo_set_source_rgb(cr, 0, NORMAL_INTENSITY, 0);
|
||||
cairo_select_font_face(cr, fontName, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_select_font_face(cr2, fontName, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
|
||||
}
|
||||
|
||||
void tube_changeCharacterSize(cairo_t *cr, cairo_t *cr2,int charsPerLine, int charsPerPage, double fontSize)
|
||||
{
|
||||
cairo_font_extents_t et;
|
||||
hDotsPerChar = windowWidth / charsPerLine;
|
||||
vDotsPerChar = windowHeight / charsPerPage;
|
||||
leftmargin = 0;
|
||||
if (argARDS) {
|
||||
currentFontSize = (int) (fontSize * APL_FONT_SIZE);
|
||||
}
|
||||
else {
|
||||
currentFontSize = (int) (fontSize * STANDARD_FONT_SIZE);
|
||||
}
|
||||
cairo_set_font_size(cr, currentFontSize);
|
||||
cairo_set_font_size(cr2,currentFontSize);
|
||||
if (argARDS) {
|
||||
cairo_font_extents(cr, &et);
|
||||
currentCharacterOffset =(int)et.ascent;
|
||||
if (DEBUG) printf("Set vertical character offset for ARDS mode to %d\n", currentCharacterOffset);
|
||||
}
|
||||
else
|
||||
currentCharacterOffset = 0;
|
||||
}
|
||||
62
RPi5/tube.h
62
RPi5/tube.h
@ -1,62 +0,0 @@
|
||||
// tube.h
|
||||
|
||||
// fonts
|
||||
#define STANDARD_FONT "Monospace"
|
||||
#define STANDARD_FONT_SIZE 18.0
|
||||
#define APL_FONT "APL385 Unicode"
|
||||
#define APL_FONT_SIZE 20.0
|
||||
|
||||
enum LineType {SOLID,DOTTED,DOTDASH,SHORTDASH,LONGDASH};
|
||||
extern enum LineType ltype;
|
||||
|
||||
extern int tube_doClearPersistent;
|
||||
extern int windowWidth;
|
||||
extern int windowHeight;
|
||||
|
||||
extern int argFull;
|
||||
extern int argTab1;
|
||||
extern int argRaw;
|
||||
extern int argAPL;
|
||||
extern int argAutoClear;
|
||||
extern int argKeepSize;
|
||||
extern int argHideCursor;
|
||||
extern int argWait;
|
||||
|
||||
extern int hDotsPerChar;
|
||||
extern int vDotsPerChar;
|
||||
|
||||
extern int refresh_interval; // after this time in msec next refresh is done
|
||||
extern long refreshCount;
|
||||
|
||||
extern int showCursor; // set of cursor is shown (not set in graphics mode)
|
||||
extern int isBrightSpot; // set if there is currently a bright spot on the screen
|
||||
extern int isGinMode; // set if GIN mode is active
|
||||
extern int isGinSuppress; // set if suppressing echoed chars in/after GIN.
|
||||
|
||||
extern int specialPlotMode;
|
||||
extern int defocussed;
|
||||
extern int intensity;
|
||||
extern int aplMode;
|
||||
|
||||
extern int plotPointMode;
|
||||
extern int writeThroughMode;
|
||||
|
||||
extern int tube_x0, tube_x2, tube_y0, tube_y2;
|
||||
|
||||
extern double pensize;
|
||||
|
||||
extern long tube_mSeconds();
|
||||
extern long tube_u100ResetSeconds(int reset);
|
||||
extern int tube_translateKeyCode(int ch);
|
||||
extern void tube_doCursor(cairo_t *cr2);
|
||||
extern void tube_clearPersistent(cairo_t *cr, cairo_t *cr2);
|
||||
extern void tube_clearSecond(cairo_t *cr2);
|
||||
extern int tube_isInput();
|
||||
extern int tube_getInputChar();
|
||||
extern void tube_emulateDeflectionTime();
|
||||
extern void tube_crosshair(cairo_t *cr, cairo_t *cr2);
|
||||
extern void tube_drawVector(cairo_t *cr, cairo_t *cr2);
|
||||
extern void tube_drawCharacter(cairo_t *cr, cairo_t *cr2, char ch);
|
||||
extern void tube_drawPoint(cairo_t *cr, cairo_t *cr2);
|
||||
extern void tube_setupPainting(cairo_t *cr, cairo_t *cr2, char *fontName);
|
||||
extern void tube_changeCharacterSize(cairo_t *cr, cairo_t *cr2, int charsPerLine, int charsPerPage, double fontSize);
|
||||
2
install
2
install
@ -7,5 +7,5 @@
|
||||
cp tek4010 ~/bin
|
||||
chmod +x ~/bin/tek4010
|
||||
|
||||
echo "tek4010 version 1.7 installed."
|
||||
echo "tek4010 installed."
|
||||
echo "Reboot if this is the first time tek4010 is installed"
|
||||
|
||||
@ -88,7 +88,7 @@ void ards_draw(cairo_t *cr, cairo_t *cr2, int first)
|
||||
first = 0;
|
||||
efactor = windowWidth / 1080.0;
|
||||
// fprintf (stderr, "efactor: %0.2f\n", efactor);
|
||||
refresh_interval = 30;
|
||||
refresh_interval = 50;
|
||||
tube_changeCharacterSize(cr, cr2, 80, 50, efactor * 1.1);
|
||||
|
||||
}
|
||||
|
||||
@ -140,8 +140,10 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
|
||||
|
||||
cairo_set_source_surface(cr, permanent_surface, windowWidthOffset, windowHeightOffset);
|
||||
cairo_paint(cr);
|
||||
cairo_set_source_surface(cr, temporary_surface, windowWidthOffset, windowHeightOffset);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_LIGHTEN);
|
||||
cairo_set_source_surface(cr, temporary_surface, windowWidthOffset, windowHeightOffset);
|
||||
cairo_paint(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
cairo_destroy(permanent_cr);
|
||||
cairo_destroy(temporary_cr);
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
#define DEBUG 0 // print debug info
|
||||
|
||||
|
||||
#define TODO (long)(16) // draw multiple objects until screen updates
|
||||
#define TODO (long)(32) // draw multiple objects until screen updates
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
@ -350,7 +350,7 @@ void tek4010_draw(cairo_t *cr, cairo_t *cr2, int first)
|
||||
if (first) {
|
||||
first = 0;
|
||||
efactor = windowWidth / 1024.0;
|
||||
refresh_interval = 30;
|
||||
refresh_interval = 50;
|
||||
tube_changeCharacterSize(cr, cr2, 74, 35, efactor);
|
||||
if (efactor > 0.8) pensize = efactor * 1.25;
|
||||
if (windowWidth != 1024) printf("Scaling: %0.3f\n", efactor / 4.0);
|
||||
|
||||
24
src/tube.c
24
src/tube.c
@ -28,11 +28,12 @@
|
||||
#define DEBUGMAX 0 // exit after DEBUGMAX chars, 0 means no exit
|
||||
|
||||
#define WRITE_TROUGH_INTENSITY 0.5 // green only
|
||||
#define NORMAL_INTENSITY 0.75
|
||||
#define CURSOR_INTENSITY 0.75
|
||||
#define NORMAL_INTENSITY 0.7
|
||||
#define CURSOR_INTENSITY 0.7
|
||||
#define BRIGHT_SPOT_COLOR 1.0
|
||||
#define BRIGHT_SPOT_COLOR_HALF 0.6
|
||||
#define BLACK_COLOR 0.08 // effect of flood gun
|
||||
#define FADE 0.3 // lower value means slower fading
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
@ -290,7 +291,7 @@ void tube_init(int argc, char* argv[])
|
||||
char *argv2[20];
|
||||
size_t bufsize = 127;
|
||||
int firstArg = 1;
|
||||
printf("tek4010 version 1.7\n");
|
||||
printf("tek4010 version 1.8\n");
|
||||
windowName = "Tektronix 4010/4014 emulator";
|
||||
if ((argc<2) || (argc>19)) {
|
||||
printf("Error:number of arguments\n");
|
||||
@ -571,7 +572,7 @@ void tube_clearPersistent(cairo_t *cr, cairo_t *cr2)
|
||||
tube_y2 = tube_y0;
|
||||
leftmargin = 0;
|
||||
cairo_set_source_rgb(cr, 0, NORMAL_INTENSITY, 0);
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR, 0);
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR / 2, 0);
|
||||
cairo_paint(cr2);
|
||||
isBrightSpot = 1;
|
||||
plotPointMode = 0;
|
||||
@ -586,10 +587,11 @@ void tube_clearPersistent(cairo_t *cr, cairo_t *cr2)
|
||||
void tube_clearSecond(cairo_t *cr2)
|
||||
// clear second surface
|
||||
{
|
||||
cairo_set_source_rgba(cr2, 0, 0, 0, 0);
|
||||
cairo_set_operator(cr2, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr2, 0, 0, 0, FADE);
|
||||
cairo_set_operator(cr2, CAIRO_OPERATOR_MULTIPLY);
|
||||
cairo_paint(cr2);
|
||||
cairo_set_operator(cr2, CAIRO_OPERATOR_OVER);
|
||||
isBrightSpot = 1;
|
||||
}
|
||||
|
||||
void tube_line_type(cairo_t *cr, cairo_t *cr2, enum LineType ln)
|
||||
@ -825,7 +827,7 @@ void tube_drawPoint(cairo_t *cr, cairo_t *cr2)
|
||||
double bsc = (BRIGHT_SPOT_COLOR * intensity) / 100;
|
||||
|
||||
cairo_set_source_rgb(cr2, 0, bsc, 0);
|
||||
cairo_arc(cr2, tube_x2, windowHeight - tube_y2, 1 + defocussed, 0, PI2);
|
||||
cairo_arc(cr2, tube_x2, windowHeight - tube_y2, 2 + defocussed, 0, PI2);
|
||||
cairo_fill(cr2);
|
||||
|
||||
xlast = tube_x2;
|
||||
@ -874,11 +876,11 @@ void tube_drawVector(cairo_t *cr, cairo_t *cr2)
|
||||
cairo_move_to(cr, tube_x0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr, tube_x2, windowHeight - tube_y2);
|
||||
cairo_stroke (cr);
|
||||
|
||||
|
||||
|
||||
// draw the bright spot, high intensity
|
||||
// cairo_set_line_width (cr2, pensize + 2 + 2 * defocussed);
|
||||
double bsc = (BRIGHT_SPOT_COLOR * intensity) / 100;
|
||||
cairo_set_source_rgb(cr2, 0, bsc, 0);
|
||||
cairo_set_line_width (cr, (pensize+1) + defocussed);
|
||||
cairo_set_source_rgb(cr2, 0, BRIGHT_SPOT_COLOR, 0);
|
||||
cairo_move_to(cr2, tube_x0, windowHeight - tube_y0);
|
||||
cairo_line_to(cr2, tube_x2, windowHeight - tube_y2);
|
||||
cairo_stroke(cr2);
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
Version 1.8 March 4, 2024
|
||||
=========================
|
||||
Improved bright spot with proper fading
|
||||
|
||||
Version 1.7 October 18, 2023
|
||||
============================
|
||||
Added -wait n seconds as argument, staying alive n seconds after
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user