//
// "FXWorker.cpp"                          Copyright A C Norman 2003-2007
//
//
// Window interface for old-fashioned C applications. Intended to
// be better than just running them within rxvt/xterm, but some people will
// always believe that running them under emacs is best!

/******************************************************************************
* Copyright (C) 2003-6 by Arthur Norman, Codemist Ltd.   All Rights Reserved.   *
*******************************************************************************
* This library is free software; you can redistribute it and/or               *
* modify it under the terms of the GNU Lesser General Public                  *
* License as published by the Free Software Foundation;                       *
* version 2.1 of the License.                                                 *
*                                                                             *
* This library 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           *
* Lesser General Public License for more details.                             *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public            *
* License along with this library; if not, write to the Free Software         *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.  *
*                                                                             *
* See also the FOX Toolkit addendum to the LGPL, which also applies to this   *
* code. This addedum gives, in addition to the rights granted by the LGPL,    *
* permission to distribute this code statically linked against other code     *
* without any need for that other code to have its source released.           *
******************************************************************************/

/* Signature: 0a60ac3d 12-Apr-2008 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <ctype.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef WIN32
#include <windows.h>
#endif

#include <fx.h>

#include "fwin.h"

#include "FXTerminal.h"

#include "termed.h"        // for command line case

#ifdef WIN32
HANDLE thread1;
#else
pthread_t thread1;
#endif

extern const char *colour_spec;

FXMenuBar *main_menu_bar;

FXMenuPane *fileMenu, *editMenu, *fontMenu,
           *breakMenu, *helpMenu;

int rootWidth, rootHeight;

#include "fox-icons.c"

//
// I derive a sub-class from MainWindow just so I can notice when the
// window is re-sized and record information in the registry.
//

FXDEFMAP(FXMainWindow1) FXMainWindow1Map[] =
{
    FXMAPFUNC(SEL_CONFIGURE,    0, FXMainWindow1::onConfigure)
};

FXIMPLEMENT(FXMainWindow1, FXMainWindow, FXMainWindow1Map, ARRAYNUMBER(FXMainWindow1Map))

FXMainWindow1::FXMainWindow1(FXApp *a, const FXString &b, FXIcon *c=NULL,
    FXIcon *d=NULL, FXuint opt=DECOR_ALL,
    FXint x=0, FXint y=0, FXint w=0, FXint h=0,
    FXint pl=0, FXint pr=0, FXint pt=0, FXint pb=0,
    FXint hs=0, FXint vs=0) :
    FXMainWindow(a, b, c, d, opt,
                 x, y, w, h,
                 pl, pr, pt, pb,
                 hs, vs)
{
}

FXMainWindow1::FXMainWindow1(): FXMainWindow()
{
}

void FXMainWindow1::create()
{
    FXMainWindow::create();
}


// This next gets called whenever the window changes size or position.
// The purpose of handing this event is twofold.
// (a) I want to record [in the registry] the place where my screen
//     is positioned, so that next time I start up it appears in the
//     same place;
// (b) I want to force the width of the screen to remain constant at
//     80 columns.

FXApp *application_object;
FXMainWindow1 *main_window;

extern "C"
{
FXTerminal *text = NULL;
}

long FXMainWindow1::onConfigure(FXObject *c, FXSelector s, void *ptr)
{
    FXEvent *e = (FXEvent *)ptr;
    FXRegistry *reg = &(application_object->reg());
    reg->writeIntEntry("screen", "screenx", e->rect.x);
    reg->writeIntEntry("screen", "screeny", e->rect.y);
    reg->writeIntEntry("screen", "screenw", e->rect.w);
    reg->writeIntEntry("screen", "screenh", e->rect.h);
    int r = FXMainWindow::onConfigure(c, s, ptr);
    if (text != NULL) e->rect.w = text->forceWidth();
    return r;
}



int windowed_worker(int argc, char *argv[])
{
    if (windowed==0) return plain_worker(argc, argv);
#ifdef WIN32
// The following is somewhat unsatisfactory so I will explain my options and
// what is happening.
// On Unix/Linux/Darwin etc I have here a single executable that, depending
// on a command-line flag, runs either as a windowed or a command-line
// program. All is pretty neat and convenient for me!  However...
//
// On Windows when I link code I can link it either as subsystem:windows or
// as subsystem:console. If I use the windows case then it detaches from
// its console when started. The effect I have is that when launched from
// a Windows command prompt asking to be run in console mode it can not
// access the console. Windows XP provides an AttachConsole API that might
// let me re-attach to the console but (a) that is not available with
// earlier versions of Windows and (b) my experimenst with it have not been
// great successes and others report delicacies! However note that even if
// the code is linked as a windows binary it can be launched from the cygwin
// shell and inherits standard input and output very happily! But from a
// regular Windows command shell it does not.
// If, on the other hand I link my code as a console application then when
// launched from a command prompt or a script things work as I might like
// and expect. When launched by double-clicking on an icon, Windows says to
// itself "aha - a console application" and rapidly creates a console for it.
//  This pops up on your screen. But here (in the case I wanted a Windowed
// interface) I just free that console, which then has no other users and
// which therefore gets destroyed. So there is a visual glitch of an unwanted
// console window popping up and instantly vanishing.
//
// The best solution that appears to be open to me under Windows is to
// have two executable versions for each application. They would only need
// to differ in the way they were linked (and hence, possibly, by one bit in
// a header record in them!). One for console and one for windowed use.
// That feels clumsy too.
//
// Web searches show that others have found the same sort of pain when they
// have wanted to create applications that are both console and window
// mode. Ah well. One final suggestion for the two-executable scheme is
// to creats two executables, say cslw.exe and cslc.exe where cslw.exe is
// linked in windows mode and cslc.exe in console mode. cslc.exe just
// creates a process to run cslw.exe. When you do this the handles on
// standard input and output can be inherited by the child process, which
// can therefore read and write characters. However because it still does not
// really have a CONSOLE it can not do the res of what one might like by way
// of supporting curses-like screen updates. A final trick on this is to
// rename those two programs as csl.exe (windowed) and csl.com (console).
// The Windows command processor will use the ".com" extension before the
// ".exe" one, but of course the executable is really in ".exe" format...
// this trick maybe reduces the confusion over file-names! Or maybe it
// makes it worse.

#ifdef KEEP_CONSOLE_OPEN
// I sometimes find a console REALLY useful for debugging.... but when you
// launch by double-clicking on an icon it is truly ugly to have one around.
// So I will allow myself to leave an "#ifdef" here in case that helps me make
// a trick version for debugging...
    FreeConsole();
#endif

#endif

#define COMPANY_NAME  "Codemist-Ltd"
#define PRODUCT_NAME  programName

#define WINDOW_NAME   programName

// registry entries will be filed under Codemist-Ltd/<something>.
    application_object = new FXApp(PRODUCT_NAME,
                                   COMPANY_NAME);
// args can be sent in via command-line args, if that is any help.
// Just at present I do not fully understand what FOX does with these
// arguments but it MAY be that it expects to allow "-geometry" or "-fn"
// etc arguments as per standard for X11 applications.
    application_object->init(argc, argv, TRUE);
#if INT_VERSION(FOX_MAJOR,FOX_MINOR,0)==INT_VERSION(1,0,0)
    FXRootWindow *r = application_object->getRoot();
#else
    FXRootWindow *r = application_object->getRootWindow();
#endif
    rootWidth = r->getDefaultWidth(),
    rootHeight = r->getDefaultHeight();

// Now I will decide how big the main window should be. If I have information
// in the registry left over from my last run I will use that.
    FXRegistry *reg = &application_object->reg();
    int screenx = reg->readIntEntry("screen", "screenx", -1);
    int screeny = reg->readIntEntry("screen", "screeny", -1);
    int screenw = reg->readIntEntry("screen", "screenw", -1);
    int screenh = reg->readIntEntry("screen", "screenh", -1);

    if (screenx < 0 || screeny < 0 || screenw <= 100 || screenh < 20)
        screenx = screeny = screenw = screenh = 0;

    int fontsize =
        reg->readIntEntry("screen", "fontsize", -1);
#if (FOX_MINOR<=4)
    int fontweight =
        reg->readIntEntry("screen", "fontweight", FONTWEIGHT_BOLD);
    int fontslant =
        reg->readIntEntry("screen", "fontslant", FONTSLANT_REGULAR);
    int fontencoding =
        reg->readIntEntry("screen", "fontencoding", FONTENCODING_DEFAULT);
    int fontsetwidth =
        reg->readIntEntry("screen", "fontsetwidth", FONTSETWIDTH_DONTCARE);
    int fonthints =
        reg->readIntEntry("screen", "fonthints",
                          FONTPITCH_FIXED|FONTHINT_MODERN);
#else
    int fontweight =
        reg->readIntEntry("screen", "fontweight", FXFont::Bold);
    int fontslant =
        reg->readIntEntry("screen", "fontslant", FXFont::Straight);
    int fontencoding =
        reg->readIntEntry("screen", "fontencoding", FONTENCODING_DEFAULT);
    int fontsetwidth =
        reg->readIntEntry("screen", "fontsetwidth", FXFont::NonExpanded);
    int fonthints =
        reg->readIntEntry("screen", "fonthints",
                          FXFont::Fixed|FXFont::Modern);
#endif
    const char *fontname =
        reg->readStringEntry("screen", "fontname",  DEFAULT_FONT_NAME);
// I have a concern here about how long the string that is returned will
// remain valid. As a matter of caution I will not read other string values
// from the registry until I have used or copied this.

// The icon that I use here depends on the name that this program is
// launched with. This situation in perhaps not perfect but it seems the
// easiest route just for now.
    const unsigned char *icondata = fwin;

#ifdef CSL
// This sets alternative custom icons based on the name of the executable
// and it clearly NOT portable...
    if (strcmp(programName, "csl") == 0) icondata = csl;
    else if (strcmp(programName, "slowr38") == 0) icondata = csl;
    else if (strcmp(programName, "r38") == 0) icondata = r38;
    else if (strcmp(programName, "demored") == 0) icondata = demored;
#endif
    main_window = new FXMainWindow1(
        application_object,
        WINDOW_NAME,
        new FXICOIcon(application_object, icondata,
                      FXRGB(255,255,255), IMAGE_ALPHAGUESS),
        NULL,    // mini-icon
        DECOR_ALL,
        screenx, screeny,
        screenw, screenh);
    main_menu_bar = new FXMenuBar(main_window,
                                  LAYOUT_SIDE_TOP | LAYOUT_FILL_X);
// NB. NB. NB.
// *NB*  the TEXT_COLUMNWRAP flag is my own PATCH to FOX, and when I build
// FOX I change the files FXText.h and FXText.cpp to implement it. It
// lets me wrap lines at exactly 80 columns, regardless of how whitespace
// happens to lie.
    text = new FXTerminal(main_window, NULL, 0,
        HSCROLLER_NEVER | TEXT_FIXEDWRAP | TEXT_WORDWRAP | TEXT_COLUMNWRAP |
        TEXT_SHOWACTIVE | LAYOUT_FILL_X | LAYOUT_FILL_Y);

// I am really supposed to destroy menus as I exit. So somewhere I need to
// arrange that - or maybe I can hope that my application only closes its
// window when finally terminating, and somebody will tidy up at a system
// level for me.
    fileMenu = new FXMenuPane(main_window);
    new FXMenuCommand(fileMenu, "&Read...", NULL,
                                (FXObject *)text, FXTerminal::ID_READ);
    new FXMenuCommand(fileMenu, "&Save...", NULL,
                                (FXObject *)text, FXTerminal::ID_SAVE);
    new FXMenuCommand(fileMenu, "Save Se&lection...", NULL,
                                (FXObject *)text, FXTerminal::ID_SAVE_SELECTION);
    new FXMenuCommand(fileMenu, "&Print...", NULL,
                                (FXObject *)text, FXTerminal::ID_PRINT);
    new FXMenuCommand(fileMenu, "Pri&nt Selection...", NULL,
                                (FXObject *)text, FXTerminal::ID_PRINT_SELECTION);
    new FXMenuCommand(fileMenu, "&Quit\tCtl-\\\tQuit the application.", NULL,
                                application_object, FXApp::ID_QUIT);
// I make this F&ile not &File since alt-F will be for "move forward one
// word" using emacs-like key bindings.
    new FXMenuTitle(main_menu_bar, "F&ile", NULL, fileMenu);

    editMenu = new FXMenuPane(main_window);
    new FXMenuCommand(editMenu, "&Cut", NULL,
                                (FXObject *)text, FXTerminal::ID_CUT_SEL_X);
    new FXMenuCommand(editMenu, "C&opy", NULL,
                                (FXObject *)text, FXTerminal::ID_COPY_SEL_X);
    new FXMenuCommand(editMenu, "&Paste\tCtl-V", NULL,
                                (FXObject *)text, FXTerminal::ID_PASTE_SEL_X);
    new FXMenuCommand(editMenu, "&Reinput\tCtl-^\tReinput", NULL,
                                (FXObject *)text, FXTerminal::ID_REINPUT);
    new FXMenuCommand(editMenu, "Select &All", NULL,
                                (FXObject *)text, FXText::ID_SELECT_ALL);
    new FXMenuCommand(editMenu, "C&lear\tCtl-L", NULL,
                                (FXObject *)text, FXTerminal::ID_CLEAR);
    new FXMenuCommand(editMenu, "Re&draw\tCtl-R", NULL,
                                (FXObject *)text, FXTerminal::ID_REDRAW);
    new FXMenuCommand(editMenu, "&Home", NULL,
                                (FXObject *)text, FXTerminal::ID_HOME);
    new FXMenuCommand(editMenu, "&End", NULL,
                                (FXObject *)text, FXTerminal::ID_END);

    new FXMenuTitle(main_menu_bar, "&Edit", NULL, editMenu);

    fontMenu = new FXMenuPane(main_window);
    new FXMenuCommand(fontMenu, "&Font...", NULL,
                                (FXObject *)text, FXTerminal::ID_FONT);
    new FXMenuCommand(fontMenu, "&Reset Font", NULL,
                                (FXObject *)text, FXTerminal::ID_RESET_FONT);
    new FXMenuCommand(fontMenu, "Reset &Window", NULL,
                                (FXObject *)text, FXTerminal::ID_RESET_WINDOW);
    new FXMenuTitle(main_menu_bar, "F&ont", NULL, fontMenu);

    breakMenu = new FXMenuPane(main_window);
    new FXMenuCommand(breakMenu, "&Break\tCtl-C\tInterrupt", NULL,
                                (FXObject *)text, FXTerminal::ID_BREAK);
    new FXMenuCommand(breakMenu, "Bac&ktrace\tCtl-G\tInterrupt/backtrace", NULL,
                                (FXObject *)text, FXTerminal::ID_BACKTRACE);
    new FXMenuCommand(breakMenu, "&Pause\tCtl-S", NULL,
                                (FXObject *)text, FXTerminal::ID_PAUSE);
    new FXMenuCommand(breakMenu, "&Resume\tCtl-Q", NULL,
                                (FXObject *)text, FXTerminal::ID_RESUME);
    new FXMenuCommand(breakMenu, "&Stop/Go\tCtl-Z", NULL,
                                (FXObject *)text, FXTerminal::ID_STOP);
    new FXMenuCommand(breakMenu, "&Discard Output\tCtl-O", NULL,
                                (FXObject *)text, FXTerminal::ID_DISCARD);

    new FXMenuTitle(main_menu_bar, "B&reak", NULL, breakMenu);

    helpMenu = new FXMenuPane(main_window);
    new FXMenuCommand(helpMenu, "&Help\tF1\tHelp", NULL,
                                (FXObject *)text, FXTerminal::ID_HELP);
#ifndef WIN32
#if !defined MACINTOSH || !defined MAC_FRAMEWORK
    new FXMenuCommand(helpMenu, "&Select Browser\t\tSelect Browser", NULL,
                                (FXObject *)text, FXTerminal::ID_BROWSER);
#endif
#endif
    new FXMenuCommand(helpMenu, "&About\t\tAbout", NULL,
                                (FXObject *)text, FXTerminal::ID_ABOUT);

    new FXMenuTitle(main_menu_bar, "Help", NULL, helpMenu, LAYOUT_RIGHT);

    text->setEditable(FALSE);
    text->setStyled(TRUE);

    text->argc = argc;
    text->argv = argv;

    strcpy(mid_stuff, programName);
    main_window->setTitle(programName);

    application_object->create();

// Selecting the font may involve measuring font sizes etc which may
// need the font creating...

    FXFont *font1 = selectFont(fontname, fontsize,
        fontweight, fontslant, fontencoding, fontsetwidth, fonthints);
    font1->create();
    text->setFont(font1);

    if (screenw == 0) text->onCmdResetWindow(NULL, 0, NULL);

    text->onCmdHome(NULL, 0, NULL); // actually just to grab the focus!
//
// I will iconify the window AFTER I have adjusted its size since I do not
// want to end up with a size that is silly and based on just an icon!
// Also somewhere (and I now do not remember where) I picked up the idea
// that minimizing twice over was a good idea...
//
#if INT_VERSION(FOX_MAJOR,FOX_MINOR,0)==INT_VERSION(1,0,0)
    if (windowed < 0) 
    {   main_window->iconify();
        main_window->iconify();
    }
#else
    if (windowed < 0)
    {   main_window->minimize();
        main_window->minimize();
    }
#endif

    text->setupShowMath();

    main_window->show();
#ifdef WIN32
    DWORD threadId;
    thread1 = CreateThread(NULL,  // security attributes
                           0,     // stack size
                           worker_thread,
                           (void *)text,
                           0,     // flags
                           &threadId); // Essential for Me/98/95
    if (thread1 == NULL)
    {   fprintf(stderr, "Fatal error attempting to create a thread\n");
        application_object->exit(1);
        exit(1);
    }
#else
    if (pthread_create(&thread1, NULL, worker_thread, (void *)text))
    {   fprintf(stderr, "Fatal error attempting to create a thread\n");
        application_object->exit(1);
        exit(1);
    }
#endif

// Once a second I will try to flush any output buffers. But do not start
// that until everything else is more or less going!
#if FOX_MAJOR==1 && FOX_MINOR==0
    timer = application_object->addTimeout(1000,
               (FXObject *)text, FXTerminal::ID_TIMEOUT);
#else
#if FOX_MAJOR==1 && (FOX_MINOR==1 || FOX_MINOR==2)
    timer = application_object->addTimeout((FXObject *)text,
                FXTerminal::ID_TIMEOUT, 1000, NULL);
#else
    application_object->addTimeout((FXObject *)text,
                FXTerminal::ID_TIMEOUT, 1000, NULL);
#endif
#endif

    return application_object->run();
}


#ifdef HAVE_LIBFOX

FXFont *selectFont(const char *name, int size,
    int weight, int slant, int encoding, int setwidth, int hints)
{
// I start with a simplistic hypothesis that the width if characters in
// fonts here will scale linearly with their point size.
    int pointSize = 200;
    if (size > 0) pointSize = size;
    FXFontDesc fd;
    strncpy(fd.face, name, sizeof(fd.face));
    fd.size = pointSize;    // NB decipoints not points
    fd.weight = weight;
    fd.slant = slant;
    fd.encoding = encoding;
    fd.setwidth = setwidth;
    fd.flags = hints;
    FXFont *f = new FXFont(application_object, fd);
// I really hope that I have a fixed-with font here!
    f->create();
// If the registry had told me what size font to use then I will just
// stick with that. If not then I will have been hended in a negative
// size specifier and I will need to make a guess here.
   if (size > 0) return f;
// My font-size selection will be based in font and screen widths.
    int w = f->getFontWidth();
// Work out what proportion of my screen's width would be filled by
// 80 characters in this font.
    double fill = (80.0*(double)w)/(double)rootWidth;
    f->getFontDesc(fd);
    pointSize = fd.size;   // in deci-points for the one actually found
// I am now going to suggest a font size based on what I will count as
// a half-sensible way to place a window on the screen... Well my idea
// here is that on larger screens you will use a larger font but still fill up
// less of the total screen. On screens down at 800*600 and below I will
// want to use almost all of the screen, while for 1280*1024, 1600*1200 etc
// I will only use half the available width. This must be a matter where
// taste comes in, so others may have different views.
    double bestSize;
    if (rootWidth > 1100)     // try to 60% fill the root width
        bestSize = (double)pointSize*0.6/fill;
    else if (rootWidth > 900) // try to fill 0.75 the root width
        bestSize = (double)pointSize*0.75/fill;
                                // finally on small roots use almost all.
    else bestSize = (double)pointSize*0.9/fill;
    pointSize = (int)(0.5 + bestSize);
// I think that I will avoid over-teeny fonts come what may. So I will
// increate the selected size so that I always use at least 8pt.
    if (pointSize < 80) pointSize = 80;
    fd.size = pointSize;
    delete f;
// Finally create a fond that is the size that may make sense!
    f = new FXFont(application_object, fd);
    f->create();
    return f;
}

#endif /* HAVE_LIBFOX */


