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 }