ocaml/maccaml/glue.c

511 lines
14 KiB
C

/***********************************************************************/
/* */
/* Objective Caml */
/* */
/* Damien Doligez, projet Para, INRIA Rocquencourt */
/* */
/* Copyright 1998 Institut National de Recherche en Informatique et */
/* en Automatique. All rights reserved. This file is distributed */
/* under the terms of the GNU Library General Public License. */
/* */
/***********************************************************************/
/* $Id$ */
#include <CursorCtl.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include "alloc.h"
#include "mlvalues.h"
#include "rotatecursor.h"
#include "signals.h"
#include "ui.h"
#include "main.h"
/* These are defined by the ocamlrun library. */
void caml_main(char **argv);
char *getcwd (char *buf, long size);
Handle macos_getfullpathname (short vrefnum, long dirid);
static int erroring = 0;
static long error_curpos;
static long error_anchor = -1;
/* This handle contains the environment variables. */
char *envPtr = NULL;
/* caml_at_work and Caml_working are used to manage the processor idle
state on PowerBooks (and also the beachball cursor: see AdjustCursor)
*/
int caml_at_work = 0;
/* Set caml_at_work to true or false. caml_at_work must always be
changed through this function, never directly. */
void Caml_working (int newstate)
{
if (gHasPowerManager){
if (caml_at_work && !newstate) EnableIdle ();
if (!caml_at_work && newstate) DisableIdle ();
}
caml_at_work = newstate;
}
/* Expand the percent escapes in the string specified by s.
The escapes are:
%a application file name
%d full pathname of the current working directory (ends in ':')
%t full pathname of the temporary directory (ends in ':')
%% %
*/
static OSErr expand_escapes (Handle s)
{
Size i, j, l;
OSErr err;
Handle curdir = NULL, tmpdir = NULL;
char *ptr2;
long len2;
l = GetHandleSize (s) - 1;
i = j = 0;
while (i < l){
if ((*s)[j] == '%'){
switch ((*s)[j+1]){
case 'a':
ptr2 = (char *) LMGetCurApName () + 1;
len2 = * (LMGetCurApName ());
break;
case 'd':
if (curdir == NULL) curdir = macos_getfullpathname (0, 0);
if (curdir == NULL){ err = fnfErr; goto failed; }
HLock (curdir);
ptr2 = *curdir;
len2 = GetHandleSize (curdir);
break;
case 't':
if (tmpdir == NULL){
short vrefnum;
long dirid;
err = FindFolder (kOnSystemDisk, kTemporaryFolderType, true,
&vrefnum, &dirid);
tmpdir = macos_getfullpathname (vrefnum, dirid);
if (tmpdir == NULL){ err = fnfErr; goto failed; }
}
HLock (tmpdir);
ptr2 = *tmpdir;
len2 = GetHandleSize (tmpdir);
break;
case '%':
ptr2 = "%";
len2 = 1;
break;
default:
ptr2 = "";
len2 = 0;
break;
}
Munger (s, j, NULL, 2, ptr2, len2);
j += len2 - 2;
i += 1;
}
++ i;
++ j;
}
if (curdir != NULL) DisposeHandle (curdir);
if (tmpdir != NULL) DisposeHandle (tmpdir);
return noErr;
failed:
if (curdir != NULL) DisposeHandle (curdir);
if (tmpdir != NULL) DisposeHandle (tmpdir);
return err;
}
/* [launch_caml_main] is called by [main].
It builds the command line according to the template found in
the 'Line'(kCommandLineTemplate) resource and the environment
variables according to the 'Line'(kEnvironmentTemplate).
Each of these resources is a sequence of strings separated by null
bytes. In each string, percent escapes are expanded (see above for
a description of percent escapes).
Each resource must end with a null byte.
*/
OSErr launch_caml_main (void)
{
Handle template = NULL;
Size len, i, j;
char *args = NULL;
int argc;
char **argv = NULL;
OSErr err;
template = GetResource ('Line', kCommandLineTemplate);
if (template == NULL){ err = ResError (); goto failed; }
err = expand_escapes (template); if (err != noErr) goto failed;
len = GetHandleSize (template);
args = malloc (len);
if (args == NULL){ err = memFullErr; goto failed; }
memcpy (args, *template, len);
argc = 0;
for (i = 0; i < len; i++){
if (args[i] == '\000') ++ argc;
}
argv = malloc ((argc+1) * sizeof (char *));
if (argv == NULL){ err = memFullErr; goto failed; }
i = j = 0;
do{
argv[j++] = args + i;
while (args [i] != '\000') ++ i;
++ i;
}while (i < len);
argv [argc] = NULL;
ReleaseResource (template);
template = GetResource ('Line', kEnvironmentTemplate);
if (template == NULL){ err = ResError (); goto failed; }
err = expand_escapes (template); if (err != noErr) goto failed;
len = GetHandleSize (template);
envPtr = NewPtr (len);
if (envPtr == NULL){ err = MemError (); goto failed; }
memcpy (envPtr, *template, len);
rotatecursor_options (&something_to_do, 50);
err = WinOpenToplevel ();
if (err != noErr) ExitApplication ();
Assert (!caml_at_work);
Caml_working (1);
caml_main (argv);
return noErr; /* Not reached */
failed:
if (template != NULL) ReleaseResource (template);
if (args != NULL) free (args);
if (argv != NULL) free (argv);
return err;
}
/***
ui_* stubs for I/O
*/
static void (**atexit_list) (void) = NULL;
static long atexit_size = 0;
static long atexit_len = 0;
void ui_exit (int return_code)
{
int i;
for (i = 0; i < atexit_len; i++) (*(atexit_list [i])) ();
Assert (caml_at_work);
Caml_working (0);
if (return_code != 0){
Str255 errorstr;
NumToString ((long) return_code, errorstr);
ParamText (errorstr, NULL, NULL, NULL);
modalkeys = kKeysOK;
InitCursor ();
NoteAlert (kAlertNonzeroExit, myModalFilterUPP);
}
while (1) GetAndProcessEvents (waitEvent, 0, 0);
}
int atexit (void (*f) (void))
{
if (atexit_list == NULL){
atexit_list = malloc (5 * sizeof (atexit_list [0]));
if (atexit_list == NULL) goto failed;
atexit_size = 5;
}else if (atexit_len >= atexit_size){
void *p = realloc (atexit_list, (atexit_size+10) * sizeof (atexit_list[0]));
if (p == NULL) goto failed;
atexit_list = p;
atexit_size += 10;
}
Assert (atexit_size > atexit_len);
atexit_list [atexit_len++] = f;
return 0;
failed:
/* errno = ENOMEM; est-ce que malloc positionne errno ? */
return -1;
}
int ui_read (int file_desc, char *buf, unsigned int length)
{
if (file_desc == 0){ /* Read from the toplevel window. */
long len, i;
char **htext;
WEReference we = WinGetWE (winToplevel);
long selstart, selend;
Boolean active;
short readonly, autoscroll;
int atend;
Assert (we != NULL);
htext = (char **) WEGetText (we);
Assert (caml_at_work);
Caml_working (0);
while (1){
char *p = *htext; /* The Handle is not locked. Be careful with p. */
len = WEGetTextLength (we);
for (i = wintopfrontier; i < len; i++){
if (p[i] == '\n') goto gotit;
}
GetAndProcessEvents (waitEvent, 0, 0);
}
gotit:
Assert (!caml_at_work);
Caml_working (1);
len = i+1 - wintopfrontier;
if (len > length) len = length;
memcpy (buf, (*htext)+wintopfrontier, len);
atend = ScrollAtEnd (winToplevel);
autoscroll = WEFeatureFlag (weFAutoScroll, weBitTest, we);
WEFeatureFlag (weFAutoScroll, weBitClear, we);
WEGetSelection (&selstart, &selend, we);
readonly = WEFeatureFlag (weFReadOnly, weBitTest, we);
WEFeatureFlag (weFReadOnly, weBitClear, we);
/* Always set an empty selection before changing OutlineHilite. */
WESetSelection (wintopfrontier, wintopfrontier, we);
WEFeatureFlag (weFOutlineHilite, weBitClear, we);
active = WEIsActive (we);
if (active) WEDeactivate (we);
WESetSelection (wintopfrontier, wintopfrontier+len, we);
WESetStyle (weDoFont + weDoFace + weDoSize + weDoColor + weDoReplaceFace,
&prefs.input, we);
WESetSelection (wintopfrontier, wintopfrontier, we);
if (active) WEActivate (we);
WEFeatureFlag (weFOutlineHilite, weBitSet, we);
WESetSelection (selstart, selend, we);
if (readonly) WEFeatureFlag (weFReadOnly, weBitSet, we);
if (autoscroll) WEFeatureFlag (weFAutoScroll, weBitSet, we);
AdjustScrollBars (winToplevel);
if (atend) ScrollToEnd (winToplevel);
WinAdvanceTopFrontier (len);
return len;
}else{
return read (file_desc, buf, length);
}
}
int ui_write (int file_desc, char *buf, unsigned int length)
{
if (file_desc == 1 || file_desc == 2){ /* Send to the toplevel window. */
long selstart, selend;
WEReference we = WinGetWE (winToplevel);
OSErr err;
short readonly, autoscroll;
int atend;
if (erroring){ /* overwrite mode to display errors; see terminfo_* */
error_curpos += length; Assert (error_curpos <= wintopfrontier);
return length;
}
Assert (we != NULL);
atend = ScrollAtEnd (winToplevel);
autoscroll = WEFeatureFlag (weFAutoScroll, weBitTest, we);
WEFeatureFlag (weFAutoScroll, weBitClear, we);
WEGetSelection (&selstart, &selend, we);
readonly = WEFeatureFlag (weFReadOnly, weBitTest, we);
WEFeatureFlag (weFReadOnly, weBitClear, we);
WESetSelection (wintopfrontier, wintopfrontier, we);
WESetStyle (weDoFont + weDoFace + weDoSize + weDoColor + weDoReplaceFace,
&prefs.output, we);
err = WEInsert (buf, (SInt32) length, NULL, NULL, we);
if (err != noErr){
WESetSelection (selstart, selend, we);
/* XXX should set errno */
return -1;
}
if (selstart >= wintopfrontier){
selstart += length;
selend += length;
}else if (selend > wintopfrontier){
selend += length;
}
WESetSelection (selstart, selend, we);
if (autoscroll) WEFeatureFlag (weFAutoScroll, weBitSet, we);
AdjustScrollBars (winToplevel);
if (atend) ScrollToEnd (winToplevel);
WinAdvanceTopFrontier (length);
return length;
}else{
return write (file_desc, buf, length);
}
}
void ui_print_stderr (char *format, void *arg)
{
char buf [1000]; /* XXX fixed size buffer :-( */
sprintf (buf, format, arg); Assert (strlen (buf) < 1000);
ui_write (2, buf, strlen (buf));
}
/***
animated cursor (only when toplevel window is frontmost)
*/
typedef struct {
short nframes;
short current;
union {
CursHandle h;
struct { short id; short fill; } i;
} frames [1];
} **AnimCursHandle;
static AnimCursHandle acurh;
pascal void InitCursorCtl (acurHandle newCursors)
{
#pragma unused (newCursors)
long i;
acurh = (AnimCursHandle) GetResource ('acur', 0);
for (i = 0; i < (*acurh)->nframes; i++){
(*acurh)->frames[i].h = GetCursor ((*acurh)->frames[i].i.id);
if ((*acurh)->frames[i].h == NULL){
(*acurh)->frames[i].h = GetCursor (watchCursor);
Assert ((*acurh)->frames[i].h != NULL);
}
}
(*acurh)->current = 0;
}
/* In O'Caml, counter is always a multiple of 32. */
pascal void RotateCursor (long counter)
{
(*acurh)->current += (*acurh)->nframes + (counter >= 0 ? 1 : -1);
(*acurh)->current %= (*acurh)->nframes;
GetAndProcessEvents (noWait, 0, 0);
}
void DisplayRotatingCursor (void)
{
SetCursor (*((*acurh)->frames[(*acurh)->current].h));
}
/***
"getenv" in the standalone application
envPtr is set up by launch_caml_main
*/
char *getenv (const char *name)
{
Size envlen, i, namelen;
Assert (envPtr != NULL);
envlen = GetPtrSize (envPtr);
namelen = strlen (name);
i = 0;
do{
if (!strncmp (envPtr + i, name, namelen) && envPtr [i+namelen] == '='){
return envPtr + i + namelen + 1;
}
while (envPtr [i] != '\000') ++ i;
++ i;
}while (i < envlen);
return NULL;
}
/***
"terminfo" stuff: change the style of displayed text to show the
error locations. See also ui_write.
*/
value terminfo_setup (value vchan);
value terminfo_backup (value lines);
value terminfo_standout (value start);
value terminfo_resume (value lines);
#define Good_term_tag 0
value terminfo_setup (value vchan)
{
#pragma unused (vchan)
value result = alloc (1, Good_term_tag);
Field (result, 0) = Val_int (1000000000);
return result;
}
value terminfo_backup (value lines)
{
long i, j;
Handle txt;
char *p;
WEReference we = WinGetWE (winToplevel);
Assert (we != NULL);
txt = WEGetText (we);
p = (char *) *txt;
j = wintopfrontier - 1;
for (i = 0; i < Long_val (lines); i++){
Assert (p[j] == '\n');
do{ --j; }while (p[j] != '\n');
}
Assert (p[j] == '\n');
error_curpos = j + 1;
erroring = 1;
error_anchor = -1;
return Val_unit;
}
value terminfo_standout (value start)
{
if (Bool_val (start) && error_anchor == -1){
error_anchor = error_curpos;
}else if (!Bool_val (start) && error_anchor != -1){
long selstart, selend;
WEReference we = WinGetWE (winToplevel);
short readonly;
Assert (we != NULL);
WEGetSelection (&selstart, &selend, we);
readonly = WEFeatureFlag (weFReadOnly, weBitTest, we);
if (readonly) WEFeatureFlag (weFReadOnly, weBitClear, we);
WESetSelection (error_anchor, error_curpos, we);
WESetStyle (weDoFont + weDoFace + weDoSize + weDoColor + weDoReplaceFace,
&prefs.errors, we);
if (readonly) WEFeatureFlag (weFReadOnly, weBitSet, we);
WESetSelection (selstart, selend, we);
error_anchor = -1;
}
return Val_unit;
}
value terminfo_resume (value lines)
{
#pragma unused (lines)
erroring = 0;
return Val_unit;
}