diff --git a/Manual.odt b/Manual.odt index 5f0f09f..4121a3d 100644 Binary files a/Manual.odt and b/Manual.odt differ diff --git a/Manual.pdf b/Manual.pdf index aca7cde..e4da6a7 100644 Binary files a/Manual.pdf and b/Manual.pdf differ diff --git a/README.md b/README.md index e7ffca0..47d260c 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/RPi5/ards.c b/RPi5/ards.c deleted file mode 100755 index 1c43609..0000000 --- a/RPi5/ards.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); - -} diff --git a/RPi5/install b/RPi5/install deleted file mode 100755 index 836378b..0000000 --- a/RPi5/install +++ /dev/null @@ -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" diff --git a/RPi5/main.c b/RPi5/main.c deleted file mode 100644 index 331bf89..0000000 --- a/RPi5/main.c +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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) || // "left arrow" key - (event->keyval == 0xFF52)) { // "up arrow" key - tube_doClearPersistent = 1; - gtk_widget_queue_draw(widget); - return; - } - else if (event->keyval == 0x0077) { // "w" makes screendump - system("scrot --focussed"); - return; - } - else if (event->keyval == 0x0071) { // "q" quits tek4010 - on_quit_event(); - return; - } - else if (argAPL && (event->keyval == 0x006E)) { // "n" switch to alternative character set - aplMode = 1; - // printf("Setting APL mode to 1 from keyboard\n"); - return; - } - else if (argAPL && (event->keyval == 0x006F)) { // "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; -} diff --git a/RPi5/main.h b/RPi5/main.h deleted file mode 100644 index 8f9fefb..0000000 --- a/RPi5/main.h +++ /dev/null @@ -1,20 +0,0 @@ - -#include - -#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); - - diff --git a/RPi5/makefile b/RPi5/makefile deleted file mode 100644 index d2176ea..0000000 --- a/RPi5/makefile +++ /dev/null @@ -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 diff --git a/RPi5/tek4010.c b/RPi5/tek4010.c deleted file mode 100755 index 89eeced..0000000 --- a/RPi5/tek4010.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); - -} diff --git a/RPi5/tube.c b/RPi5/tube.c deleted file mode 100755 index 9a2c3c6..0000000 --- a/RPi5/tube.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// Cygwin defines FIONREAD in . #26 -#ifndef FIONREAD -#include -#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 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; -} diff --git a/RPi5/tube.h b/RPi5/tube.h deleted file mode 100755 index 6f83fa8..0000000 --- a/RPi5/tube.h +++ /dev/null @@ -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); diff --git a/install b/install index aa4c030..9629f41 100755 --- a/install +++ b/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" diff --git a/src/ards.c b/src/ards.c index 1c43609..e9e040a 100755 --- a/src/ards.c +++ b/src/ards.c @@ -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); } diff --git a/src/main.c b/src/main.c index 24793c2..331bf89 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/tek4010.c b/src/tek4010.c index 89eeced..d22ae4c 100755 --- a/src/tek4010.c +++ b/src/tek4010.c @@ -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); diff --git a/src/tube.c b/src/tube.c index eecba77..9a2c3c6 100755 --- a/src/tube.c +++ b/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); diff --git a/versions.txt b/versions.txt index 3e0ff07..1fbdde7 100644 --- a/versions.txt +++ b/versions.txt @@ -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