void fwin_callback_on_delay(delay_callback_t *f)
{
    delay_callback = f;
}

void fwin_callback_to_interrupt(interrupt_callback_t *f)
{
    interrupt_callback = f;
}

#ifdef HAVE_LIBFOX

#ifndef WIN32
#include <pthread.h>
#endif

#endif // HAVE_LIBFOX

extern "C" {

//
// The "curses" header is a very fine example of the trouble that can be
// generated by mixing up name-spaces. And the way that C code can be
// less than perfect for use from C++.  On my system at least a header
// file here goes
//   #define clear() wclear(stdsrc)
// but now consider a class that has a class-scope method called clear -
// calls to it get mapped onto wclear in a way that is utterly horrid.
// 
// I include the curses headers AFTER all other headers to avoid mixups
// in declarations, and sit and hope! If I get very worried I will just
// migrate all uses of curses into a separate file where I will provide
// myself with C++ wrappers...
//

#ifdef MOUSE_MOVED
#undef MOUSE_MOVED
#endif

#ifdef HAVE_LIBCURSES
#include <curses.h>
#define HAVE_CURSES 1
#else
#ifdef HAVE_LIBNCURSES
#include <ncurses.h>
#define HAVE_CURSES 2
#endif
#endif

}

static int returncode = 0;


