Artifact 2a1b33d6743d18b7917c621c897f54a4ac18ef74bcc574fc00bebd0962f7328d:
- Executable file
r38/lisp/csl/cslbase/FXWorker.cpp
— part of check-in
[f2fda60abd]
at
2011-09-02 18:13:33
on branch master
— Some historical releases purely for archival purposes
git-svn-id: https://svn.code.sf.net/p/reduce-algebra/code/trunk/historical@1375 2bfe0521-f11c-4a00-b80e-6202646ff360 (user: arthurcnorman@users.sourceforge.net, size: 36938) [annotate] [blame] [check-ins using] [more...]
// // "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