gwin.c - sam - An updated version of the sam text editor. | |
git clone git://vernunftzentrum.de/sam.git | |
Log | |
Files | |
Refs | |
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 | XIMStatusNoth… | |
124 XNClientWindow, XtWindow(w), XNFocusWindow, XtWindow… | |
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)NU… | |
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… | |
271 * modifiers that are specified for the binding are all pressed,… | |
272 * we allow other modifiers to be as well. This is because when … | |
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, NU… | |
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) != … | |
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)… | |
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(_… | |
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), XtLastTimestampPro… | |
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, … | |
542 } |