#ifdef HAVE_LIBFOX

// Here is the worker thread.

static FXTerminal *term;

#ifdef WIN32
DWORD __stdcall worker_thread(void *arg)
#else
void *worker_thread(void *arg)
#endif
{
// The curious-looking delegation here is to ensure that the signature
// of the thread is a simple C-style one rather than a C++ member
// function. That is what is required for thread creation.
    return ((FXTerminal *)arg)->worker_thread(arg);
}

// I can signal the GUI thread by writing a byte to a pipe that it
// watches. The byte that I write can be an indication of what
// event I wanted to report

void wake_up_terminal(int n)
{
#ifdef WIN32
    event_code = n;
    SetEvent(pipedes);
#else
    char pipe_data[1];
    pipe_data[0] = n;
    if (write(pipedes[PIPE_WRITE_PORT], pipe_data, 1) != 1)
    {   fprintf(stdout, "Fatal error attempting to write to a pipe\n");
        application_object->exit(1);
        exit(1);
    }
#endif
}

#ifdef WIN32
DWORD FXTerminal::worker_thread(void *arg)
#else
void *FXTerminal::worker_thread(void *arg)
#endif
{
    signal(SIGINT, sigint_handler);
#ifdef SIGBREAK
    signal(SIGBREAK, sigbreak_handler);
#endif
    term = (FXTerminal *)arg;

// set proper initial state for all the locks.
    LockMutex(term->mutex1);
    LockMutex(term->mutex2);
    term->sync_even = 1;

// run the application code.
    returncode = fwin_main(term->argc, term->argv);
    wake_up_terminal(WORKER_EXITING);
#ifdef WIN32
    ExitThread(returncode);
    return returncode;
#else
    pthread_exit(&returncode);      // between these two lines I think
    return &returncode;             // I must certainly exit!
#endif
}

