gwin.c - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       gwin.c (13612B)
       ---
            1 /* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
            2 #include <u.h>
            3 #include <libg.h>
            4 #include <stdio.h>
            5 #include <X11/IntrinsicP.h>
            6 #include <X11/StringDefs.h>
            7 #include <X11/Xatom.h>
            8 #include <X11/XKBlib.h>
            9 #include <X11/keysym.h>
           10 
           11 #include "GwinP.h"
           12 #include "libgint.h"
           13 
           14 const char *clipatom = "PRIMARY";
           15 
           16 /* Forward declarations */
           17 static void Realize(Widget, XtValueMask *, XSetWindowAttributes *);
           18 static void Resize(Widget);
           19 static void Redraw(Widget, XEvent *, Region);
           20 static void Mappingaction(Widget, XEvent *, String *, Cardinal*);
           21 static void Keyaction(Widget, XEvent *, String *, Cardinal*);
           22 static void Mouseaction(Widget, XEvent *, String *, Cardinal*);
           23 static String SelectSwap(Widget, String);
           24 
           25 /* Data */
           26 
           27 #define Offset(field) XtOffsetOf(GwinRec, gwin.field)
           28 
           29 static XtResource resources[] = {
           30     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
           31         Offset(foreground), XtRString, (XtPointer)XtDefaultForeground},
           32     {XtNscrollForwardR, XtCScrollForwardR, XtRBoolean, sizeof(Boolean),
           33         Offset(forwardr), XtRImmediate, (XtPointer)true},
           34     {XtNreshaped, XtCReshaped, XtRFunction, sizeof(Reshapefunc),
           35         Offset(reshaped), XtRFunction, (XtPointer) NULL},
           36     {XtNgotchar, XtCGotchar, XtRFunction, sizeof(Charfunc),
           37         Offset(gotchar), XtRFunction, (XtPointer) NULL},
           38     {XtNgotmouse, XtCGotmouse, XtRFunction, sizeof(Mousefunc),
           39         Offset(gotmouse), XtRFunction, (XtPointer) NULL},
           40     {XtNselection, XtCSelection, XtRString, sizeof(String),
           41         Offset(selection), XtRString, (XtPointer) NULL},
           42 };
           43 #undef Offset
           44 
           45 static XtActionsRec actions[] = {
           46     {"key", Keyaction},
           47     {"mouse", Mouseaction},
           48     {"mapping", Mappingaction}
           49 };
           50 
           51 static char tms[] =
           52     "<Key> : key() \n\
           53     <Motion> : mouse() \n\
           54     <BtnDown> : mouse() \n\
           55     <BtnUp> : mouse() \n\
           56     <Mapping> : mapping() \n";
           57 
           58 /* Class record declaration */
           59 
           60 GwinClassRec gwinClassRec = {
           61   /* Core class part */
           62    {
           63     /* superclass         */    (WidgetClass)&widgetClassRec,
           64     /* class_name         */    "Gwin",
           65     /* widget_size        */    sizeof(GwinRec),
           66     /* class_initialize   */    NULL,
           67     /* class_part_initialize*/  NULL,
           68     /* class_inited       */    false,
           69     /* initialize         */    NULL,
           70     /* initialize_hook    */    NULL,
           71     /* realize            */    Realize,
           72     /* actions            */    actions,
           73     /* num_actions        */    XtNumber(actions),
           74     /* resources          */    resources,
           75     /* num_resources      */    XtNumber(resources),
           76     /* xrm_class          */    NULLQUARK,
           77     /* compress_motion    */    true,
           78     /* compress_exposure  */    XtExposeCompressMultiple,
           79     /* compress_enterleave*/    true,
           80     /* visible_interest   */    false,
           81     /* destroy            */    NULL,
           82     /* resize             */    Resize,
           83     /* expose             */    Redraw,
           84     /* set_values         */    NULL,
           85     /* set_values_hook    */    NULL,
           86     /* set_values_almost  */    XtInheritSetValuesAlmost,
           87     /* get_values_hook    */    NULL,
           88     /* accept_focus       */    XtInheritAcceptFocus,
           89     /* version            */    XtVersion,
           90     /* callback_offsets   */    NULL,
           91     /* tm_table           */    tms,
           92     /* query_geometry       */  XtInheritQueryGeometry,
           93     /* display_accelerator  */  NULL,
           94     /* extension            */  NULL
           95    },
           96   /* Gwin class part */
           97    {
           98     /* select_swap    */    SelectSwap,
           99    }
          100 };
          101 
          102 /* Class record pointer */
          103 WidgetClass gwinWidgetClass = (WidgetClass) &gwinClassRec;
          104 
          105 static XModifierKeymap *modmap;
          106 static int keypermod;
          107 extern XIC xic;
          108 extern XIM xim;
          109 
          110 static void
          111 Realize(Widget w, XtValueMask *valueMask, XSetWindowAttributes *attrs)
          112 {
          113     *valueMask |= CWBackingStore;
          114     attrs->backing_store = Always;
          115 
          116     XtCreateWindow(w, InputOutput, (Visual *)0, *valueMask, attrs);
          117     XtSetKeyboardFocus(w->core.parent, w);
          118     if ((modmap = XGetModifierMapping(XtDisplay(w))))
          119         keypermod = modmap->max_keypermod;
          120 
          121     Resize(w);
          122 
          123     xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
          124                     XNClientWindow, XtWindow(w), XNFocusWindow, XtWindow(w), NULL);
          125     if (!xic){
          126         fprintf(stderr, "could not create input context\n");
          127         exit(EXIT_FAILURE);
          128     }
          129 }
          130 
          131 static void
          132 Resize(Widget w)
          133 {
          134     if(XtIsRealized(w))
          135         (*(XtClass(w)->core_class.expose))(w, (XEvent *)NULL, (Region)NULL);
          136 }
          137 
          138 static void
          139 Redraw(Widget w, XEvent *e, Region r)
          140 {
          141     Reshapefunc f;
          142 
          143     f = ((GwinWidget)w)->gwin.reshaped;
          144     if(f)
          145         (*f)(w->core.x, w->core.y,
          146             w->core.x+w->core.width, w->core.y+w->core.height);
          147 }
          148 
          149 static void
          150 Mappingaction(Widget w, XEvent *e, String *p, Cardinal *np)
          151 {
          152     if (modmap)
          153         XFreeModifiermap(modmap);
          154     modmap = XGetModifierMapping(e->xany.display);
          155     if (modmap)
          156         keypermod = modmap->max_keypermod;
          157 }
          158 
          159 typedef struct Unikeysym Unikeysym;
          160 struct Unikeysym{
          161     KeySym keysym;
          162     uint16_t value;
          163 };
          164 
          165 Unikeysym unikeysyms[] ={
          166     #include "unikeysyms.h"
          167     {0, 0}
          168 };
          169 
          170 uint16_t
          171 keysymtoshort(KeySym k)
          172 {
          173     for (Unikeysym *ks = unikeysyms; ks->keysym != 0; ks++){
          174         if (k == ks->keysym)
          175             return ks->value;
          176     }
          177 
          178     return k;
          179 }
          180 
          181 typedef struct Keymapping Keymapping;
          182 struct Keymapping{
          183     Keymapping *next;
          184     int m;
          185     KeySym s;
          186     int k;
          187     int c;
          188     char a[];
          189 };
          190 
          191 static Keymapping *keymappings = NULL;
          192 
          193 int
          194 installbinding(int m, KeySym s, int k, int c, const char *a)
          195 {
          196     if (m < 0 || s == NoSymbol || k < 0 || c < 0)
          197         return -1;
          198 
          199     a = a ? a : "";
          200     Keymapping *km = calloc(1, sizeof(Keymapping) + strlen(a) + 1);
          201     if (!km)
          202         return -1;
          203 
          204     km->m = m;
          205     km->s = s;
          206     km->k = k;
          207     km->c = c;
          208     strcpy(km->a, a);
          209     km->next = keymappings;
          210     keymappings = km;
          211 
          212     return 0;
          213 }
          214 
          215 int
          216 removebinding(int m, KeySym s)
          217 {
          218     if (m < 0 || s == NoSymbol)
          219         return -1;
          220 
          221     for (Keymapping *km = keymappings; km; km = km->next){
          222         if (km->m == m && km->s == s)
          223             km->c = Cdefault;
          224     }
          225 
          226     return 0;
          227 }
          228 
          229 void
          230 freebindings(void)
          231 {
          232     Keymapping *m = keymappings;
          233     while (m){
          234         Keymapping *n = m->next;
          235         free(m);
          236         m = n;
          237     }
          238 }
          239 
          240 static void
          241 Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
          242 {
          243     extern XIC xic;
          244     int kind = Kraw;
          245 
          246     int c, len, minmod;
          247     KeySym k, mk;
          248     Charfunc f;
          249     Modifiers md;
          250     Status s;
          251     wchar_t buf[32] = {0};
          252 
          253     c = 0;
          254     len = 0;
          255 
          256     /* Translate the keycode into a key symbol. */
          257     if(e->xany.type != KeyPress)
          258         return;
          259 
          260     len = XwcLookupString(xic, &e->xkey, buf, 32, &k, &s);
          261     if (IsModifierKey(k))
          262         return;
          263 
          264     /* Check to see if it's a specially-handled key first. */
          265     for (Keymapping *m = keymappings; m; m = m->next){
          266         KeySym u = NoSymbol;
          267         KeySym l = NoSymbol;
          268         XConvertCase(k, &l, &u);
          269 
          270         /* Note that magic bit manipulation here - we want to check that the
          271          * modifiers that are specified for the binding are all pressed, but
          272          * we allow other modifiers to be as well. This is because when NumLock
          273          * is on, it's always added to the modifier mask.
          274          */
          275         if (l == m->s || m->s == XK_VoidSymbol){
          276             if (m->m == 0 || (m->m & ~e->xkey.state) == 0){
          277                 switch (m->c){
          278                     case Cnone:
          279                         return;
          280 
          281                     case Cdefault:
          282                         continue;
          283 
          284                     default:
          285                         f = ((GwinWidget)w)->gwin.gotchar;
          286                         if (f)
          287                             (*f)(m->c, m->k, Tcurrent, 0, 0, m->a);
          288                         return;
          289                 }
          290             }
          291         }
          292     }
          293 
          294     c = keysymtoshort(k);
          295     f = ((GwinWidget)w)->gwin.gotchar;
          296     if(f && c)
          297         (*f)(c? c : buf[0], kind, Tcurrent, 0, 0, NULL);
          298 }
          299 
          300 typedef struct Chordmapping Chordmapping;
          301 struct Chordmapping{
          302     Chordmapping *next;
          303     int s1;
          304     int s2;
          305     int c;
          306     int t;
          307     const char *a;
          308 };
          309 
          310 static Chordmapping *chordmap = NULL;
          311 
          312 int
          313 installchord(int s1, int s2, int c, int t, const char *a)
          314 {
          315     if (s1 < 0 || s2 < 0 || c < 0 || (t != Tmouse && t != Tcurrent))
          316         return -1;
          317 
          318     Chordmapping *m = calloc(1, sizeof(Chordmapping));
          319     if (!m)
          320         return -1;
          321 
          322     m->s1 = s1;
          323     m->s2 = s2;
          324     m->c = c;
          325     m->t = t;
          326     m->a = a;
          327 
          328     m->next = chordmap;
          329     chordmap = m;
          330     return 0;
          331 }
          332 
          333 int
          334 removechord(int s1, int s2)
          335 {
          336     if (s1 < 0 || s2 < 0)
          337         return -1;
          338 
          339     for (Chordmapping *m = chordmap; m; m = m->next){
          340         if (m->s1 == s1 && m->s2 == s2)
          341             m->c = Cdefault;
          342     }
          343 
          344     return 0;
          345 }
          346 
          347 void
          348 freechords(void)
          349 {
          350     Chordmapping *m = chordmap;
          351     while (m){
          352         Chordmapping *n = m->next;
          353         free(m);
          354         m = n;
          355     }
          356 }
          357 
          358 static void
          359 Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
          360 {
          361     int s = 0;
          362     int ps = 0; /* the previous state */
          363     int ob = 0;
          364     static bool chording = false;
          365     Charfunc kf;
          366 
          367     XButtonEvent *be = (XButtonEvent *)e;
          368     XMotionEvent *me = (XMotionEvent *)e;
          369     Gwinmouse m;
          370     Mousefunc f;
          371 
          372     switch(e->type){
          373     case ButtonPress:
          374         m.xy.x = be->x;
          375         m.xy.y = be->y;
          376         m.msec = be->time;
          377         ps = s = be->state;
          378         switch(be->button){
          379         case 1: s |= Button1Mask; break;
          380         case 2: s |= Button2Mask; break;
          381         case 3: s |= Button3Mask; break;
          382         case 4: s |= Button4Mask; break;
          383         case 5: s |= Button5Mask; break;
          384         }
          385         break;
          386     case ButtonRelease:
          387         m.xy.x = be->x;
          388         m.xy.y = be->y;
          389         m.msec = be->time;
          390         ps = s = be->state;
          391         switch(be->button){
          392         case 1: s &= ~Button1Mask; break;
          393         case 2: s &= ~Button2Mask; break;
          394         case 3: s &= ~Button3Mask; break;
          395         case 4: s &= ~Button4Mask; break;
          396         case 5: s &= ~Button5Mask; break;
          397         }
          398         break;
          399     case MotionNotify:
          400         ps = s = me->state;
          401         m.xy.x = me->x;
          402         m.xy.y = me->y;
          403         m.msec = me->time;
          404         break;
          405     default:
          406         return;
          407     }
          408 
          409     m.buttons = 0;
          410 
          411     if(ps & Button1Mask) ob |= 1;
          412     if(ps & Button2Mask) ob |= 2;
          413     if(ps & Button3Mask) ob |= (s & ShiftMask) ? 2 : 4;
          414     if(ps & Button4Mask) ob |= 8;
          415     if(ps & Button5Mask) ob |= 16;
          416 
          417     if(s & Button1Mask) m.buttons |= 1;
          418     if(s & Button2Mask) m.buttons |= 2;
          419     if(s & Button3Mask) m.buttons |= (s & ShiftMask) ? 2 : 4;
          420     if(s & Button4Mask) m.buttons |= 8;
          421     if(s & Button5Mask) m.buttons |= 16;
          422 
          423     if (!m.buttons)
          424         chording = false;
          425 
          426     /* Check to see if it's a chord first. */
          427     for (Chordmapping *cm = chordmap; cm; cm = cm->next){
          428         if (ob == cm->s1 && m.buttons == cm->s2){
          429             switch (cm->c){
          430                 case Cdefault:
          431                     continue;
          432 
          433                 case Cnone:
          434                     break;
          435 
          436                 default:
          437                     kf = ((GwinWidget)w)->gwin.gotchar;
          438                     if (kf)
          439                         (*kf)(cm->c, Kcommand, cm->t, m.xy.x, m.xy.y, NULL);
          440         
          441                     m.buttons = 0;
          442                     chording = true;
          443                     break;
          444             }
          445         }
          446     }
          447 
          448     if (chording)
          449         m.buttons = 0;
          450 
          451     f = ((GwinWidget)w)->gwin.gotmouse;
          452     if(f)
          453         (*f)(&m);
          454 }
          455 
          456 static void
          457 SelCallback(Widget w, XtPointer cldata, Atom *sel, Atom *seltype,
          458     XtPointer val, uint64_t *len, int *fmt)
          459 {
          460     GwinWidget gw = (GwinWidget)w;
          461     XTextProperty p = {0};
          462     char *ls[2] = {(char *)val, NULL};
          463 
          464     if (*seltype == 0){
          465         if (gw->gwin.selection == NULL)
          466             gw->gwin.selection = strdup("");
          467         return;
          468     }
          469 
          470     if(gw->gwin.selection){
          471         XtFree(gw->gwin.selection);
          472         gw->gwin.selection = NULL;
          473     }
          474 
          475     if(*seltype != XInternAtom(_dpy, "UTF8_STRING", 0))
          476         return;
          477 
          478     if (XmbTextListToTextProperty(_dpy, ls, 1, XUTF8StringStyle, &p) != Success)
          479         return;
          480 
          481     gw->gwin.selection = strdup(p.value);
          482     XtFree(val);
          483     XFree(p.value);
          484 }
          485 
          486 static Boolean
          487 SendSel(Widget w, Atom *sel, Atom *target, Atom *rtype, XtPointer *ans,
          488         uint64_t *anslen, int *ansfmt)
          489 {
          490     GwinWidget gw = (GwinWidget)w;
          491     XTextProperty p = {0};
          492     char *ls[2] = {NULL, NULL};
          493 
          494     if (*target == XA_STRING){
          495         ls[0] = gw->gwin.selection? gw->gwin.selection : "";
          496         if (XmbTextListToTextProperty(_dpy, ls, 1, XUTF8StringStyle, &p) != Success)
          497             return false;
          498 
          499         *rtype = p.encoding;
          500         *ans = (XtPointer) XtNewString(p.value);
          501         *anslen = p.nitems;
          502         *ansfmt = p.format;
          503         XFree(p.value);
          504         return true;
          505     }
          506 
          507     return false;
          508 }
          509 
          510 static String
          511 SelectSwap(Widget w, String s)
          512 {
          513     GwinWidget gw;
          514     String ans;
          515 
          516     gw = (GwinWidget)w;
          517     if(gw->gwin.selection){
          518         XtFree(gw->gwin.selection);
          519         gw->gwin.selection = NULL;
          520     }
          521     XtGetSelectionValue(w, XInternAtom(_dpy, clipatom, 0), XInternAtom(_dpy, "UTF8_STRING", 0), SelCallback, 0,
          522             XtLastTimestampProcessed(XtDisplay(w)));
          523 
          524     while(gw->gwin.selection == NULL)
          525         XtAppProcessEvent(XtWidgetToApplicationContext(w) , XtIMAll);
          526     ans = gw->gwin.selection;
          527     gw->gwin.selection = XtMalloc(strlen(s)+1);
          528     strcpy(gw->gwin.selection, s);
          529 
          530     XtOwnSelection(w, XInternAtom(_dpy, clipatom, 0), XtLastTimestampProcessed(XtDisplay(w)),
          531             SendSel, NULL, NULL);
          532 
          533     return ans;
          534 }
          535 
          536 /* The returned answer should be free()ed when no longer needed */
          537 String
          538 GwinSelectionSwap(Widget w, String s)
          539 {
          540     XtCheckSubclass(w, gwinWidgetClass, NULL);
          541     return (*((GwinWidgetClass) XtClass(w))->gwin_class.select_swap)(w, s);
          542 }