#define _EVENT_C
#include "openaxiom-c-macros.h"
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include "debug.h"
#include "sockio.h"
#include "hyper.h"
#include "event.h"
#include "keyin.h"
#include "hyper.h"
#include "display.h"
#include "parse.h"
#include "parse-paste.h"
#include "initx.h"
#include "scrollbar.h"
#include "group.h"
#include "lex.h"
#include "sockio.h"
jmp_buf env;
Window gActiveWindow;
int motion = 0;
int gNeedIconName = 0;
unsigned long bigmask= 0xffffffff;
static HyperLink *gSavedInputAreaLink = NULL;
static int
HyperDocErrorHandler(Display *display, XErrorEvent *xe)
{
if (xe->request_code != 15) {
char buf[1024];
XGetErrorText(display, xe->error_code, buf, sizeof(buf));
fprintf(stderr, "error code = %d\n", xe->error_code);
fprintf(stderr, "major op code = %d\n", xe->request_code);
fprintf(stderr, "minor op code = %d\n", xe->minor_code);
fprintf(stderr, "XID = %ld\n", xe->resourceid);
fprintf(stderr, "%s\n", buf);
if (xe->request_code != 15)
exit(-1);
}
return(0);
}
static void
set_error_handlers()
{
XSetErrorHandler(HyperDocErrorHandler);
}
static int
set_window(Window window)
{
Window root, parent, *children, grandparent,myarg;
HDWindow *htw;
unsigned int nchildren;
int st;
myarg=window;
nchildren = 0;
htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&myarg);
if (htw != NULL) {
gWindow = htw;
return 1;
}
st = XQueryTree(gXDisplay, myarg, &root, &parent, &children, &nchildren);
if (st==0) goto ERROR;
if (nchildren > 0)
XFree(children);
htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&parent);
if (htw != NULL) {
gWindow = htw;
return 1;
}
else {
st = XQueryTree(gXDisplay, parent, &root, &grandparent, &children, &nchildren);
if (st==0) goto ERROR;
if (nchildren > 0)
XFree(children);
htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&grandparent);
if (htw != NULL) {
gWindow = htw;
return 1;
}
}
ERROR:
gWindow=gParentWindow;
return 0;
}
static HyperLink *
findButtonInList(HDWindow * window, int x, int y)
{
ButtonList *bl;
if (!window || window->page->type == UnloadedPageType)
return NULL;
for (bl = window->page->s_button_list; bl != NULL; bl = bl->next)
if (x >= bl->x0 && x <= bl->x1 && y >= bl->y0 && y <= bl->y1)
return bl->link;
for (bl = window->page->button_list; bl != NULL; bl = bl->next)
if (x >= bl->x0 && x <= bl->x1 && y >= bl->y0 && y <= bl->y1)
return bl->link;
return NULL;
}
static void
set_cursor(HDWindow *window,Cursor state)
{
if (state == gBusyCursor)
XDefineCursor(gXDisplay, window->fMainWindow, gBusyCursor);
else if (state == gActiveCursor)
XDefineCursor(gXDisplay, window->fMainWindow, gActiveCursor);
else
XDefineCursor(gXDisplay, window->fMainWindow, gNormalCursor);
XFlush(gXDisplay);
}
static void
change_cursor(Cursor state, HDWindow *window)
{
if (window->fDisplayedCursor == state)
return;
window->fDisplayedCursor = state;
set_cursor(window, state);
}
static void
make_busy_cursor(HDWindow *window)
{
change_cursor(gBusyCursor, window);
}
static void
make_busy_cursors()
{
hash_map(&gSessionHashTable, (MappableFunction)make_busy_cursor);
}
static void
handle_motion_event(XMotionEvent *event)
{
if (!gWindow)
return;
if (findButtonInList(gWindow, event->x, event->y) != NULL)
change_cursor(gActiveCursor, gWindow);
else
change_cursor(gNormalCursor, gWindow);
}
static void
clear_exposures(Window w)
{
XEvent report;
XFlush(gXDisplay);
while (XCheckTypedWindowEvent(gXDisplay, w, Expose, &report));
}
static HyperLink *
get_hyper_link(XButtonEvent * event)
{
HyperLink *l1, *l2;
l1 = (HyperLink *) hash_find(gWindow->fWindowHashTable, (char *)&(event->window));
if (l1)
return l1;
l2 = findButtonInList(gWindow, event->x, event->y);
return l2;
}
static HyperDocPage *
paste_button(PasteNode * paste)
{
HyperDocPage *page = NULL;
auto pastewhere = paste->where;
if ( paste->end_node ==NULL || paste->begin_node==NULL || paste->arg_node==NULL ){
BeepAtTheUser();
return NULL;
}
page=parse_patch(paste);
if (pastewhere != SourceInputKind{ } && page ) {
if (0 == strcmp(page->name, "ErrorPage"))
page = NULL;
}
else
BeepAtTheUser();
return page;
}
static void
killAxiomPage(HyperDocPage * page)
{
char command[512];
sprintf(command, "(|htpDestroyPage| '%s)", page->name);
send_lisp_command(command);
}
static void
kill_page(HyperDocPage * page)
{
page->scroll_off = 0;
if (page->type == SpadGen) {
hash_delete(gWindow->fPageHashTable, page->name);
killAxiomPage(page);
free_page(page);
}
}
static HyperDocPage *
returnlink()
{
int i;
if (gWindow->fMemoStackIndex == 0) {
BeepAtTheUser();
return NULL;
}
else {
kill_page(gWindow->page);
for (i = gWindow->fDownLinkStackIndex - 1;
i >= gWindow->fDownLinkStackTop[gWindow->fMemoStackIndex - 1];
i--)
{
kill_page(gWindow->fDownLinkStack[i]);
}
gWindow->fDownLinkStackIndex =
gWindow->fDownLinkStackTop[--gWindow->fMemoStackIndex];
return (gWindow->fMemoStack[gWindow->fMemoStackIndex]);
}
}
static HyperDocPage *
uplink()
{
if (gWindow->fDownLinkStackIndex == 0)
return returnlink();
else {
kill_page(gWindow->page);
return (gWindow->fDownLinkStack[--gWindow->fDownLinkStackIndex]);
}
}
static HyperDocPage *
find_page(TextNode * node)
{
char *page_name;
HyperDocPage *page;
page_name = print_to_string(node);
page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page_name);
if (page == NULL) {
page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "UnknownPage");
if (page == NULL) {
fprintf(stderr, "Unknown page name %s\n", page_name);
}
else {
if (page->type == UnloadedPageType)
page->type = UlUnknownPage;
else
page->type = UnknownPage;
}
}
return page;
}
static void
downlink()
{
if (gWindow->fDownLinkStackIndex == MaxDownlinkDepth)
fprintf(stderr, "exceeded maximum link nesting level\n");
else
gWindow->fDownLinkStack[gWindow->fDownLinkStackIndex++] = gWindow->page;
}
static void
memolink()
{
if (gWindow->fMemoStackIndex == MaxMemoDepth)
fprintf(stderr, "exceeded maximum link nesting level\n");
else {
gWindow->fMemoStack[gWindow->fMemoStackIndex] = gWindow->page;
gWindow->fDownLinkStackTop[gWindow->fMemoStackIndex++] = gWindow->fDownLinkStackIndex;
}
}
static void
windowlink_handler(TextNode * node)
{
char *page_name;
page_name = print_to_string(node);
if (init_top_window(page_name) == -1) {
return;
}
}
static void
lispwindowlink_handler(HyperLink * link)
{
if (init_top_window(NULL) != -1) {
HyperDocPage *page = NULL;
int frame = gWindow->fAxiomFrame;
page = issue_server_command(link);
gWindow->fAxiomFrame = frame;
gWindow->page = page;
}
}
static void
create_window()
{
XWindowAttributes wa;
XGetWindowAttributes(gXDisplay, gWindow->fMainWindow, &wa);
gWindow->width = wa.width;
gWindow->height = wa.height;
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
XSelectInput(gXDisplay, gWindow->fMainWindow, ButtonPress | KeyPressMask |
PointerMotionMask |
ExposureMask );
XSelectInput(gXDisplay, gWindow->fScrollWindow, ExposureMask);
}
#define NotSpecial(t) \
((t == openaxiom_Quitbutton_token || t == openaxiom_Returnbutton_token \
|| t == openaxiom_Upbutton_token || t == UnknownPage \
|| t == UlUnknownPage || t == ErrorPage) \
?(0):(1))
static void
handle_button(int button, XButtonEvent * event)
{
HyperLink *link;
HyperDocPage *page = NULL;
char *page_name;
link = get_hyper_link(event);
if (link == NULL) {
return;
}
switch (link->type) {
case openaxiom_Pastebutton_token:
page = paste_button(link->reference.paste);
break;
case openaxiom_Link_token:
page_name = print_to_string(link->reference.node);
page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page_name);
break;
case openaxiom_Helpbutton_token:
helpForHyperDoc();
page = NULL;
break;
case openaxiom_Scrollbar_token:
scrollScroller(event);
break;
case Scrollupbutton:
scrollUp();
break;
case Scrolldownbutton:
scrollDown();
break;
case openaxiom_Inputstring_token:
change_input_focus(link);
if ( button == Button2 ) {
XConvertSelection(gXDisplay, XA_PRIMARY, XA_STRING,
XInternAtom(gXDisplay, "PASTE_SELECTION", False),
gWindow->fMainWindow, CurrentTime);
gSavedInputAreaLink = link;
}
break;
case openaxiom_SimpleBox_token:
page = NULL;
toggle_input_box(link);
break;
case openaxiom_Radiobox_token:
page = NULL;
toggle_radio_box(link);
break;
case openaxiom_Quitbutton_token:
quitHyperDoc();
break;
case openaxiom_Returnbutton_token:
page = returnlink();
break;
case openaxiom_Upbutton_token:
page = uplink();
break;
case openaxiom_Downlink_token:
page = find_page(link->reference.node);
if (page && NotSpecial(page->type))
downlink();
break;
case openaxiom_Memolink_token:
page = find_page(link->reference.node);
if (page && NotSpecial(page->type))
memolink();
break;
case openaxiom_Windowlink_token:
page = find_page(link->reference.node);
if (page && NotSpecial(page->type)) {
windowlink_handler(link->reference.node);
gNeedIconName = 1;
page = NULL;
}
break;
case openaxiom_Lispwindowlink_token:
lispwindowlink_handler(link);
gNeedIconName = 1;
page = NULL;
break;
case openaxiom_LispMemoLink_token:
case openaxiom_Spadmemolink_token:
page = issue_server_command(link);
if (page && NotSpecial(page->type))
memolink();
break;
case openaxiom_LispDownLink_token:
case openaxiom_Spaddownlink_token:
page = issue_server_command(link);
if (page && NotSpecial(page->type))
downlink();
break;
case openaxiom_Spadlink_token:
case openaxiom_Lisplink_token:
page = issue_server_command(link);
break;
case openaxiom_Lispcommand_token:
case openaxiom_Qspadcall_token:
case openaxiom_Spadcall_token:
page = issue_server_command(link);
break;
case openaxiom_Lispcommandquit_token:
case openaxiom_Spadcallquit_token:
case openaxiom_Qspadcallquit_token:
page = issue_server_command(link);
exitHyperDoc();
break;
case openaxiom_Spadcommand_token:
case openaxiom_Spadgraph_token:
case openaxiom_Spadsrc_token:
issue_spadcommand(gWindow->page, link->reference.node,
button == Button1, link->type);
break;
case openaxiom_Unixlink_token:
page = issue_unixlink(link->reference.node);
if (page && NotSpecial(page->type)) {
downlink();
}
break;
case openaxiom_Unixcommand_token:
issue_unixcommand(link->reference.node);
break;
default:
break;
}
if (page) {
switch (page->type) {
case openaxiom_Quitbutton_token:
exitHyperDoc();
return;
case openaxiom_Returnbutton_token:
gWindow->page = returnlink();
break;
case openaxiom_Upbutton_token:
gWindow->page = uplink();
break;
case ErrorPage:
case UnknownPage:
case UlUnknownPage:
if (page->type == UlUnknownPage)
page->type = UnloadedPageType;
downlink();
gWindow->page = page;
break;
default:
gWindow->page = page;
break;
}
if (link->type != openaxiom_Pastebutton_token)
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
}
}
static void
handle_event(XEvent * event)
{
XWindowAttributes wa;
set_window(event->xany.window);
if (event->type == MotionNotify) {
handle_motion_event((XMotionEvent *)event);
motion = 1;
return;
}
make_busy_cursors();
switch (event->type) {
case DestroyNotify:
break;
case Expose:
XGetWindowAttributes(gXDisplay, gWindow->fMainWindow, &wa);
if ((gWindow->width == 0 && gWindow->height == 0) ||
(wa.width != gWindow->width || wa.height != gWindow->height)) {
gWindow->width = wa.width;
gWindow->height = wa.height;
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
}
else
expose_page(gWindow->page);
XFlush(gXDisplay);
clear_exposures(gWindow->fMainWindow);
clear_exposures(gWindow->fScrollWindow);
break;
case ButtonPress:
handle_button(event->xbutton.button, (XButtonEvent *)event);
XFlush(gXDisplay);
if (gWindow) {
while (XCheckTypedWindowEvent(gXDisplay, gWindow->fMainWindow,
Expose, event));
while (XCheckTypedWindowEvent(gXDisplay, gWindow->fScrollWindow,
Expose, event));
}
break;
case KeyPress:
handle_key(event);
if (gWindow) {
while (XCheckTypedWindowEvent(gXDisplay, gWindow->fMainWindow,
Expose, event));
while (XCheckTypedWindowEvent(gXDisplay, gWindow->fScrollWindow,
Expose, event));
}
break;
case MapNotify:
create_window();
break;
case SelectionNotify:
if ( gSavedInputAreaLink ) {
XSelectionEvent *pSelEvent;
Atom dataProperty;
pSelEvent = (XSelectionEvent *) event;
dataProperty = XInternAtom(gXDisplay, "PASTE_SELECTION", False);
if ( pSelEvent->requestor == gWindow->fMainWindow &&
pSelEvent->selection == XA_PRIMARY &&
pSelEvent->target == XA_STRING &&
pSelEvent->property == dataProperty )
{
Atom actual_type;
int actual_format;
unsigned long nitems, leftover;
char *pSelection = NULL;
if (Success == XGetWindowProperty(gXDisplay,
gWindow->fMainWindow,
pSelEvent->property, 0L, 100000000L, True,
AnyPropertyType, &actual_type, &actual_format,
&nitems, &leftover, (unsigned char **) &pSelection) )
{
char *pBuffer;
InputItem *item = gSavedInputAreaLink->reference.string;
for (pBuffer = pSelection; *pBuffer; ++pBuffer)
add_buffer_to_sym(pBuffer, item);
XFree(pSelection);
}
}
gSavedInputAreaLink = NULL;
}
break;
default:
break;
}
}
void
quitHyperDoc()
{
HyperDocPage *page;
if (gSessionHashTable.num_entries == 1 || gParentWindow == gWindow) {
if (!strcmp(gWindow->page->name, "ProtectedQuitPage")){
exitHyperDoc();
}
page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ProtectedQuitPage");
if (page == NULL) {
fprintf(stderr, "Unknown page name %s\n", "ProtectedQuitPage");
exitHyperDoc();
return;
}
if (gWindow->fDownLinkStackIndex == MaxDownlinkDepth)
fprintf(stderr, "exceeded maximum link nesting level\n");
else
gWindow->fDownLinkStack[gWindow->fDownLinkStackIndex++] = gWindow->page;
gWindow->page = page;
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
}
else
exitHyperDoc();
}
void
make_window_link(char *name)
{
if (init_top_window(name) != -1)
{}
}
void
helpForHyperDoc()
{
HyperDocPage *page = NULL;
if (0 == strcmp(gWindow->page->name, NoMoreHelpPage))
return;
if (!gWindow->page->helppage)
gWindow->page->helppage = alloc_string(NoMoreHelpPage);
if (0 == strcmp(gWindow->page->name, TopLevelHelpPage))
gWindow->page->helppage = alloc_string(NoMoreHelpPage);
page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, gWindow->page->helppage);
if (page)
make_window_link(gWindow->page->helppage);
else
BeepAtTheUser();
}
void
exitHyperDoc()
{
XEvent event;
if (gSessionHashTable.num_entries == 1 || gParentWindow == gWindow) {
free_hd_window(gWindow);
exit(0);
}
hash_delete(&gSessionHashTable, (char *)&gWindow->fMainWindow);
XFlush(gXDisplay);
while (XCheckWindowEvent(gXDisplay, gWindow->fMainWindow, bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->fScrollWindow,bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->fDisplayedWindow, bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->fScrollUpWindow, bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->fScrollDownWindow, bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->scrollbar, bigmask, &event)) {
}
while (XCheckWindowEvent(gXDisplay, gWindow->scroller, bigmask, &event)) {
}
XDestroyWindow(gXDisplay, gWindow->fMainWindow);
free_hd_window(gWindow);
gWindow = NULL;
gActiveWindow = -1;
XFlush(gXDisplay);
}
void
get_new_window()
{
int val;
char buf[128];
int frame;
Window wid;
HDWindow *htw;
HyperDocPage *hpage;
frame = get_int(spad_socket);
val = get_int(spad_socket);
switch (val) {
case StartPage:
init_top_window(NULL);
val = get_int(spad_socket);
init_scanner();
input_type = SourceInputKind::SpadSocket;
input_string = "";
gWindow->page = parse_page_from_socket();
gWindow->fAxiomFrame = frame;
XFlush(gXDisplay);
break;
case LinkToPage:
get_string_buf(spad_socket, buf, 128);
if (init_top_window(buf) == -1) {
fprintf(stderr, "get_new_window: Did not find page %s\n", buf);
}
gWindow->fAxiomFrame = frame;
break;
case PopUpPage:
val = get_int(spad_socket);
init_form_window(NULL, val);
send_int(spad_socket, gWindow->fMainWindow);
init_scanner();
input_type = SourceInputKind::SpadSocket;
input_string = "";
gWindow->page = parse_page_from_socket();
compute_form_page(gWindow->page);
XMapWindow(gXDisplay, gWindow->fMainWindow);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
gWindow->fAxiomFrame = frame;
XFlush(gXDisplay);
break;
case PopUpNamedPage:
val = get_int(spad_socket);
get_string_buf(spad_socket, buf, 128);
if (init_form_window(buf, val) == -1) {
send_int(spad_socket, -1);
break;
}
load_page(gWindow->page);
compute_form_page(gWindow->page);
XMapWindow(gXDisplay, gWindow->fMainWindow);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
gWindow->fAxiomFrame = frame;
XFlush(gXDisplay);
send_int(spad_socket, gWindow->fMainWindow);
break;
case ReplaceNamedPage:
wid = (Window) get_int(spad_socket);
get_string_buf(spad_socket, buf, 128);
htw = (HDWindow *) hash_find(&gSessionHashTable,(char *)&wid);
if (htw == NULL) break;
hpage = (HyperDocPage *) hash_find(gWindow->fPageHashTable, buf);
if (hpage == NULL) break;
gWindow = htw;
gWindow->page = hpage;
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
clear_exposures(gWindow->fMainWindow);
clear_exposures(gWindow->fScrollWindow);
XFlush(gXDisplay);
break;
case ReplacePage:
wid = (Window) get_int(spad_socket);
set_window(wid);
init_scanner();
input_type = SourceInputKind::SpadSocket;
input_string = "";
gWindow->page = parse_page_from_socket();
display_page(gWindow->page);
gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
clear_exposures(gWindow->fMainWindow);
clear_exposures(gWindow->fScrollWindow);
XFlush(gXDisplay);
break;
case KillPage:
wid = (Window) get_int(spad_socket);
htw = (HDWindow *) hash_find(&gSessionHashTable,(char *)&wid);
if (htw !=NULL) {
gWindow = htw;
exitHyperDoc();
break;
}
break;
}
}
static void
init_cursor_state(HDWindow *window)
{
if (window) {
int x, y, rx, ry, but;
Window r, c;
XQueryPointer(gXDisplay, window->fMainWindow,
&r, &c, &rx, &ry, &x, &y,(unsigned int *) &but);
if (findButtonInList(window, x, y) != NULL)
change_cursor(gActiveCursor, window);
else
change_cursor(gNormalCursor, window);
}
}
static void
init_cursor_states()
{
hash_map(&gSessionHashTable,(MappableFunction) init_cursor_state);
}
void
mainEventLoop()
{
XEvent event;
int Xcon;
fd_set rd, dum1, dum2;
motion = 0;
gActiveWindow = -1;
set_error_handlers();
Xcon = ConnectionNumber(gXDisplay);
while (1) {
while (gSessionHashTable.num_entries == 0)
pause();
if (!motion)
init_cursor_states();
motion = 0;
if (!spad_socket == 0) {
FD_ZERO(&rd);
FD_ZERO(&dum1);
FD_ZERO(&dum2);
FD_CLR(0, &dum1);
FD_CLR(0, &dum2);
FD_CLR(0, &rd);
FD_SET(spad_socket->socket, &rd);
FD_SET(Xcon, &rd);
if (!session_server == 0) {
FD_SET(session_server->socket, &rd);
}
if (XEventsQueued(gXDisplay, QueuedAlready)) {
XNextEvent(gXDisplay, &event);
handle_event(&event);
}
else {
select(FD_SETSIZE, &rd, &dum1, &dum2, NULL);
if (FD_ISSET(Xcon, &rd) ||
XEventsQueued(gXDisplay, QueuedAfterFlush)) {
XNextEvent(gXDisplay, &event);
handle_event(&event);
}
else if (FD_ISSET(spad_socket->socket, &rd))
{
if (100 == get_int(spad_socket)) {
set_window(gParentWindow->fMainWindow);
make_busy_cursors();
get_new_window();
}
}
else
if (session_server && FD_ISSET(session_server->socket, &rd)) {
service_session_socket();
}
}
}
else {
XNextEvent(gXDisplay, &event);
handle_event(&event);
}
}
}