int fwin_windowmode()
{
    int r = 0;
    r |= FWIN_WITH_TERMED;
    if (windowed) r |= FWIN_IN_WINDOW;
    return r;
}

#endif // HAVE_LIBFOX

void fwin_exit(int return_code)
{
#ifdef HAVE_LIBFOX
    if (windowed)
    {   wake_up_terminal(FXTerminal::WORKER_EXITING);
        returncode = return_code;
#ifdef WIN32
        ExitThread(returncode);
#else
        pthread_exit(&returncode);
#endif
    }
#endif
    if (using_termed)
    {   input_history_end();
        term_close();
    }
    exit(return_code);
}


void fwin_minimize()
{
#ifdef HAVE_LIBFOX
    if (!windowed) return;
    fflush(stdout);
    wake_up_terminal(FXTerminal::MINIMIZE_MAIN);
// I do not feel any need to get the threads into lockstep here.
#endif
}

#ifdef HAVE_LIBFOX

void fwin_restore()
{
    if (!windowed) return;
    fflush(stdout);
    wake_up_terminal(FXTerminal::RESTORE_MAIN);
}

void fwin_putchar(int c)
{
    if (!windowed)
    {
#ifdef RAW_CYGWIN
        if (c == '\n') putchar('\r');
#endif
        putchar(c);
        return;
    }
// Observe that since I am concerned (at least a bit) with performance
// I buffer characters here so that the cost of inter-thread communication
// is not suffered. But every other second (or so) the user-interface thread
// will be worken up and will flush the buffer for me, so the user ought to
// be given a tolerable experience/
    int nxt = term->fwin_in + 1;
    if (nxt == FWIN_BUFFER_SIZE) nxt = 0;
    if (nxt == term->fwin_out ||
        term->pauseFlags & PAUSE_PAUSE) fwin_ensure_screen();
// Note and BEWARE here that fwin_ensure_screen() can synchronize the
// worker and interface threads and update fwin_in. Observe also that I
// can generate a screen update if ^S has been hit.
    term->fwin_buffer[term->fwin_in] = c;
    nxt = term->fwin_in + 1;
    if (nxt == FWIN_BUFFER_SIZE) nxt = 0;
    term->fwin_in = nxt;
    FILE *f = term->logfile;
    if (f != NULL) putc(c, f);
}

