more X11 primitives
[k8lst.git] / src / primlib / x11lib / lstpl_x11.c
blob5d6350a2e9424512fe95baaaa398eddd8739ca37
1 /*
2 * coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
3 * Understanding is not required. Only obedience.
5 * This program is free software. It comes without any warranty, to
6 * the extent permitted by applicable law. You can redistribute it
7 * and/or modify it under the terms of the Do What The Fuck You Want
8 * To Public License, Version 2, as published by Sam Hocevar. See
9 * http://sam.zoy.org/wtfpl/COPYING for more details.
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include <X11/Xlib.h>
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
18 #include <X11/Xos.h>
19 #include <X11/Xproto.h>
20 #include <X11/Xutil.h>
22 #include "lstcore/k8lst.h"
23 #include "lstpl_x11.h"
26 static const char *x11ObjId = "X11Handle";
27 static const char *x11SubId[] = {
28 "Display",
29 "Window",
30 "GC",
35 enum {
36 X11_DISPLAY,
37 X11_WINDOW,
38 X11_GC,
39 X11_MAXSUB
43 static Display *curDisp = NULL;
44 static Colormap curCMap;
45 static int cmapIsHere = 0;
46 #define MAX_QUEUE 128
47 static XEvent eQueue[MAX_QUEUE];
48 static int eqHead = 0;
49 static int eqTail = 0;
50 static int eqCount = 0;
53 typedef struct X11Info X11Info;
54 typedef void (*X11HCloseFn) (X11Info *i);
56 struct X11Info {
57 const char *type;
58 int subtype;
59 Display *dpy;
60 intptr_t handle;
61 X11HCloseFn cfn;
65 static void killDisplay (X11Info *i) {
66 if (i->handle) {
67 XCloseDisplay((Display *)i->handle);
68 i->handle = 0;
73 static void killWindow (X11Info *i) {
74 if (i->handle) {
75 XDestroyWindow(i->dpy, (Window)i->handle);
76 i->handle = 0;
81 static void killGC (X11Info *i) {
82 if (i->handle) {
83 XFreeGC(i->dpy, (GC)i->handle);
84 i->handle = 0;
89 LST_FINALIZER(lpX11Finalizer) {
90 X11Info *re = (X11Info *)udata;
91 if (re) {
92 if (re->handle && re->cfn) {
93 fprintf(stderr, "closing [%s]\n", x11SubId[re->subtype]);
94 if (re->subtype == X11_DISPLAY && (Display *)re->handle == curDisp) { curDisp = NULL; eqHead = eqTail = eqCount = 0; }
95 re->cfn(re);
97 free(re);
102 static lstObject *newX11 (void *handle, int subtype, X11HCloseFn cfn) {
103 if (subtype < 0 || subtype >= X11_MAXSUB) return lstNewString("internal error");
104 lstObject *res = lstNewBinary(NULL, 0);
105 X11Info *re = malloc(sizeof(X11Info));
106 if (!re) return lstNewString("out of memory");
107 re->type = x11ObjId;
108 re->subtype = subtype;
109 re->handle = (intptr_t)handle;
110 re->cfn = cfn;
111 lstSetFinalizer(res, lpX11Finalizer, re);
112 return res;
116 static X11Info *getX11 (lstObject *o) {
117 if (LST_IS_SMALLINT(o) || !LST_IS_BYTES(o) || LST_SIZE(o) || !o->fin || !o->fin->udata) return NULL;
118 X11Info *re = (X11Info *)o->fin->udata;
119 if (re->type != x11ObjId) return NULL;
120 return re;
124 static int getColor (lstObject *o, unsigned short *c) {
125 int cc;
126 if (LST_IS_SMALLINT(o)) {
127 cc = lstIntValue(o);
128 if (cc < 0) cc = 0; else if (cc > 65535) cc = 65535;
129 } else if (o->stclass == lstIntegerClass) {
130 LstLInt n = lstLIntValue(o);
131 if (n < 0) cc = 0; else if (n > 65535) cc = 65535; else cc = n;
132 } else if (o->stclass == lstFloatClass) {
133 LstFloat n = lstFloatValue(o);
134 if (n < 0.0) cc = 0; else if (n >= 1.0) cc = 65535; else cc = (int)(n*65535);
135 } else return -1;
136 *c = cc;
137 return 0;
141 LST_PRIMFN(lpX11Do) {
142 if (LST_PRIMARGC < 1 || !LST_IS_SMALLINT(LST_PRIMARG(0))) return NULL;
143 int action = lstIntValue(LST_PRIMARG(0)), tmp;
144 if ((action != 1 && action != 5 && action != 6) && LST_PRIMARGC < 2) return NULL;
145 lstObject *res = lstNilObj, *op;
146 const char *str = NULL;
147 X11Info *xi;
148 Display *dpy;
149 Window w;
150 switch (action) {
151 case 0: /* get subtype name */
152 if (LST_PRIMARGC < 1) return NULL;
153 if ((xi = getX11(LST_PRIMARG(1))) && xi->handle) res = lstNewString(x11SubId[xi->subtype]);
154 break;
155 case 1: { /* XOpenDisplay [name] */
156 if (LST_PRIMARGC > 1) {
157 op = LST_PRIMARG(1);
158 if (op != lstNilObj) {
159 if (!LST_IS_BYTES_EX(op)) return NULL;
160 str = lstGetStringPtr(LST_PRIMARG(1));
163 Display *d = XOpenDisplay(str);
164 if (d) res = newX11(d, X11_DISPLAY, killDisplay);
165 break; }
166 case 2: /* XCloseDisplay x11 */
167 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
168 if (xi->subtype == X11_DISPLAY && xi->handle) {
169 Display *dpy = (Display *)xi->handle;
170 if (curDisp == dpy) { curDisp = NULL; eqHead = eqTail = eqCount = 0; }
171 XCloseDisplay(dpy);
172 xi->handle = 0;
173 res = lstTrueObj;
175 break;
176 case 3: /* BlackWhitePixel nil/false==black */
177 if (!curDisp) return NULL;
178 op = LST_PRIMARG(1);
179 if (op == lstNilObj || op == lstFalseObj) tmp = BlackPixel(curDisp, DefaultScreen(curDisp));
180 else tmp = WhitePixel(curDisp, DefaultScreen(curDisp));
181 res = lstNewInteger(tmp);
182 break;
183 case 4: /* XSelectInput window interestFlag (no other args for now) */
184 /* interestFlag is not nil, not false, not true: do not get motion events */
185 if (LST_PRIMARGC < 3) return NULL;
186 if (!curDisp) return NULL;
187 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
188 if (xi->subtype != X11_WINDOW || !xi->handle) return NULL;
189 w = (Window)xi->handle;
190 op = LST_PRIMARG(2);
191 XSelectInput(curDisp, w, op==lstNilObj||op==lstFalseObj ? 0 :
192 ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
193 ExposureMask | FocusChangeMask | KeymapStateMask | KeyPressMask | KeyReleaseMask |
194 StructureNotifyMask | SubstructureNotifyMask | VisibilityChangeMask |
195 (op==lstTrueObj ? ButtonMotionMask | PointerMotionMask : 0));
196 break;
197 case 5: /* Get/SetDefaultDisplay */
198 if (LST_PRIMARGC > 1) {
199 op = LST_PRIMARG(1);
200 if (op == lstNilObj || op == lstFalseObj) dpy = NULL;
201 else {
202 if (!(xi = getX11(op))) return NULL;
203 if (xi->subtype != X11_DISPLAY || !xi->handle) return NULL;
204 dpy = (Display *)xi->handle;
206 if (dpy != curDisp) {
207 curDisp = dpy; eqHead = eqTail = eqCount = 0;
208 /*if (cmapIsHere) {}*/
209 Window win = XCreateSimpleWindow(curDisp, DefaultRootWindow(curDisp), 0, 0, 1, 1, 0,
210 BlackPixel(curDisp, DefaultScreen(curDisp)), BlackPixel(curDisp, DefaultScreen(curDisp)));
211 /*fprintf(stderr, "%p\n", (void *)win);*/
212 curCMap = XCreateColormap(curDisp, win, DefaultVisual(curDisp, DefaultScreen(curDisp)), AllocNone);
213 XDestroyWindow(curDisp, win);
214 cmapIsHere = 1;
216 } else {
217 if (curDisp) res = newX11(curDisp, X11_DISPLAY, NULL);
219 break;
220 case 6: /* GetEvent */
221 if (!curDisp) return NULL;
222 x11LoopStep();
223 if (eqCount > 0) res = x11GetEvent();
224 break;
225 case 7: { /* XCreateSimpleWindow parent x y wdt hgt bgcolor */
226 if (LST_PRIMARGC < 7 || !curDisp) return NULL;
227 int x, y, w, h, bc;
228 Window parent;
229 if (LST_PRIMARG(1) == lstNilObj) {
230 parent = DefaultRootWindow(curDisp);
231 } else {
232 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
233 if (xi->subtype != X11_WINDOW || !xi->handle) return NULL;
234 parent = (Window)xi->handle;
236 op = LST_PRIMARG(2); if (!LST_IS_SMALLINT(op)) return NULL;
237 x = lstIntValue(op);
238 op = LST_PRIMARG(3); if (!LST_IS_SMALLINT(op)) return NULL;
239 y = lstIntValue(op);
240 op = LST_PRIMARG(4); if (!LST_IS_SMALLINT(op)) return NULL;
241 w = lstIntValue(op);
242 op = LST_PRIMARG(5); if (!LST_IS_SMALLINT(op)) return NULL;
243 h = lstIntValue(op);
244 op = LST_PRIMARG(6); if (!LST_IS_SMALLINT(op)) return NULL;
245 bc = lstIntValue(op);
246 Window win = XCreateSimpleWindow(curDisp, parent, x, y, w, h, 0, bc, bc);
247 res = newX11((void *)win, X11_WINDOW, killWindow);
248 if ((xi = getX11(res))) xi->dpy = curDisp;
249 break; }
250 case 8: { /* XMapWindow win */
251 if (!curDisp) return NULL;
252 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
253 if (xi->subtype != X11_WINDOW || !xi->handle || xi->dpy != curDisp) return NULL;
254 XMapWindow(curDisp, (Window)xi->handle);
255 break; }
256 case 9: { /* XUnmapWindow win */
257 if (!curDisp) return NULL;
258 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
259 if (xi->subtype != X11_WINDOW || !xi->handle || xi->dpy != curDisp) return NULL;
260 XUnmapWindow(curDisp, (Window)xi->handle);
261 break; }
262 case 10: { /* XCreateGC window [fgcolor] */
263 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
264 Window parent;
265 if (LST_PRIMARG(1) == lstNilObj) {
266 parent = DefaultRootWindow(curDisp);
267 } else {
268 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
269 if (xi->subtype != X11_WINDOW || !xi->handle) return NULL;
270 parent = (Window)xi->handle;
272 GC gc = XCreateGC(curDisp, parent, 0, NULL);
273 if (LST_PRIMARGC > 2) {
274 op = LST_PRIMARG(2); if (!LST_IS_SMALLINT(op)) return NULL;
275 int fc = lstIntValue(op);
276 XSetForeground(curDisp, gc, fc);
278 res = newX11((void *)gc, X11_GC, killGC);
279 if ((xi = getX11(res))) xi->dpy = curDisp;
280 break; }
281 case 11: { /* newColor r g b */
282 if (!curDisp || LST_PRIMARGC < 4) return NULL;
284 op = LST_PRIMARG(1); if (!LST_IS_SMALLINT(op)) return NULL;
285 int r = lstIntValue(op);
286 op = LST_PRIMARG(2); if (!LST_IS_SMALLINT(op)) return NULL;
287 int g = lstIntValue(op);
288 op = LST_PRIMARG(3); if (!LST_IS_SMALLINT(op)) return NULL;
289 int b = lstIntValue(op);
291 XColor cc;
292 /*cc.red = r; cc.green = g; cc.blue = b;*/
293 if (getColor(LST_PRIMARG(1), &cc.red)) return NULL;
294 if (getColor(LST_PRIMARG(2), &cc.green)) return NULL;
295 if (getColor(LST_PRIMARG(3), &cc.blue)) return NULL;
296 /*Status rc = */XAllocColor(curDisp, curCMap, &cc);
297 /*fprintf(stderr, "rc=%d; r=%d; g=%d; b=%d; pixel=%p\n", (int)rc, r, g, b, (void *)cc.pixel);*/
298 int pix = cc.pixel;
299 /*if (rc == 0)*/ res = lstNewInteger(pix);
300 break; }
301 case 12: { /* SetColor flag gc color */
302 if (!curDisp || LST_PRIMARGC < 4) return NULL;
303 if (!(xi = getX11(LST_PRIMARG(2)))) return NULL;
304 if (xi->subtype != X11_GC || !xi->handle) return NULL;
305 GC gc = (GC)xi->handle;
306 op = LST_PRIMARG(3); if (!LST_IS_SMALLINT(op)) return NULL;
307 int clr = lstIntValue(op);
308 op = LST_PRIMARG(1);
309 if (op == lstTrueObj) XSetForeground(curDisp, gc, clr);
310 else XSetBackground(curDisp, gc, clr);
311 break; }
312 case 13: { /* XDrawLine wid gc x0 y0 x1 y1 */
313 if (LST_PRIMARGC < 7 || !curDisp) return NULL;
314 int x0, y0, x1, y1;
315 Window parent;
316 if (LST_PRIMARG(1) == lstNilObj) {
317 parent = DefaultRootWindow(curDisp);
318 } else {
319 if (!(xi = getX11(LST_PRIMARG(1)))) return NULL;
320 if (xi->subtype != X11_WINDOW || !xi->handle) return NULL;
321 parent = (Window)xi->handle;
323 if (!(xi = getX11(LST_PRIMARG(2)))) return NULL;
324 if (xi->subtype != X11_GC || !xi->handle) return NULL;
325 GC gc = (GC)xi->handle;
326 op = LST_PRIMARG(3); if (!LST_IS_SMALLINT(op)) return NULL;
327 x0 = lstIntValue(op);
328 op = LST_PRIMARG(4); if (!LST_IS_SMALLINT(op)) return NULL;
329 y0 = lstIntValue(op);
330 op = LST_PRIMARG(5); if (!LST_IS_SMALLINT(op)) return NULL;
331 x1 = lstIntValue(op);
332 op = LST_PRIMARG(6); if (!LST_IS_SMALLINT(op)) return NULL;
333 y1 = lstIntValue(op);
334 XDrawLine(curDisp, parent, gc, x0, y0, x1, y1);
335 break; }
336 case 200: { /* wid2str */
337 if (!(xi = getX11(LST_PRIMARG(1)))) break;
338 if (!xi->handle) break;
339 static char buf[128];
340 sprintf(buf, "%p", (void *)xi->handle);
341 res = lstNewString(buf);
342 break; }
343 case 201: { /* SameWID w0 w1 */
344 if (LST_PRIMARGC < 3) return NULL;
345 if (!(xi = getX11(LST_PRIMARG(1)))) break;
346 if (xi->subtype != X11_WINDOW || !xi->handle) break;
347 Window w0 = (Window)xi->handle;
348 if (!(xi = getX11(LST_PRIMARG(2)))) break;
349 if (xi->subtype != X11_WINDOW || !xi->handle) break;
350 Window w1 = (Window)xi->handle;
351 res = w0==w1 ? lstTrueObj : lstFalseObj;
352 break; }
353 default: return NULL;
355 return res;
359 void x11LoopStep (void) {
360 if (!curDisp) return;
361 /*x11_fd = ConnectionNumber(dis);*/
362 while (XPending(curDisp) > 0) {
363 if (eqCount >= MAX_QUEUE) return;
364 XNextEvent(curDisp, &eQueue[eqTail++]);
365 if (eqTail == MAX_QUEUE) eqTail = 0;
366 ++eqCount;
371 int x11HasEvent (void) {
372 return eqCount > 0;
376 static struct {
377 const char *name;
378 lstObject *sym; /* not need to register as root, 'cause symbols will not disappear */
379 } eventNames[] = {
380 {"MotionNotify", 0},
381 {"ButtonPress", 0},
382 {"ButtonRelease", 0},
383 {"EnterNotify", 0},
384 {"LeaveNotify", 0},
385 {"Expose", 0},
386 {"FocusIn", 0},
387 {"FocusOut", 0},
388 {"KeymapNotify", 0},
389 {"KeyPress", 0},
390 {"KeyRelease", 0},
391 {"MapNotify", 0},
392 {"UnmapNotify", 0},
393 {"VisibilityNotify", 0},
394 {"DestroyNotify", 0},
398 lstObject *x11GetEvent (void) {
399 LST_ENTER_BLOCK();
400 LST_NEW_TEMP(res);
401 while (eqCount > 0) {
402 XEvent *e = &eQueue[eqHead++];
403 if (eqHead == MAX_QUEUE) eqHead = 0;
404 --eqCount;
405 switch (e->type) {
406 case MotionNotify: {
407 XMotionEvent *ee = (XMotionEvent *)e;
408 /* type, window, x, y, keymask, xroot, yroot */
409 res = lstNewArray(7);
410 res->data[0] = eventNames[0].sym;
411 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
412 res->data[2] = lstNewInteger(ee->x);
413 res->data[3] = lstNewInteger(ee->y);
414 res->data[4] = lstNewInteger(ee->state);
415 res->data[5] = lstNewInteger(ee->x_root);
416 res->data[6] = lstNewInteger(ee->y_root);
417 LST_LEAVE_BLOCK();
418 return res; }
419 case ButtonPress:
420 case ButtonRelease: {
421 /* type, window, x, y, keymask, xroot, yroot, button */
422 XButtonEvent *ee = (XButtonEvent *)e;
423 res = lstNewArray(8);
424 res->data[0] = e->type==ButtonPress ? eventNames[1].sym : eventNames[2].sym;
425 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
426 res->data[2] = lstNewInteger(ee->x);
427 res->data[3] = lstNewInteger(ee->y);
428 res->data[4] = lstNewInteger(ee->state);
429 res->data[5] = lstNewInteger(ee->x_root);
430 res->data[6] = lstNewInteger(ee->y_root);
431 res->data[7] = lstNewInteger(ee->button);
432 LST_LEAVE_BLOCK();
433 return res; }
434 case EnterNotify:
435 case LeaveNotify: {
436 /* type window focus */
437 XCrossingEvent *ee = (XCrossingEvent *)e;
438 res = lstNewArray(3);
439 res->data[0] = e->type==EnterNotify ? eventNames[3].sym : eventNames[4].sym;
440 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
441 res->data[2] = ee->focus ? lstTrueObj : lstFalseObj;
442 LST_LEAVE_BLOCK();
443 return res; }
444 case Expose: {
445 /* type window x y wdt hgt count */
446 XExposeEvent *ee = (XExposeEvent *)e;
447 res = lstNewArray(7);
448 res->data[0] = eventNames[5].sym;
449 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
450 res->data[2] = lstNewInteger(ee->x);
451 res->data[3] = lstNewInteger(ee->y);
452 res->data[4] = lstNewInteger(ee->width);
453 res->data[5] = lstNewInteger(ee->height);
454 res->data[6] = lstNewInteger(ee->count);
455 LST_LEAVE_BLOCK();
456 return res; }
457 case FocusIn:
458 case FocusOut: {
459 /* type window */
460 XFocusChangeEvent *ee = (XFocusChangeEvent *)e;
461 res = lstNewArray(2);
462 res->data[0] = e->type==FocusIn ? eventNames[6].sym : eventNames[7].sym;
463 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
464 LST_LEAVE_BLOCK();
465 return res; }
466 break;
467 case KeymapNotify:
468 break;
469 case KeyPress:
470 case KeyRelease: {
471 /*TODO: i18n */
472 static char keyName[32];
473 KeySym key;
474 /* type window x, y, keymask, xroot, yroot, key */
475 XKeyEvent *ee = (XKeyEvent *)e;
476 res = lstNewArray(9);
477 res->data[0] = e->type==KeyPress ? eventNames[9].sym : eventNames[10].sym;
478 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
479 res->data[2] = lstNewInteger(ee->x);
480 res->data[3] = lstNewInteger(ee->y);
481 res->data[4] = lstNewInteger(ee->state);
482 res->data[5] = lstNewInteger(ee->x_root);
483 res->data[6] = lstNewInteger(ee->y_root);
484 res->data[7] = lstNewInteger(ee->keycode);
485 int xslen = XLookupString(ee, keyName, sizeof(keyName), &key, 0);
486 if (xslen < 0) xslen = 0;
487 keyName[xslen] = '\0';
488 /*fprintf(stderr, "[%s]\n", keyName);*/
489 if (xslen == 1) {
490 res->data[8] = lstNewChar(keyName[0]);
491 } else {
492 res->data[8] = lstNewString(keyName);
494 LST_LEAVE_BLOCK();
495 return res; }
496 break;
497 case MapNotify: {
498 /* type, window */
499 XMapEvent *ee = (XMapEvent *)e;
500 res = lstNewArray(2);
501 res->data[0] = eventNames[11].sym;
502 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
503 LST_LEAVE_BLOCK();
504 return res; }
505 case UnmapNotify: {
506 /* type, window */
507 XMapEvent *ee = (XMapEvent *)e;
508 res = lstNewArray(2);
509 res->data[0] = eventNames[12].sym;
510 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
511 LST_LEAVE_BLOCK();
512 return res; }
513 case VisibilityNotify: {
514 /* type, window visflag */
515 XVisibilityEvent *ee = (XVisibilityEvent *)e;
516 res = lstNewArray(3);
517 res->data[0] = eventNames[13].sym;
518 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
519 res->data[2] = ee->state==VisibilityFullyObscured ? lstNilObj :
520 (ee->state==VisibilityPartiallyObscured ? lstFalseObj : lstTrueObj);
521 LST_LEAVE_BLOCK();
522 return res; }
523 case DestroyNotify: {
524 /* type, window */
525 XMapEvent *ee = (XMapEvent *)e;
526 res = lstNewArray(2);
527 res->data[0] = eventNames[14].sym;
528 res->data[1] = newX11((void *)ee->window, X11_WINDOW, NULL);
529 LST_LEAVE_BLOCK();
530 return res; }
533 return lstNilObj;
537 static const LSTExtPrimitiveTable primTbl[] = {
538 {"X11Do", lpX11Do, NULL},
539 {0}};
542 void lstInitX11Library (void) {
543 int f;
544 for (f = 0; eventNames[f].name; ++f) {
545 eventNames[f].sym = lstNewSymbol(eventNames[f].name);
546 lstAddStaticRoot(&eventNames[f].sym);
550 void lstInitPrimitivesX11 (void) {
551 lstRegisterExtPrimitiveTable(primTbl);