#error this version is no longer supported. The file is left in case it ever proves a useful prototype for anything else. /* sysarm.c Copyright (C) 1989-93 Codemist Ltd */ /* * Acorn Archimedes-specific code */ /* Signature: 4f2fd4ee 07-Mar-2000 */ #include <stdarg.h> #include <string.h> #include <ctype.h> #include "bbc.h" #include "akbd.h" #include "flex.h" #include "os.h" #include "wimp.h" #include "wimpt.h" #include "werr.h" #include "win.h" #include "menu.h" #define template acorn_template /* To avoid clash with C++ */ #include "template.h" #include "dbox.h" #include "txt.h" #include "saveas.h" #include "event.h" #include "res.h" #include "resspr.h" #include "baricon.h" #include "colourtran.h" #include "xferrecv.h" #include "xfersend.h" #include "sprite.h" #include "msgs.h" /* #include <swis.h> */ #define Wimp_SendMessage 0x0400e7 #define OS_EvaluateExpression 0x00002d #define OS_Claim 0x00001f #define OS_Release 0x000020 #define OS_DelinkApplication 0x00004d #define OS_RelinkApplication 0x00004e #define Hourglass_On 0x0406c0 #define Hourglass_Off 0x0406c1 #define Wimp_ReadSysInfo 0x0400f2 #include "machine.h" #include "version.h" #include "tags.h" #include "cslerror.h" #include "externs.h" #include "arith.h" #include "read.h" #include "entries.h" #ifdef TIMEOUT #include "timeout.h" #endif static void get_home_directory(char *b, int length); static void get_users_home_directory(char *b, int length); #include "filename.c" int create_directory(char *filename, char *old, size_t n) { process_file_name(filename, old, n); if (*filename == 0) return 1; return mkdir(filename); } int delete_file(char *filename, char *old, size_t n) { process_file_name(filename, old, n); if (*filename == 0) return NULL; return remove(filename); } CSLbool file_exists(char *filename, char *old, size_t n, char *tt) { os_filestr b; time_t t; unsigned int load, exec, w; process_file_name(filename, old, n); if (*filename == 0) return NO; b.action = 5; b.name = filename; if (os_file(&b) != 0) return NO; load = b.loadaddr; exec = b.execaddr; if ((load & 0xfff00000) != 0xfff00000) return NO; load = load & 0xff; /* Now I need to divide the datestamp by 100 */ w = (load << 16) | ((exec >> 16) & 0xffff); exec = (exec & 0xffff) | ((w % 100) << 16); w = w / 100; t = (w << 16) | (exec / 100); strcpy(tt, ctime(&t)); return YES; } /* * This is a dummy definition of get_truename, here so that everything * will link. Both the calling convention used here and the exact * meaning and implementation may be under gentle review! */ char *get_truename(char *filename, char *old, size_t n) { char *w; process_file_name(filename, old, n); if (*filename == 0) { aerror("truename"); return NULL; } w = (char *)malloc(1+strlen(filename)); if (w == NULL) return w; strcpy(w, filename); return w; } char *my_getenv(char *s) { return getenv(s); } static void delink(void), relink(void); int my_system(char *s) { int w; delink(); w = system(s); relink(); return w; } /* * RISCOS does not support the idea of home directories for * users, so in case anybody still wants to use the notation "~" that * would indicate a home directory under Unix I implement something * in terms of environment variables. */ static void get_home_directory(char *b, int length) { char *w = my_getenv("home"); if (w != NULL) strcpy(b, w); else strcpy(b, "@"); } static void get_users_home_directory(char *b, int length) { char *w, h[LONGEST_LEGAL_FILENAME]; sprintf(h, "home$%s", b); w = my_getenv(h); if (w != NULL) strcpy(b, w); else strcpy(b, "@"); } /* * The following structure MUST match the layout of a FILE as used by * the library that is linked with. */ #define FILEHANDLE int struct FILE_COPY { int icnt; /* two separate _cnt fields so we can police ... */ unsigned char *ptr; int ocnt; /* ... restrictions that read/write are fseek separated */ int flag; unsigned char *base; /* buffer base */ int file; /* RISCOS/Arthur/Brazil file handle */ long pos; /* position in file */ int bufsiz; /* maximum buffer size */ int signature; /* used with temporary files */ unsigned char lilbuf[2]; /* single byte buffer for them that want it */ /* plus an unget char is put in __lilbuf[1] */ long lspos; /* what __pos should be (set after lazy seek)*/ unsigned char *extent; /* extent of writes into the current buffer */ int buflim; /* used size of buffer */ int icnt_save; /* after unget contains old icnt */ int ocnt_save; /* after unget contains old ocnt */ }; static int pipe_ptr = 0; static unsigned char pipe_buffer[20+256]; static CSLbool under_wimp = NO; FILE *my_popen(char *cmd, char *dirn) { if (!under_wimp) return NULL; pipe_ptr = 20; return (FILE *)1; } void my_pipe_putc(int c, FILE *f) { if (pipe_ptr >= 20 && pipe_ptr < sizeof(pipe_buffer)-1) { if (c == '\n') my_pipe_flush(f); else pipe_buffer[pipe_ptr++] = c; } } void my_pipe_flush(FILE *f) { if (pipe_ptr > 20 && pipe_ptr < sizeof(pipe_buffer)-1) { int32 *w = (int32 *)pipe_buffer; remove_ticker(); delink(); pipe_buffer[pipe_ptr] = 0; w[0] = (pipe_ptr + 4) & ~3; w[1] = 0; w[2] = 0; w[3] = 0; /* An original message, this one */ w[4] = 0x0c1202; /* A magic number for GNUPLOT, I am told! */ os_swi3(Wimp_SendMessage, 17, (int)w, 0); relink(); add_ticker(); /* here w[2] is my reference for the message */ accept_tick(); } pipe_ptr = 20; return; } void my_pclose(FILE *f) { return; } int batchp() { return (((struct FILE_COPY *)stdin)->file != 0); } /* * The following function controls memory allocation policy */ int32 ok_to_grab_memory(int32 current) { return 3*current + 2; } /* * The next procedure is responsible for establishing information about * where the main checkpoint image should be recovered from, and where * and fasl files should come from. */ static char appname[16] = "!CSL"; static char CSL_version_string[64]; static char LOADSTART[16] = "(rdf \""; static char LOADEND[16] = "\")"; char *find_image_directory(int argc, char *argv[]) { char image[LONGEST_LEGAL_FILENAME]; char pgmname[LONGEST_LEGAL_FILENAME]; char *w; CSLbool riscos_application = NO; int i; /* * For RISCOS there are a number of possibilities that can support * either window-launched uses or command-line driven uses of this code. * * When launched from the window manager I will be called with a full * path name for by executable binary available in argv[0], and this will * always be of the form xxx.yyy.!zzz.!runimage * In this case I will use * xxx.yyy.!zzz.-runimage * as the name for my checkpoint file, and I will treate zzz as the name * of the application, using that name to control what appears in menus * and what sprites I use to display things. * * The second possibility is that I am called from a command-line. In that * case if the command line request was of the form * !zzz.!qqq * then I view zzz as the application name, and use !zzz.-qqq as the * checkpoint file. The use of things like xxx.yyy.!zzz.!qqq will be * supported in this case too. * * A final strategy is used if the command line shows only one component * of a file name, or if either of the two final components in the path * fail to start with '!'. In this case if the final component of the * name (i.e. of argv[0]) is xxx or !xxx then I look up the value of a shell * variable xxx$dir, and if look for a file called "-runimage" in that * directory. If argv[0] is null I take it to be "csl" and if the * environment variable indicated here is unset I look for -runimage in * the current directory. In such cases the application name is taken as xxx. */ if (argc > 0 && argv[0] != NULL) { int j, r0, r1; w = argv[0]; sprintf(pgmname, "\"%s\"\n", w); /* * The following line expands any shell-variables mentioned in the * command that launched me. E.g. typically from the window manager * the raw command-line will have been * <Obey$Dir>.!RunImage ... * and I need to expand <Obey$Dir> */ os_swi3r(OS_EvaluateExpression, (int)pgmname, (int)image, LONGEST_LEGAL_FILENAME, &r0, &r1, &i); if (r1 == 0) sprintf(image, "%d", i); else image[i] = 0; w = image; i = strlen(w); while (i > 0 && w[i-1] != '.') i--; j = i-1; while (j > 0 && w[j-1] != '.') j--; if (j >= 0 && w[i] == '!' && w[j] == '!') { w[i] = '-'; if (i > j+16) i = j+16; /* Truncate name if necessary */ sprintf(appname, "%.*s", i-j-1, &w[j]); riscos_application = YES; } else { strcpy(pgmname, &w[i]); strcpy(image, pgmname); } } else strcpy(image, "!csl"); /* even argv[0] is not available! */ if (!riscos_application) { /* * If I get here then image contains the final component of the application * name, and I must use it to build the name of an environment variable to * check. */ if (image[0] == '!') { sprintf(pgmname, "%s$dir", &image[1]); sprintf(appname, "%.15s", image); } else { sprintf(pgmname, "%s$dir", image); sprintf(appname, "!%.15s", image); } w = my_getenv(pgmname); if (w == NULL) w = "@"; sprintf(image, "%s.-runimage", w); } w = appname; /* Force application name into upper case */ while ((i = *w) != 0) { if (islower(i)) i = toupper(i); *w++ = i; } if (strcmp(appname, "!CSL") != 0) { strcpy(LOADSTART, "in \""); strcpy(LOADEND, "\";"); } /* * I copy from local vectors into malloc'd space to hand my * answer back. */ w = (char *)malloc(1+strlen(image)); /* * The error exit here seem unsatisfactory... */ if (w == NULL) { fprintf(stderr, "\n+++ Panic - run out of space\n"); exit(EXIT_FAILURE); } strcpy(w, image); sprintf(CSL_version_string, "%s %s (%s)", appname+1, VERSION, __DATE__); return w; } #ifdef PROFILED /* * The following are really part of the ARM C library, but are included here * first as prototypes in case the ideas are useful with other architectures. * * Support for gathering various sorts of information that shows where time * may be going in this system. Un Unix machines try prof/monitor and friends. */ extern void *__RO_Base, *__RO_Limit; extern void _count(void); extern void _count1(void); typedef union count_position /* * This defines the format of the word that follows on from a call * to _count1(). The related constant values are related to the way that * file-name decoding tables are packed away. */ { int i; struct s { unsigned int posn:12, line:16, file:4; } s; } count_position; #define file_name_map_start 0xfff12340 /* Magic number */ #define file_name_map_end 0x31415926 /* Magic number */ #define word_roundup(s) ((char *)(((int)s + 3) & (~3))) static char *find_file_map(int p) { int i, w; char *s; while (((w = *(int *)p) & 0xfffffff0) != file_name_map_start) { if (p >= (int)__RO_Limit) return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; p += 4; } s = (char *)(p + 4); for ( i = 0; i<=(w & 0xf); i++) { s += 1 + strlen(s); s = word_roundup(s); } if (*(int *)s != file_name_map_end) return find_file_map((int)s); return (char *)(p + 4); } void show_counts(void) { int count = (int)_count; /* address of the function as an int */ int p, onthisline = 0, w1 = 0, w2 = 0; myprintf("\nCounts from %p to %p\n", __RO_Base, __RO_Limit); for (p = (int)__RO_Base; p<(int)__RO_Limit; p += 4) { int w = *(int *)p; if ((w & 0xff000000) == 0xeb000000) /* Unconditional BL instruction */ { int dest = (p + 8 + 4*(w & 0x00ffffff)) & 0x03fffffc; if (dest == count) { myprintf("%.6x: %-10u ", p, *(int *)(p + 4)); if (onthisline == 3) onthisline = 0, myprintf("\n"); else onthisline++, myprintf(" "); } } if ((w & 0xfffe0000) == 0xe92c0000 && /* STMFD sp!, sp = r12 or r13 */ w1 == 0xe1a0b00c && /* MOV ip, sp @@@@@ */ (w2 & 0xffff0000) == 0xff000000) { char *name = (char *)(p - 8 - (w2 & 0xffff)); int len = 0; char *temp = name; /* I do not use strlen() here to keep the library more modular */ while (*temp++ != 0) len++; if (len >= 10 && onthisline == 3) { onthisline = 0; myprintf("\n"); } myprintf( "%.6x: %s", p - 4, name); len = 11 - strlen(name); while (len < 0) len += 20, onthisline++; while (len > 0) len--, myprintf(" "); if (onthisline >= 3) onthisline = 0, myprintf("\n"); else onthisline++, myprintf(" "); } w2 = w1; w1 = w; } if (onthisline != 0) myprintf(" "); } void write_profile(char *filename) { /* Create a (binary) file containing execution profile information for */ /* the current program. The format is eccentric, and must be kept in step */ /* with (a) parts of armgen.c that generate code that collects statistics */ /* and (b) code in misc.c that reads in the binary file created here and */ /* displays the counts attached to a source listing of the original code. */ int count1 = (int)_count1; int p, w1 = 0, w2 = 0, pass, nfiles = 0, namebytes = 0, ncounts = 0; int global_name_offset[256]; char *global_file_map[256]; /* Limits total number of files allowed */ FILE *map_file = fopen(filename, "wb"); char *file_map; if (map_file == NULL) { myprintf("\nUnable to open %s for execution profile log\n", filename); return; } for (pass = 1; pass <=2; pass++) { if (pass == 2) /* Write file header indicating size of sub-parts */ { fwrite("\xff*COUNTFILE*", 4, 3, map_file); fwrite(&namebytes, 4, 1, map_file); fwrite(&nfiles, 4, 1, map_file); fwrite(&ncounts, 4, 1, map_file); for (p = 0; p < nfiles; p++) { char *ss = global_file_map[p]; int len = 1 + strlen(ss); len = ((len + 3) & (~3)) / 4; fwrite(ss, 4, len, map_file); } for (p = 0; p < nfiles; p++) fwrite(&global_name_offset[p], 4, 1, map_file); } file_map = NULL; for (p = (int)__RO_Base; p<(int)__RO_Limit; p += 4) { int w = *(int *)p; if ((w & 0xff000000) == 0xeb000000) /* BL instruction */ { int dest = (p + 8 + 4*(w & 0x00ffffff)) & 0x03fffffc; if (dest == count1) { count_position k; int i; char *s; if (file_map == NULL || (int)file_map <= p) file_map = find_file_map(p+12); s = file_map; k.i = *(int *)(p + 8); for (i = 0; i<k.s.file; i++) { s += 1 + strlen(s); s = word_roundup(s); } if (pass == 1) { int i; for (i = 0;;i++) { if (i >= nfiles) { global_name_offset[nfiles] = namebytes; global_file_map[nfiles++] = s; namebytes += 1 + strlen(s); namebytes = (namebytes + 3) & (~3); break; } else if (strcmp(s,global_file_map[i]) ==0) break; } ncounts++; } else { int i; for (i = 0; strcmp(s, global_file_map[i]) !=0; i++); fwrite((int *)(p + 4), 4, 1, map_file); i = (k.s.line & 0xffff) | (i << 16); fwrite(&i, 4, 1, map_file); } p += 8; } } w2 = w1; w1 = w; } } fwrite("\xff*ENDCOUNT*\n", 4, 3, map_file); /* Trailer data */ fclose(map_file); myprintf("\nProfile information written to %s\n", filename); } #endif /* PROFILED */ /* * Wimp interfaces... */ static CSLbool CSL_window_open = NO; static menu Iconbar_menu = 0, Main_menu = 0; static txt session; static txt_marker session_marker; #define MInfo 1 /* Icon-bar menu items */ #define MQuit 2 #define Vfield 4 /* version number field in "about" box */ #define MMInt 1 /* main window menu items */ #define MMAbort 2 #define MMQuit 3 #define MMSave 4 #define MMSelAll 5 #define MMCopy 6 #define MMDelete 7 #define MMClear 8 #define BUFLEN 256 /* * Text window event handling */ static char *clipboard = NULL; static int clipboard_size = 0, clipboard_p, clipboard_n; typedef enum wimp_source_t { from_keyboard = 0, from_clipboard, from_file } wimp_source_t; static wimp_source_t wimp_source = from_keyboard; #define TTYBUF_SIZE 256 static CSLbool tty_ready = NO; static int tty_icount = 0; static char tty_ibuffer[TTYBUF_SIZE]; #ifdef TICK_STREAM extern void wimp_tick(void); /* * wimp_tick behaves roughly as shown in the following code, but is * in assembly code (in asmarm.s) since it is an interrupt routine and I * am required to adhere to rigid register-use etc conventions... * * void wimp_tick() * { * polltick_pending = YES; * if (tick_pending) return; * else if (already_in_gc) tick_on_gc_exit = YES; * else * { Lisp_Object nil = C_nil; * tick_pending = YES; * saveheaplimit = heaplimit; * heaplimit = fringe; * savevheaplimit = vheaplimit; * vheaplimit = vfringe; * savecodelimit = codelimit; * codelimit = codefringe; * savestacklimit = stacklimit; * stacklimit = stackbase; * } * return; * } */ static CSLbool ticker_active = NO; void remove_ticker() { if (!ticker_active) return; #ifndef SOFTWARE_TICKS os_swi3(OS_Release, 0x1c, (int)wimp_tick, 0x91494530); #endif ticker_active = NO; } void add_ticker() { if (ticker_active) return; #ifdef SOFTWARE_TICKS countdown = SOFTWARE_TICKS; #else os_swi3(OS_Claim, 0x1c, (int)wimp_tick, 0x91494530); #endif ticker_active = YES; } #endif /* TICK_STREAM */ static char task_links[61]; static void delink() { os_swi2(OS_DelinkApplication, (int)&task_links[0], sizeof(task_links)); } static void relink() { os_swi1(OS_RelinkApplication, (int)&task_links[0]); } static int selstart = -1, selend = -1, dragstart = -1; static int done_something = 0; static BOOL enlarge_clipboard(char **bufp, int *lenp) { if (!flex_extend((flex_ptr)&clipboard, clipboard_size+128)) return FALSE; clipboard_size += 128; *bufp = clipboard; *lenp = clipboard_size; return TRUE; } static void clip_selection() { int p1 = txt_selectstart(session), p2 = txt_selectend(session), i; clipboard_p = clipboard_n = 0; wimp_source = from_clipboard; for (i=p1; i<p2; i++) { int c = txt_charat(session, i); if (clipboard_n >= clipboard_size) { if (!flex_extend((flex_ptr)&clipboard, clipboard_size+128)) return; clipboard_size += 128; } clipboard[clipboard_n++] = c; } } static void CSL_txthandler(txt t, void *handle) { txt_eventcode e; CSL_IGNORE(t); /* I only have one txt, so I use its global handle */ CSL_IGNORE(handle); e = txt_get(session); /* * An event is either a mouse one or it involves the keyboard. * The latter is taken to include all sorts of special cases such as * hits on various buttons. * For mouse hits I need to support region selection as well as just * positioning. */ if (e & txt_MOUSECODE) { int mousepos = e & 0xffffff; if (mousepos == 0xffffff) return; if (e & txt_MSELECT) { txt_setdot(session, mousepos); if ((e & txt_MSELOLD) && (dragstart >= 0)) { if (mousepos >= dragstart) selstart = dragstart, selend = mousepos; else selstart = mousepos, selend = dragstart; wimp_source = from_keyboard; } else dragstart = mousepos; } else if ((e & txt_MSELOLD) && (dragstart >= 0)) { if (mousepos >= dragstart) selstart = dragstart, selend = mousepos; else selstart = mousepos, selend = dragstart; dragstart = -1; wimp_source = from_keyboard; } else if ((e & txt_MEXTEND) && (selstart >= 0)) { if (2*mousepos <= (selstart+selend)) selstart = mousepos; else selend = mousepos; wimp_source = from_keyboard; } if (selstart != selend) txt_setselect(session, selstart, selend); else txt_setselect(session, 0, 0); /* Unset any selection */ done_something = 1; } else switch(e) { case txt_EXTRACODE + akbd_Sh + akbd_Ctl + akbd_UpK: /* Scroll up one line */ txt_movevertical(session, -1, 1); break; case txt_EXTRACODE + akbd_Sh + akbd_UpK: /* Scroll up one page */ txt_movevertical(session, -txt_visiblelinecount(session), 1); break; case txt_EXTRACODE + akbd_Sh + akbd_Ctl + akbd_DownK: /* Down one line */ txt_movevertical(session, 1, 1); break; case txt_EXTRACODE + akbd_Sh + akbd_DownK: /* Down one page */ txt_movevertical(session, txt_visiblelinecount(session), 1); break; case akbd_LeftK: /* left arrow key */ txt_movehorizontal(session, -1); break; case akbd_RightK: /* right arrow key */ txt_movehorizontal(session, 1); break; case akbd_UpK: /* up arrow key */ txt_movevertical(session, -1, 0); break; case akbd_DownK: /* down arrow key */ txt_movevertical(session, 1, 0); break; case txt_EXTRACODE + akbd_Fn +akbd_Sh + 2: /* Insert drag file */ { char *filename; int filetype, len; if ((filetype = xferrecv_checkinsert(&filename)) != -1) { len = strlen(LOADSTART) + strlen(filename) + strlen(LOADEND); if (tty_icount + len < TTYBUF_SIZE) { sprintf(&tty_ibuffer[tty_icount], "%s%s%s", LOADSTART, filename, LOADEND); tty_icount += len; txt_setdot(session, txt_size(session)); txt_insertstring(session, LOADSTART); txt_setdot(session, txt_size(session)); txt_insertstring(session, filename); txt_setdot(session, txt_size(session)); txt_insertstring(session, LOADEND); txt_setdot(session, txt_size(session)); } xferrecv_insertfileok(); } else if ((filetype = xferrecv_checkimport(&len)) != -1) { clipboard_p = 0; clipboard_n = xferrecv_doimport(clipboard, clipboard_size, enlarge_clipboard); xferrecv_insertfileok(); if (clipboard_n > 0) wimp_source = from_clipboard; txt_setdot(session, txt_size(session)); } } break; case txt_EXTRACODE + akbd_Fn + 127: /* close icon */ case akbd_Fn + akbd_Ctl + 2: /* Ctl-F2 also quits */ txt_dispose(&session); exit(0); break; case 127: /* backspace and ^H */ case 8: txt_setdot(session, txt_size(session)); txt_movehorizontal(session, -1); txt_delete(session, 1); if (tty_icount > 0) tty_icount--; break; default: if (e & txt_EXTRACODE) { sprintf(&tty_ibuffer[tty_icount], "<%.8x>", e); tty_ibuffer[tty_icount+10] = 0; txt_setdot(session, txt_size(session)); txt_insertstring(session, &tty_ibuffer[tty_icount]); txt_setdot(session, txt_size(session)); tty_icount += 10; } else if (e >= 256) wimp_processkey(e); /* give back to wimp */ else switch (e) { case 'C' & 0x1f: /* ^C = copy selection */ if (selstart == selend || selstart < 0) break; clip_selection(); break; case 'X' & 0x1f: /* ^X = delete selection */ if (selstart == selend || selstart < 0) break; txt_movemarker(session, &session_marker, txt_dot(session)); txt_setdot(session, txt_selectstart(session)); txt_delete(session, txt_selectend(session)-txt_selectstart(session)); txt_movedottomarker(session, &session_marker); wimp_source = from_keyboard; break; case 'Z' & 0x1f: /* ^Z = cancel selection */ selstart = selend = -1; txt_setselect(session, 0, 0); /* Unset any selection */ wimp_source = from_keyboard; break; default: if (e == 4 || e == 13 || e == 7 || e == 0x1b || /* bell and <escape> */ e >= 32 && e < 127) { if (e == 13) e = 10; /* newline char is special case */ txt_setdot(session, txt_size(session)); txt_insertchar(session, e); /* insert the char */ txt_movedot(session, 1); /* advance the dot */ /* * I ignore characters (with no comment) if the user types in > 256 * of them on one line. */ if (tty_icount < TTYBUF_SIZE) { tty_ibuffer[tty_icount++] = e; if (e == 10) tty_ready = YES; } } } break; } } #define SCROLLBACK 4000 #define STDOUT_BUFSIZE 1024 #define LONGEST_PRINTF 120 static char stdout_buffer[STDOUT_BUFSIZE]; static char *stdout_p; static int stdout_n; static BOOL saveproc(char *filename, void *handle) { FILE *f = fopen(filename, "w"); int i; CSL_IGNORE(handle); if (f == NULL) return FALSE; if (selstart == selend || selstart < 0) return FALSE; for (i=txt_selectstart(session); i<=txt_selectend(session); i++) putc(txt_charat(session, i), f); fclose(f); return TRUE; } static BOOL sendproc(void *handle, int *maxbuf) { int i; char buf[4]; CSL_IGNORE(handle); CSL_IGNORE(maxbuf); for (i=txt_selectstart(session); i<=txt_selectend(session); i++) { buf[0] = txt_charat(session, i); if (!xfersend_sendbuf(buf, 1)) return FALSE; } return TRUE; } /* * Menu to be generated by "menu" button when mouse is over our main * text window */ static void Main_menuproc(void *handle, char *hit) { CSL_IGNORE(handle); switch (hit[0]) { case MMInt: #ifdef SOFTWARE_TICKS countdown = -1; #endif interrupt_pending = YES; wimp_source = from_keyboard; break; case MMAbort: #ifdef SOFTWARE_TICKS countdown = -1; #endif interrupt_pending = YES; wimp_source = from_keyboard; break; case MMQuit: exit(0); break; case MMSave: wimp_source = from_keyboard; saveas(0xfff, "savetext", txt_size(session), saveproc, sendproc, NULL, NULL); break; case MMSelAll: selstart = 0; selend = txt_size(session) - 1; if (selstart != selend) txt_setselect(session, selstart, selend); break; case MMCopy: if (selstart == selend || selstart < 0) break; clip_selection(); break; case MMDelete: if (selstart == selend || selstart < 0) break; txt_movemarker(session, &session_marker, txt_dot(session)); txt_setdot(session, txt_selectstart(session)); txt_delete(session, txt_selectend(session)-txt_selectstart(session)); txt_movedottomarker(session, &session_marker); wimp_source = from_keyboard; break; case MMClear: selstart = selend = -1; txt_setselect(session, 0, 0); /* Unset any selection */ wimp_source = from_keyboard; break; } } static CSLbool CSL_create_txt(char *title) /* * returns YES if all went well */ { /* Create a new txt object. The title passed here over-rides one in the template file (a bit of a pity, that?) */ if ((session = txt_new(title)) == 0) return NO; txt_newmarker(session, &session_marker); clipboard_size = 128; if (!flex_alloc((flex_ptr)&clipboard, clipboard_size)) return NO; CSL_window_open = YES; if ((Main_menu = menu_new(appname+1, "Interrupt,Abort,Quit|>Save,SelectAll,Copy ^C,Delete ^X,Clear ^Z" )) == NULL) return NO; if (!event_attachmenu(txt_syshandle(session), Main_menu, Main_menuproc, 0)) return NO; /* Attach an event handler for the txt */ txt_eventhandler(session, CSL_txthandler, 0); /* Show the txt to the user */ txt_show(session); /* Set "dot" at beginning */ txt_setdot(session, 0); stdout_p = stdout_buffer; stdout_n = 0; win_claim_idle_events(txt_syshandle(session)); event_setmask((wimp_emask)0); /* permits null events to show up */ return YES; } /* * Icon Bar Icon. */ static void CSL_clickproc(wimp_i i) { CSL_IGNORE(i); if (!CSL_window_open) { if (!CSL_create_txt(appname+1)) werr(YES, "Unable to create text object - exiting"); } } static void Iconbar_menuproc(void *handle, char *hit) { CSL_IGNORE(handle); switch (hit[0]) { case MInfo: { dbox d; d = dbox_new("ProgInfo"); if (d == 0) return; dbox_setfield(d, Vfield, CSL_version_string); dbox_show(d); dbox_fillin(d); dbox_dispose(&d); } break; case MQuit: exit(0); break; } } /* * Arrange that files can be loaded into CSL by dragging */ static void CSL_load_events(wimp_eventstr *e, void *handle) { CSL_IGNORE(handle); if (e->e == wimp_ESEND || e->e == wimp_ESENDWANTACK) { if (CSL_window_open && e->data.msg.hdr.action == wimp_MDATALOAD) { char *filename; int filetype, len; filetype = xferrecv_checkinsert(&filename); if (filetype != -1) { len = strlen(LOADSTART) + strlen(filename) + strlen(LOADEND); if (tty_icount + len < TTYBUF_SIZE) { sprintf(&tty_ibuffer[tty_icount], "%s%s%s", LOADSTART, filename, LOADEND); tty_icount += len; txt_setdot(session, txt_size(session)); txt_insertstring(session, LOADSTART); txt_setdot(session, txt_size(session)); txt_insertstring(session, filename); txt_setdot(session, txt_size(session)); txt_insertstring(session, LOADEND); txt_setdot(session, txt_size(session)); } xferrecv_insertfileok(); } } } } /* * Output procedures */ static clock_t last_output_time; void flush_screen() /* * Bring screen up to date */ { int n; if (!under_wimp) { fflush(stdout); return; } if (stdout_n == 0) { last_output_time = clock(); return; } remove_ticker(); delink(); n = txt_size(session); /* * This code is intended to keep about around SCROLLBACK characters * buffered for the user to scroll back to. If I have more than SCROLLBACK * stored when I come to update the display I will discard some. */ if (n > SCROLLBACK) { txt_setcharoptions(session, txt_DISPLAY, (txt_charoption)0); txt_setdot(session, 0); txt_delete(session, SCROLLBACK - n + stdout_n); txt_setdot(session, txt_size(session)); txt_setcharoptions(session, txt_DISPLAY, txt_DISPLAY); n = txt_size(session); } if (n != txt_dot(session)) txt_setdot(session, txt_size(session)); txt_insertstring(session, stdout_buffer); txt_setdot(session, txt_size(session)); stdout_p = stdout_buffer; stdout_n = 0; last_output_time = clock(); relink(); add_ticker(); } static void remove_hourglass(void); void pause_for_user() /* * This is called at the end of a run so that the user gets a chance to read * the final screen-full of output. It pops up a dialog box that will * wait for a button push. I take the view that if output is going to a * file then the delay is not needed, since the user can always check for * messages there. This has the effect that non-interactive build sequences * will often run without the pause - a good thing! Note however that this * mean that you MUST use the close box to exit from a wimp session. Just * "quit;" or "(stop 0)" will not do. At least "quit;" etc pops up the * dialog box you see here... */ { dbox d; if (spool_file != NULL || !use_wimp) return; ensure_screen(); remove_ticker(); delink(); remove_hourglass(); d = dbox_new("finished"); if (d == 0) return; /* Unable to find the "finish" dialog box */ dbox_show(d); dbox_fillin(d); dbox_dispose(&d); return; } void putc_stdout(int c) { if (under_wimp) { *stdout_p++ = c; *stdout_p = 0; stdout_n++; if (stdout_n > STDOUT_BUFSIZE - LONGEST_PRINTF) ensure_screen(); if (polltick_pending) accept_tick(); } else putchar(c); } static char txt_title[64]; static int last_gc_count=-1, last_t=-1, last_gct=-1; static double last_percent; void report_time(int32 t, int32 gct) { int pos; if (!under_wimp) return; sprintf(txt_title, "%s", appname+1); pos = strlen(txt_title); last_t = t; last_gct = gct; if (last_t != -1) sprintf(&txt_title[pos], " time %ld.%.2ld+%ld.%.2ld secs", last_t/100L, last_t%100L, last_gct/100L, last_gct%100L); pos = strlen(txt_title); if (last_gc_count != -1) sprintf(&txt_title[pos], " GC %d [%.2f%% full]", last_gc_count, last_percent); txt_settitle(session, txt_title); } void report_space(int gc_count, double percent) { last_gc_count = gc_count; last_percent = percent; report_time(last_t, last_gct); } /* * Input procedures */ static CSLbool hourglass_showing; static void remove_hourglass() { if (hourglass_showing) { os_swi0(Hourglass_Off); hourglass_showing = NO; } } int wimpget(char *tty_buffer) /* * This is the main call from the body of CSL into the window manager * to obtain input from the user. It is expected to copy some input * characters into tty_buffer and return the number of characters * provided. In this implementation I keep a separate buffer called * tty_ibuffer that characters are collected into - when either this * gets full or a newline is inserted into it I copy it to tty_buffer * and return. * I will cancel copies from a selection if I have almost any mouse * activity. */ { int n; if (stdout_n != 0) ensure_screen(); mouse_directed_input: switch (wimp_source) { case from_clipboard: while (wimp_source == from_clipboard) { int c = clipboard[clipboard_p++]; txt_setdot(session, txt_size(session)); txt_insertchar(session, c); /* insert the char */ txt_movedot(session, 1); /* advance the dot */ if (clipboard_p >= clipboard_n) wimp_source = from_keyboard; tty_ibuffer[tty_icount++] = c; /* * If the line ended at buffer full or with a newline I return * the completed buffer. Otherwise I will drop through to wait for * keyboard input to finish off the line. */ if (c == '\n' || tty_icount == TTYBUF_SIZE) { memcpy(tty_buffer, tty_ibuffer, TTYBUF_SIZE); n = tty_icount; tty_icount = 0; tty_ready = NO; return n; } } break; case from_file: case from_keyboard: break; } remove_ticker(); delink(); remove_hourglass(); win_claim_idle_events(txt_syshandle(session)); event_setmask((wimp_emask)0); /* permits null events to show up */ while (!tty_ready && wimp_source == from_keyboard) event_process(); memcpy(tty_buffer, tty_ibuffer, TTYBUF_SIZE); relink(); add_ticker(); if (wimp_source != from_keyboard) goto mouse_directed_input; n = tty_icount; tty_icount = 0; tty_ready = NO; return n; } /* * Start-up code for the window manager */ static int CSL_initialise() { wimp_save_fp_state_on_poll(); wimpt_init("Codemist Lisp"); flex_init(); res_init(appname+1); msgs_init(); resspr_init(); template_use_fancyfonts(); template_init(); dbox_init(); txt_init(); /* * Create menu to associate with icon-bar object and attach it */ if ((Iconbar_menu = menu_new(appname+1, ">Info,Quit")) == NULL) return NO; baricon(appname, 1, CSL_clickproc); if (!event_attachmenu(win_ICONBAR, Iconbar_menu, Iconbar_menuproc, 0)) return NO; /* * Register function to get "unknown" events */ win_register_event_handler(win_ICONBARLOAD, CSL_load_events, 0); win_claim_unknown_events(win_ICONBARLOAD); tty_count = tty_icount = 0; tty_ready = NO; return YES; } void accept_tick() /* * My clock ticks ensure that this procedure is called fairly regularly... */ { if (under_wimp) { clock_t c0 = clock(); /* * Here I ensure that the screen is brought up to date at least every 3 * seconds or so. */ if (last_output_time == 0 || c0 > last_output_time + 3*CLOCKS_PER_SEC) { int t, gct; if (clock_stack == &consolidated_time[0]) { consolidated_time[0] += (double)(c0 - base_time)/(double)CLOCKS_PER_SEC; base_time = c0; } t = (int)(100.0 * consolidated_time[0]); gct = (int)(100.0 * gc_time); report_time(t, gct); push_clock(); ensure_screen(); } else push_clock(); /* * I also poll the window manager so that other tasks can get a look in * and so that my window responds to drag events and is generally kept * up to date on the screen. */ remove_ticker(); delink(); win_claim_idle_events(txt_syshandle(session)); event_setmask((wimp_emask)0); /* permits null events to show up */ do { done_something = 0; event_process(); } while (done_something); relink(); add_ticker(); /* * Time spent doing all of this is counted as "overhead" or "system time" * and not included in the times that I will report to my users... */ pop_clock(); polltick_pending = NO; } } void start_up_window_manager(int use_wimp) { int i, r0; if (!use_wimp) { under_wimp = NO; last_output_time = clock(); return; } os_swi1r(Wimp_ReadSysInfo, 0, &r0); if (r0 == 0) under_wimp = NO; else { under_wimp = YES; if (!CSL_initialise()) { fprintf(stderr, "\nFailed to start the window manager - stopping\n"); exit(EXIT_FAILURE); } /* * Now I hang, doing not a lot, until the main window gets opened... */ while (!CSL_window_open) event_process(); os_swi0(Hourglass_On); hourglass_showing = YES; atexit(remove_hourglass); win_claim_idle_events(txt_syshandle(session)); event_setmask((wimp_emask)0); /* permits null events to show up */ for (i=0; i<5; i++) event_process(); /* Maybe helps things become visible? */ last_output_time = clock(); } } #include "fileops.c" /* end of sysarm.c */