static void fwin_ensure_buffer_space();

void fwin_puts(const char *s)
{
    if (!windowed)
    {
#ifdef RAW_CYGWIN
        while (*s != 0) fwin_putchar(*s++);
#else
        puts(s);
#endif
        return;
    }
    int len = strlen(s);
    while (len > 0)
    {   int n = len;
        if (n > SPARE_FOR_VFPRINTF) n = SPARE_FOR_VFPRINTF;
        if (term->fwin_in+SPARE_FOR_VFPRINTF >= FWIN_BUFFER_SIZE ||
            term->pauseFlags & PAUSE_PAUSE)
            fwin_ensure_buffer_space();
        memcpy(&term->fwin_buffer[term->fwin_in], s, n);
        FILE *f = term->logfile;
        if (f != NULL) fwrite(s, 1, n, f);
        term->fwin_in += n;
        len -= n;
    }
}


void 
#ifdef _MSC_VER
            __cdecl
#endif
            fwin_printf(const char *fmt, ...)
{
    va_list a;
    va_start(a, fmt);
    if (!windowed)
    {
#ifdef RAW_CYGWIN
/* NOT reconstructed yet @@@ */
        vfprintf(stdout, fmt, a);
        va_end(a);
#else
        vfprintf(stdout, fmt, a);
        va_end(a);
#endif
        return;
    }
    if (term->fwin_in+SPARE_FOR_VFPRINTF >= FWIN_BUFFER_SIZE ||
        term->pauseFlags & PAUSE_PAUSE)
        fwin_ensure_buffer_space();
// The code here is inherently UNSAFE since vsprintf does not give
// any protection against buffer overflow and there is no universally
// available equivalent that is safe. I arrange that if the printf call
// generates at most SPARE_FOR_VFPRINTF characters I am OK.
//
// Well C99 provides vsnprintf so if that is available I ought to use it...
//
#ifdef HAVE_VSNPRINTF
    vsnprintf(&term->fwin_buffer[term->fwin_in], SPARE_FOR_VFPRINTF, fmt, a);
#else
    vsprintf(&term->fwin_buffer[term->fwin_in], fmt, a);
#endif
// Cautious about portability and old libraries, and aware of values that
// vsnprintf may return when the data does not fit, I ignore the values
// of the above functions and adjust the data pointers by hand.
    int n = strlen(&term->fwin_buffer[term->fwin_in]);
    FILE *f = term->logfile;
    if (f != NULL) fwrite(&term->fwin_buffer[term->fwin_in], 1, n, f);
    term->fwin_in += n;
    va_end(a);
}

