/* event.c * * $Id$ * * Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin * Copyright 2002, 2003 Sam Hocevar , Paris * * This software was derived from Elk 1.2, which was Copyright 1987, 1988, * 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written * by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project * between TELES and Nixdorf Microprocessor Engineering, Berlin). * * Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co- * owners or individual owners of copyright in this software, grant to any * person or company a worldwide, royalty free, license to * * i) copy this software, * ii) prepare derivative works based on this software, * iii) distribute copies of this software or derivative works, * iv) perform this software, or * v) display this software, * * provided that this notice is not removed and that neither Oliver Laumann * nor Teles nor Nixdorf are deemed to have made any representations as to * the suitability of this software for any purpose nor are held responsible * for any defects of this software. * * THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ #include "xlib.h" #include #define MAX_ARGS 14 static Object Argl, Argv; static struct event_desc { char *name; int argc; } Event_Table[] = { { "event-0", 1 }, { "event-1", 1 }, { "key-press", 12 }, { "key-release", 12 }, { "button-press", 12 }, { "button-release", 12 }, { "motion-notify", 12 }, { "enter-notify", 14 }, { "leave-notify", 14 }, { "focus-in", 4 }, { "focus-out", 4 }, { "keymap-notify", 3 }, { "expose", 7 }, { "graphics-expose", 9 }, { "no-expose", 4 }, { "visibility-notify", 3 }, { "create-notify", 9 }, { "destroy-notify", 3 }, { "unmap-notify", 4 }, { "map-notify", 4 }, { "map-request", 3 }, { "reparent-notify", 7 }, { "configure-notify", 10 }, { "configure-request", 11 }, { "gravity-notify", 5 }, { "resize-request", 4 }, { "circulate-notify", 4 }, { "circulate-request", 4 }, { "property-notify", 5 }, { "selection-clear", 4 }, { "selection-request", 7 }, { "selection-notify", 6 }, { "colormap-notify", 5 }, { "client-message", 4 }, { "mapping-notify", 4 }, { 0, 0 } }; struct predicate_arg { Object *funcs; Object *ret; }; /*ARGSUSED*/ static int Event_Predicate (Display *dpy, XEvent *ep, #ifdef XLIB_RELEASE_5_OR_LATER XPointer ptr) { #else char *ptr) { #endif struct predicate_arg *ap = (struct predicate_arg *)ptr; register int i; Object args; GC_Node; if ((i = ep->type) < LASTEvent && !Nullp (ap->funcs[i])) { args = Get_Event_Args (ep); GC_Link (args); *ap->ret = Funcall (ap->funcs[i], args, 0); Destroy_Event_Args (args); GC_Unlink; } return Truep (*ap->ret); } /* (handle-events display discard? peek? clause...) * clause = (event function) or ((event...) function) or (else function) * loops/blocks until a function returns x != #f, then returns x. * discard?: discard unprocessed events. * peek?: don't discard processed events. */ static Object P_Handle_Events (Object argl) { Object next, clause, func, ret, funcs[LASTEvent], args; register int i, discard, peek; Display *dpy; char *errmsg = "event occurs more than once"; GC_Node3; struct gcnode gcv; TC_Prolog; TC_Disable; clause = args = Null; GC_Link3 (argl, clause, args); next = Eval (Car (argl)); Check_Type (next, T_Display); dpy = DISPLAY(next)->dpy; argl = Cdr (argl); next = Eval (Car (argl)); Check_Type (next, T_Boolean); discard = Truep (next); argl = Cdr (argl); next = Eval (Car (argl)); Check_Type (next, T_Boolean); peek = Truep (next); for (i = 0; i < LASTEvent; i++) funcs[i] = Null; gcv.gclen = 1+LASTEvent; gcv.gcobj = funcs; gcv.next = &gc3; GC_List = &gcv; for (argl = Cdr (argl); !Nullp (argl); argl = Cdr (argl)) { clause = Car (argl); Check_List (clause); if (Fast_Length (clause) != 2) Primitive_Error ("badly formed event clause"); func = Eval (Car (Cdr (clause))); Check_Procedure (func); clause = Car (clause); if (EQ(clause, Sym_Else)) { for (i = 0; i < LASTEvent; i++) if (Nullp (funcs[i])) funcs[i] = func; } else { if (TYPE(clause) == T_Pair) { for (; !Nullp (clause); clause = Cdr (clause)) { i = Encode_Event (Car (clause)); if (!Nullp (funcs[i])) Primitive_Error (errmsg); funcs[i] = func; } } else { i = Encode_Event (clause); if (!Nullp (funcs[i])) Primitive_Error (errmsg); funcs[i] = func; } } } ret = False; while (!Truep (ret)) { XEvent e; if (discard) { (peek ? XPeekEvent : XNextEvent) (dpy, &e); if ((i = e.type) < LASTEvent && !Nullp (funcs[i])) { args = Get_Event_Args (&e); ret = Funcall (funcs[i], args, 0); Destroy_Event_Args (args); } else { if (peek) XNextEvent (dpy, &e); /* discard it */ } } else { struct predicate_arg a; a.funcs = funcs; a.ret = &ret; (peek ? XPeekIfEvent : XIfEvent) (dpy, &e, Event_Predicate, #ifdef XLIB_RELEASE_5_OR_LATER (XPointer)&a); #else (char *)&a); #endif } } GC_Unlink; TC_Enable; return ret; } static Object Get_Time_Arg (Time t) { return t == CurrentTime ? Sym_Now : Make_Unsigned_Long ((unsigned long)t); } Object Get_Event_Args (XEvent *ep) { Object tmpargs[MAX_ARGS]; register int e, i; register Object *a, *vp; struct gcnode gcv; Object dummy; GC_Node; e = ep->type; dummy = Null; a = tmpargs; for (i = 0; i < MAX_ARGS; i++) a[i] = Null; GC_Link (dummy); gcv.gclen = 1 + MAX_ARGS; gcv.gcobj = a; gcv.next = &gc1; GC_List = &gcv; switch (e) { case KeyPress: case KeyRelease: case ButtonPress: case ButtonRelease: case MotionNotify: case EnterNotify: case LeaveNotify: { register XKeyEvent *p = (XKeyEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Window (0, p->display, p->root); a[3] = Make_Window (0, p->display, p->subwindow); a[4] = Get_Time_Arg (p->time); a[5] = Make_Integer (p->x); a[6] = Make_Integer (p->y); a[7] = Make_Integer (p->x_root); a[8] = Make_Integer (p->y_root); if (e == KeyPress || e == KeyRelease) { a[9] = Bits_To_Symbols ((unsigned long)p->state, 1, State_Syms); a[10] = Make_Integer (p->keycode); a[11] = p->same_screen ? True : False; } else if (e == ButtonPress || e == ButtonRelease) { register XButtonEvent *q = (XButtonEvent *)ep; a[9] = Bits_To_Symbols ((unsigned long)q->state, 1, State_Syms); a[10] = Bits_To_Symbols ((unsigned long)q->button, 0, Button_Syms); a[11] = q->same_screen ? True : False; } else if (e == MotionNotify) { register XMotionEvent *q = (XMotionEvent *)ep; a[9] = Bits_To_Symbols ((unsigned long)q->state, 1, State_Syms); a[10] = q->is_hint ? True : False; a[11] = q->same_screen ? True : False; } else { register XCrossingEvent *q = (XCrossingEvent *)ep; a[9] = Bits_To_Symbols ((unsigned long)q->mode, 0, Cross_Mode_Syms); a[10] = Bits_To_Symbols ((unsigned long)q->detail, 0, Cross_Detail_Syms); a[11] = q->same_screen ? True : False; a[12] = q->focus ? True : False; a[13] = Bits_To_Symbols ((unsigned long)q->state, 1, Button_Syms); } } break; case FocusIn: case FocusOut: { register XFocusChangeEvent *p = (XFocusChangeEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Bits_To_Symbols ((unsigned long)p->mode, 0, Cross_Mode_Syms); a[3] = Bits_To_Symbols ((unsigned long)p->detail, 0, Focus_Detail_Syms); } break; case KeymapNotify: { register XKeymapEvent *p = (XKeymapEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_String (p->key_vector, 32); } break; case Expose: { register XExposeEvent *p = (XExposeEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Integer (p->x); a[3] = Make_Integer (p->y); a[4] = Make_Integer (p->width); a[5] = Make_Integer (p->height); a[6] = Make_Integer (p->count); } break; case GraphicsExpose: { register XGraphicsExposeEvent *p = (XGraphicsExposeEvent *)ep; a[1] = Make_Window (0, p->display, p->drawable); a[2] = Make_Integer (p->x); a[3] = Make_Integer (p->y); a[4] = Make_Integer (p->width); a[5] = Make_Integer (p->height); a[6] = Make_Integer (p->count); a[7] = Make_Integer (p->major_code); a[8] = Make_Integer (p->minor_code); } break; case NoExpose: { register XNoExposeEvent *p = (XNoExposeEvent *)ep; a[1] = Make_Window (0, p->display, p->drawable); a[2] = Make_Integer (p->major_code); a[3] = Make_Integer (p->minor_code); } break; case VisibilityNotify: { register XVisibilityEvent *p = (XVisibilityEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Bits_To_Symbols ((unsigned long)p->state, 0, Visibility_Syms); } break; case CreateNotify: { register XCreateWindowEvent *p = (XCreateWindowEvent *)ep; a[1] = Make_Window (0, p->display, p->parent); a[2] = Make_Window (0, p->display, p->window); a[3] = Make_Integer (p->x); a[4] = Make_Integer (p->y); a[5] = Make_Integer (p->width); a[6] = Make_Integer (p->height); a[7] = Make_Integer (p->border_width); a[8] = p->override_redirect ? True : False; } break; case DestroyNotify: { register XDestroyWindowEvent *p = (XDestroyWindowEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); } break; case UnmapNotify: { register XUnmapEvent *p = (XUnmapEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = p->from_configure ? True : False; } break; case MapNotify: { register XMapEvent *p = (XMapEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = p->override_redirect ? True : False; } break; case MapRequest: { register XMapRequestEvent *p = (XMapRequestEvent *)ep; a[1] = Make_Window (0, p->display, p->parent); a[2] = Make_Window (0, p->display, p->window); } break; case ReparentNotify: { register XReparentEvent *p = (XReparentEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = Make_Window (0, p->display, p->parent); a[4] = Make_Integer (p->x); a[5] = Make_Integer (p->y); a[6] = p->override_redirect ? True : False; } break; case ConfigureNotify: { register XConfigureEvent *p = (XConfigureEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = Make_Integer (p->x); a[4] = Make_Integer (p->y); a[5] = Make_Integer (p->width); a[6] = Make_Integer (p->height); a[7] = Make_Integer (p->border_width); a[8] = Make_Window (0, p->display, p->above); a[9] = p->override_redirect ? True : False; } break; case ConfigureRequest: { register XConfigureRequestEvent *p = (XConfigureRequestEvent *)ep; a[1] = Make_Window (0, p->display, p->parent); a[2] = Make_Window (0, p->display, p->window); a[3] = Make_Integer (p->x); a[4] = Make_Integer (p->y); a[5] = Make_Integer (p->width); a[6] = Make_Integer (p->height); a[7] = Make_Integer (p->border_width); a[8] = Make_Window (0, p->display, p->above); a[9] = Bits_To_Symbols ((unsigned long)p->detail, 0, Stack_Mode_Syms); a[10] = Make_Unsigned_Long (p->value_mask); } break; case GravityNotify: { register XGravityEvent *p = (XGravityEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = Make_Integer (p->x); a[4] = Make_Integer (p->y); } break; case ResizeRequest: { register XResizeRequestEvent *p = (XResizeRequestEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Integer (p->width); a[3] = Make_Integer (p->height); } break; case CirculateNotify: { register XCirculateEvent *p = (XCirculateEvent *)ep; a[1] = Make_Window (0, p->display, p->event); a[2] = Make_Window (0, p->display, p->window); a[3] = Bits_To_Symbols ((unsigned long)p->place, 0, Place_Syms); } break; case CirculateRequest: { register XCirculateRequestEvent *p = (XCirculateRequestEvent *)ep; a[1] = Make_Window (0, p->display, p->parent); a[2] = Make_Window (0, p->display, p->window); a[3] = Bits_To_Symbols ((unsigned long)p->place, 0, Place_Syms); } break; case PropertyNotify: { register XPropertyEvent *p = (XPropertyEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Atom (p->atom); a[3] = Get_Time_Arg (p->time); a[4] = Bits_To_Symbols ((unsigned long)p->state, 0, Prop_Syms); } break; case SelectionClear: { register XSelectionClearEvent *p = (XSelectionClearEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Atom (p->selection); a[3] = Get_Time_Arg (p->time); } break; case SelectionRequest: { register XSelectionRequestEvent *p = (XSelectionRequestEvent *)ep; a[1] = Make_Window (0, p->display, p->owner); a[2] = Make_Window (0, p->display, p->requestor); a[3] = Make_Atom (p->selection); a[4] = Make_Atom (p->target); a[5] = Make_Atom (p->property); a[6] = Get_Time_Arg (p->time); } break; case SelectionNotify: { register XSelectionEvent *p = (XSelectionEvent *)ep; a[1] = Make_Window (0, p->display, p->requestor); a[2] = Make_Atom (p->selection); a[3] = Make_Atom (p->target); a[4] = Make_Atom (p->property); a[5] = Get_Time_Arg (p->time); } break; case ColormapNotify: { register XColormapEvent *p = (XColormapEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Colormap (0, p->display, p->colormap); a[3] = p->new ? True : False; a[4] = p->state == ColormapInstalled ? True : False; } break; case ClientMessage: { register XClientMessageEvent *p = (XClientMessageEvent *)ep; register int i; a[1] = Make_Window (0, p->display, p->window); a[2] = Make_Atom (p->message_type); switch (p->format) { case 8: a[3] = Make_String (p->data.b, 20); break; case 16: a[3] = Make_Vector (10, Null); for (i = 0; i < 10; i++) VECTOR(a[3])->data[i] = Make_Integer (p->data.s[i]); break; case 32: a[3] = Make_Vector (5, Null); for (i = 0; i < 5; i++) VECTOR(a[3])->data[i] = Make_Long (p->data.l[i]); break; default: a[3] = Make_Integer (p->format); /* ??? */ } } break; case MappingNotify: { register XMappingEvent *p = (XMappingEvent *)ep; a[1] = Make_Window (0, p->display, p->window); a[2] = Bits_To_Symbols ((unsigned long)p->request, 0, Mapping_Syms); a[3] = Make_Integer (p->first_keycode); a[4] = Make_Integer (p->count); } break; } a[0] = Intern (Event_Table[e].name); for (vp = VECTOR(Argv)->data, i = 0; i < Event_Table[e].argc; i++) { if (i) vp++; Car (*vp) = a[i]; Cdr (*vp) = vp[1]; } Cdr (*vp) = Null; GC_Unlink; return Argl; } void Destroy_Event_Args (Object args) { Object t; for (t = args; !Nullp (t); t = Cdr (t)) Car (t) = Null; } int Encode_Event (Object e) { Object s; register char *p; register struct event_desc *ep; register int n; Check_Type (e, T_Symbol); s = SYMBOL(e)->name; p = STRING(s)->data; n = STRING(s)->size; for (ep = Event_Table; ep->name; ep++) if (n && strncmp (ep->name, p, n) == 0) break; if (ep->name == 0) Primitive_Error ("no such event: ~s", e); return ep-Event_Table; } static Object P_Get_Motion_Events (Object w, Object from, Object to) { XTimeCoord *p; int n; register int i; Object e, ret; GC_Node2; Check_Type (w, T_Window); p = XGetMotionEvents (WINDOW(w)->dpy, WINDOW(w)->win, Get_Time (from), Get_Time (to), &n); e = ret = Make_Vector (n, Null); GC_Link2 (ret, e); for (i = 0; i < n; i++) { e = P_Make_List (Make_Integer (3), Null); VECTOR(ret)->data[i] = e; Car (e) = Get_Time_Arg (p[i].time); e = Cdr (e); Car (e) = Make_Integer (p[i].x); e = Cdr (e); Car (e) = Make_Integer (p[i].y); } GC_Unlink; XFree ((char *)p); return ret; } static Object P_Event_Listen (Object d, Object wait_flag) { Display *dpy; register int n; XEvent e; Check_Type (d, T_Display); Check_Type (wait_flag, T_Boolean); dpy = DISPLAY(d)->dpy; n = XPending (dpy); if (n == 0 && EQ(wait_flag, True)) { XPeekEvent (dpy, &e); n = XPending (dpy); } return Make_Integer (n); } void elk_init_xlib_event () { Object t; register int i; Argl = P_Make_List (Make_Integer (MAX_ARGS), Null); Global_GC_Link (Argl); Argv = Make_Vector (MAX_ARGS, Null); Global_GC_Link (Argv); for (i = 0, t = Argl; i < MAX_ARGS; i++, t = Cdr (t)) VECTOR(Argv)->data[i] = t; Define_Primitive (P_Handle_Events, "handle-events", 3, MANY, NOEVAL); Define_Primitive (P_Get_Motion_Events, "get-motion-events", 3, 3, EVAL); Define_Primitive (P_Event_Listen, "event-listen", 2, 2, EVAL); }