1
0
mirror of https://github.com/rricharz/Tek4010.git synced 2026-01-13 15:27:31 +00:00

Version 1.8 with improved handling of bright drawing spot and fading released for all systems

This commit is contained in:
Rene Richarz 2024-03-04 09:19:17 +01:00
parent 51ac8ac44d
commit 9ae8b458cf
17 changed files with 40 additions and 2331 deletions

Binary file not shown.

Binary file not shown.

View File

@ -32,10 +32,22 @@ Dave Ault using tek4010.
![scale model](scalemodel.jpg?raw=true "scale model of Tektronix 4010")
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.

View File

@ -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);
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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"

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
@ -875,10 +877,10 @@ void tube_drawVector(cairo_t *cr, cairo_t *cr2)
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);

View File

@ -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