void fwin_vfprintf(const char *fmt, va_list a)
{
    if (!windowed)
    {
#ifdef RAW_CYGWIN
/* Not reconstructed yet @@@ */
        vfprintf(stdout, fmt, a);
#else
        vfprintf(stdout, fmt, a);
#endif
        return;
    }
// see comments above.
    if (term->fwin_in+SPARE_FOR_VFPRINTF >= FWIN_BUFFER_SIZE ||
        term->pauseFlags & PAUSE_PAUSE)
        fwin_ensure_buffer_space();
#ifdef HAVE_VSNPRINTF
    vsnprintf(&term->fwin_buffer[term->fwin_in], SPARE_FOR_VFPRINTF, fmt, a);
#else
    vsprintf(&term->fwin_buffer[term->fwin_in], fmt, a);
#endif
    int n = strlen(&term->fwin_buffer[term->fwin_in]);
    FILE *f = term->logfile;
    if (f != NULL) fwrite(&term->fwin_buffer[term->fwin_in], 1, n, f);
    term->fwin_in += n;
}

static void regain_lockstep()
{
// This looks jolly ugly to me - I need to talk to others who are more
// experienced in concurrency to see if I can improve it.
    if (term->sync_even)
    {   UnlockMutex(term->mutex1);
        LockMutex(term->mutex3);
        UnlockMutex(term->mutex2);
        LockMutex(term->mutex4);
    }
    else
    {   UnlockMutex(term->mutex3);
        LockMutex(term->mutex1);
        UnlockMutex(term->mutex4);
        LockMutex(term->mutex2);
    }
}

const char *fwin_maths = NULL;

void fwin_showmath(const char *s)
{
    if (!windowed) return;
    fwin_ensure_screen();   // get regular text up to date first.
    fwin_maths = s;
    LockMutex(term->pauseMutex);
// here I have to do real inter-thread communication.
    if (delay_callback != NULL) (*delay_callback)(1);
    wake_up_terminal(FXTerminal::SHOW_MATH);
// here I need to wait until the signal that I just sent has been received
// and processed.
    regain_lockstep();
    if (delay_callback != NULL) (*delay_callback)(0);
    UnlockMutex(term->pauseMutex);
}


void fwin_ensure_screen()
{
    if (!windowed)
    {   fflush(stdout);
        return;
    }
    if (term->fwin_in == term->fwin_out) return;
    LockMutex(term->pauseMutex);
// here I have to do real inter-thread communication.
    if (delay_callback != NULL) (*delay_callback)(1);
    wake_up_terminal(FXTerminal::FLUSH_BUFFER);
// here I need to wait until the signal that I just sent has been received
// and processed.
    regain_lockstep();
    if (delay_callback != NULL) (*delay_callback)(0);
    UnlockMutex(term->pauseMutex);
}

static void fwin_ensure_buffer_space()
{
    if (!windowed) return;
    if (term->fwin_in == term->fwin_out) return;
    LockMutex(term->pauseMutex);
    if (delay_callback != NULL) (*delay_callback)(1);
    wake_up_terminal(FXTerminal::FLUSH_BUFFER);
    if (term->sync_even)
    {   UnlockMutex(term->mutex1);
        LockMutex(term->mutex3);
        term->fwin_in = term->fwin_out = 0;
        UnlockMutex(term->mutex2);
        LockMutex(term->mutex4);
    }
    else
    {   UnlockMutex(term->mutex3);
        LockMutex(term->mutex1);
        term->fwin_in = term->fwin_out = 0;
        UnlockMutex(term->mutex4);
        LockMutex(term->mutex2);
    }
    if (delay_callback != NULL) (*delay_callback)(0);
    UnlockMutex(term->pauseMutex);
}

#endif // HAVE_LIBFOX

#ifdef CSL
#ifdef HAVE_LIBFOX
extern "C"
{
extern void review_switch_settings();
}
static int update_next_time = 0;
#endif
#endif

#ifdef HAVE_LIBFOX

int fwin_getchar()
{
    if (!windowed) return fwin_plain_getchar();
// In general I have a line of stuff ready sitting in a buffer. So on
// most calls to here I can just return what is in it.
    if (term->inputBufferP < term->inputBufferLen)
        return term->inputBuffer[term->inputBufferP++];
// Now however a new line of input is needed, so I have to request it from
// the user-interface thread.
#ifdef CSL
    if (update_next_time)
    {   review_switch_settings();
        update_next_time = 0;
    }
#endif
    if (delay_callback != NULL) (*delay_callback)(1);
    wake_up_terminal(FXTerminal::REQUEST_INPUT);
// Wait until the signal that I just sent has been received
// and processed.
    regain_lockstep();
    if (delay_callback != NULL) (*delay_callback)(0);
// I will try a convention that if inputBufferLen is zero that indicates
// a dodgy state. Eg the user is sending an EOF or interrupt.
    int n = term->inputBufferLen;
    if (n == 0) return EOF;
    const char *p = &term->inputBuffer[term->inputBufferP];
#ifdef CSL
    while (n>0 && isspace(*p))
    {   n--;
        p++;
    }
    if (n>12 && strncmp(p, "load_package", 12) == 0)
        update_next_time = 1;
#endif
    int ch = term->inputBuffer[term->inputBufferP++];
    if (ch == (0x1f & 'D')) return EOF;
    else return ch;
}


void fwin_set_prompt(const char *s)
{
    strncpy(fwin_prompt_string, s, sizeof(fwin_prompt_string));
    fwin_prompt_string[sizeof(fwin_prompt_string)-1] = 0;
    if (!windowed) return;
    wake_up_terminal(FXTerminal::SET_PROMPT);
    regain_lockstep();
}

void fwin_menus(char **modules, char **switches)
{
#ifdef CSL
    if (!windowed) return;
    modules_list = modules;
    switches_list = switches;
    wake_up_terminal(FXTerminal::SET_MENUS);
    regain_lockstep();
#endif
}

void fwin_refresh_switches(char **switches, char **packages)
{
#ifdef CSL
    if (!windowed) return;
    switches_list = switches;
    modules_list = packages;
    wake_up_terminal(FXTerminal::REFRESH_SWITCHES);
    regain_lockstep();
#endif
}

static char left_stuff[32] = "",
            right_stuff[32] = "";
char mid_stuff[32] = "", full_title[90] = "";

#ifdef USE_A0_SPACER
#define SPACER_CHAR 0xa0
#else
#define SPACER_CHAR 0x20
#endif

static void rewrite_title_bar()
{
// Just at present this does not cope with cases where the width of the window
// has been changed...
    int ll = strlen(left_stuff),
        lm = strlen(mid_stuff),
        lr = strlen(right_stuff);
    int i, j;
    for (i=0; i<80; i++) full_title[i] = SPACER_CHAR;
    strncpy(full_title, left_stuff, ll);
    j = 80 - strlen(right_stuff);
    strncpy(&full_title[j], right_stuff, lr);
    j = 40-(lm/2);
    strncpy(&full_title[j], mid_stuff, lm);
    full_title[80] = 0;
    wake_up_terminal(FXTerminal::REFRESH_TITLE);
    regain_lockstep();
}

#endif

void fwin_acknowledge_tick()
{
#ifdef HAVE_LIBFOX
// This is to do with my handling of "^Z" to suspend the computation.
// If the user enters ^Z I lock the pause mutex and then send a "TICK".
// The user is expected to notice it and respond here - and hence get
// suspended.
    if (!windowed) return;
    LockMutex(term->pauseMutex);
    UnlockMutex(term->pauseMutex);
#endif
}

#ifdef HAVE_LIBFOX

void fwin_report_left(const char *msg)
{
    if (!windowed) return;
    strncpy(left_stuff, msg, 31);
    left_stuff[31] = 0;
    rewrite_title_bar();
}

void fwin_report_mid(const char *msg)
{
    if (!windowed) return;
    strncpy(mid_stuff, msg, 31);
    mid_stuff[31] = 0;
    rewrite_title_bar();
}

void fwin_report_right(const char *msg)
{
    if (!windowed) return;
    strncpy(right_stuff, msg, 31);
    right_stuff[31] = 0;
    rewrite_title_bar();
}

void fwin_set_help_file(const char *key, const char *path)
{
    if (!windowed) return;
    printf("fwin_set_help_file called\n");
    fflush(stdout);
}

#endif

// end of FXWorker.cpp

