better reports about failed primitive; X11 fixes (again)
[k8lst.git] / src / primlib / x11lib / lstpl_x11.c
blob017e42dd67de9c30c1702f4dd668e61d598463c7
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>
21 #include <X11/Xft/Xft.h>
23 #include "lstcore/k8lst.h"
24 #include "lstpl_x11.h"
27 #ifdef DEBUG
28 # define dprintf(...) fprintf(stderr, __VA_ARGS__)
29 #else
30 # define dprintf(...)
31 #endif
34 /*#define DEBUG_EVENTS*/
37 static const char *x11ObjId = "X11Handle";
38 static const char *x11SubId[] = {
39 "Window",
40 "GC",
41 "XftFont",
42 "XftDraw",
43 "XftColor",
48 enum {
49 X11_WINDOW,
50 X11_GC,
51 X11_XFTFONT,
52 X11_XFTDRAW,
53 X11_XFTCOLOR,
54 X11_MAXSUB
58 static int initialized = 0;
59 static Display *curDisp = NULL;
60 static Colormap curCMap;
62 static unsigned long blackPixel;
63 static unsigned long whitePixel;
65 static int screen;
66 static int rootWin;
67 static Visual *vis = NULL;
68 /*static Window intrWin;*/
69 static Atom wmDeleteWindow;
72 #define MAX_QUEUE 128
73 static XEvent eQueue[MAX_QUEUE];
74 static int eqHead = 0;
75 static int eqTail = 0;
76 static int eqCount = 0;
78 static int aliveObjects = 0;
81 static void deinitializeX11Subsystem (void) {
82 if (initialized) {
83 if (aliveObjects == 0) {
84 /*XFreeColormap(curDisp, curCMap);*/
85 /*XDestroyWindow(curDisp, intrWin);*/
86 XCloseDisplay(curDisp);
87 curDisp = NULL;
88 eqHead = eqTail = eqCount = 0;
90 initialized = 0;
95 static int initializeX11Subsystem (const char *dispName) {
96 if (initialized) {
97 if (aliveObjects != 0) return -1;
98 deinitializeX11Subsystem();
100 if (!(curDisp = XOpenDisplay(dispName))) return -1;
101 screen = DefaultScreen(curDisp);
102 rootWin = DefaultRootWindow(curDisp);
104 blackPixel = BlackPixel(curDisp, screen);
105 whitePixel = WhitePixel(curDisp, screen);
107 vis = DefaultVisual(curDisp, screen);
108 curCMap = DefaultColormap(curDisp, screen);
109 /*intrWin = XCreateSimpleWindow(curDisp, rootWin, 0, 0, 1, 1, 0, blackPixel, blackPixel);*/
110 /*curCMap = XCreateColormap(curDisp, intrWin, vis, AllocNone);*/
111 eqHead = eqTail = eqCount = 0;
112 wmDeleteWindow = XInternAtom(curDisp, "WM_DELETE_WINDOW", False);
113 initialized = 1;
114 return 0;
118 typedef struct {
119 const char *type;
120 int subtype;
121 int inited;
122 int dontKill;
123 union {
124 Window win;
125 GC gc;
126 XftFont *font;
127 XftDraw *xd;
128 XftColor tc;
130 } X11Object;
133 static void deinitX11Obj (X11Object *x11o) {
134 if (x11o) {
135 if (x11o->inited) {
136 if (!x11o->dontKill) {
137 /*dprintf("deinitializing X11 object; subtype=%s\n", x11SubId[x11o->subtype]);*/
138 /*fprintf(stderr, "deinitializing X11 object; subtype=%s\n", x11SubId[x11o->subtype]);*/
139 switch (x11o->subtype) {
140 case X11_WINDOW: XDestroyWindow(curDisp, x11o->win); break;
141 case X11_GC: XFreeGC(curDisp, x11o->gc); break;
142 case X11_XFTFONT: XftFontClose(curDisp, x11o->font); break;
143 case X11_XFTDRAW: XftDrawDestroy(x11o->xd); break;
144 case X11_XFTCOLOR: XftColorFree(curDisp, vis, curCMap, &x11o->tc);
147 if (--aliveObjects <= 0) {
148 aliveObjects = 0;
149 if (!initialized) {
150 dprintf("closing X11\n");
151 XCloseDisplay(curDisp); curDisp = NULL;
153 } else {
154 dprintf("X11: %d objects left\n", aliveObjects);
156 x11o->inited = 0;
162 LST_FINALIZER(lpX11Finalizer) {
163 X11Object *x11o = (X11Object *)udata;
164 if (x11o) {
165 deinitX11Obj(x11o);
166 free(x11o);
171 static lstObject *newX11 (X11Object **x11o, int subtype) {
172 if (x11o) *x11o = NULL;
173 if (subtype < 0 || subtype >= X11_MAXSUB) return lstNewString("internal error");
174 lstObject *res = lstNewBinary(NULL, 0);
175 X11Object *xo = calloc(1, sizeof(X11Object));
176 if (!xo) return lstNewString("out of memory");
177 xo->type = x11ObjId;
178 xo->subtype = subtype;
179 xo->dontKill = 0;
180 xo->inited = 1; /* MUST! be initialized by caller */
181 lstSetFinalizer(res, lpX11Finalizer, xo);
182 ++aliveObjects;
183 if (x11o) *x11o = xo;
184 return res;
188 static X11Object *getX11 (lstObject *o) {
189 if (!LST_IS_BYTES_EX(o) || LST_SIZE(o) || !o->fin || !o->fin->udata) return NULL;
190 X11Object *x11o = (X11Object *)o->fin->udata;
191 if (x11o->type != x11ObjId) return NULL;
192 return x11o;
196 static X11Object *getX11As (lstObject *o, int subtype) {
197 X11Object *xo = getX11(o);
198 if (!xo || !xo->inited || xo->subtype != subtype) return NULL;
199 return xo;
203 static int getColor (lstObject *o, unsigned short *c) {
204 int cc;
205 if (LST_IS_SMALLINT(o)) {
206 cc = lstIntValue(o);
207 if (cc < 0) cc = 0; else if (cc > 65535) cc = 65535;
208 } else if (o->stclass == lstIntegerClass) {
209 LstLInt n = lstLIntValue(o);
210 if (n < 0) cc = 0; else if (n > 65535) cc = 65535; else cc = n;
211 } else if (o->stclass == lstFloatClass) {
212 LstFloat n = lstFloatValue(o);
213 if (n < 0.0) cc = 0; else if (n >= 1.0) cc = 65535; else cc = (int)(n*65535);
214 } else return -1;
215 *c = cc;
216 return 0;
220 #define GETINT(dest, argn) {\
221 op = LST_PRIMARG(argn);\
222 if (LST_IS_SMALLINT(op)) (dest) = lstIntValue(op);\
223 else if (op->stclass == lstIntegerClass) (dest) = (int)lstLIntValue(op);\
224 else if (op->stclass == lstFloatClass) (dest) = (int)lstFloatValue(op);\
225 else return NULL;\
228 #define GETSTR(dest, argn) {\
229 op = LST_PRIMARG(argn);\
230 if (!LST_IS_BYTES_EX(op)) return NULL;\
231 (dest) = lstGetStringPtr(op);\
234 #define GETWIN(dest, argn) {\
235 op = LST_PRIMARG(argn);\
236 if (op == lstNilObj || op == lstFalseObj) (dest) = rootWin;\
237 else {\
238 if (!(xi = getX11As(op, X11_WINDOW))) return NULL;\
239 (dest) = xi->win;\
243 #define GETGC(dest, argn) {\
244 op = LST_PRIMARG(argn);\
245 if (!(xi = getX11As(op, X11_GC))) return NULL;\
246 (dest) = xi->gc;\
249 #define GETFONT(dest, argn) {\
250 op = LST_PRIMARG(argn);\
251 if (!(xi = getX11As(op, X11_XFTFONT))) return NULL;\
252 (dest) = xi->font;\
255 #define GETDRAW(dest, argn) {\
256 op = LST_PRIMARG(argn);\
257 if (!(xi = getX11As(op, X11_XFTDRAW))) return NULL;\
258 (dest) = xi->xd;\
261 #define GETTC(dest, argn) {\
262 op = LST_PRIMARG(argn);\
263 if (!(xi = getX11As(op, X11_XFTCOLOR))) return NULL;\
264 (dest) = xi->tc;\
268 LST_PRIMFN(lpX11Do) {
269 if (LST_PRIMARGC < 1 || !LST_IS_SMALLINT(LST_PRIMARG(0))) return NULL;
270 int action = lstIntValue(LST_PRIMARG(0)), tmp, act;
271 lstObject *res = lstNilObj, *op;
272 const char *str = NULL;
273 static char buf[128];
274 X11Object *xi;
275 Window win, parent;
276 GC gc;
277 int x, y, w, h, bc;
278 int x0, y0, x1, y1;
279 unsigned int wdt, hgt, border, depth;
280 unsigned long upix;
281 XColor cc;
282 XftDraw *xd;
283 XftFont *font;
284 XRenderColor rcc;
285 XftColor tc;
286 XGlyphInfo info;
287 LstLInt ll0;
288 XWMHints *wmh;
290 /*fprintf(stderr, "X11Do: %d\n", action);*/
291 switch (action) {
292 case 0: /* get subtype name; return string or nil */
293 if (LST_PRIMARGC < 1) return NULL;
294 if ((xi = getX11(LST_PRIMARG(1))) && xi->inited) res = lstNewString(x11SubId[xi->subtype]);
295 break;
296 case 1: /* wid2str */
297 if (LST_PRIMARGC < 2) return NULL;
298 GETWIN(win, 1);
299 sprintf(buf, "%p", (void *)win);
300 res = lstNewString(buf);
301 break;
302 case 2: /* SameWID w0 w1 */
303 if (LST_PRIMARGC < 3) return NULL;
304 GETWIN(win, 1);
305 GETWIN(parent, 2);
306 res = win==parent ? lstTrueObj : lstFalseObj;
307 break;
308 case 5: /* info type */
309 if (LST_PRIMARGC < 2 || !initialized) return NULL;
310 GETINT(tmp, 1);
311 switch (tmp) {
312 case 0: /* black pixel */
313 ll0 = XBlackPixel(curDisp, screen);
314 res = lstNewInteger(ll0);
315 break;
316 case 1: /* white pixel */
317 ll0 = XWhitePixel(curDisp, screen);
318 res = lstNewInteger(ll0);
319 break;
320 case 2: /* all planes */
321 ll0 = XAllPlanes();
322 res = lstNewInteger(ll0);
323 break;
324 case 3: /* connection number */
325 ll0 = XConnectionNumber(curDisp);
326 res = lstNewInteger(ll0);
327 break;
328 case 4: /* depth */
329 ll0 = XDefaultDepth(curDisp, screen);
330 res = lstNewInteger(ll0);
331 break;
332 case 5: /* planes */
333 ll0 = XDisplayPlanes(curDisp, screen);
334 res = lstNewInteger(ll0);
335 break;
336 case 6: /* display name */
337 res = lstNewString(XDisplayString(curDisp));
338 break;
339 case 7: /* vendor name */
340 res = lstNewString(XServerVendor(curDisp));
341 break;
342 case 8: /* release */
343 ll0 = XVendorRelease(curDisp);
344 res = lstNewInteger(ll0);
345 break;
346 case 9: /* width */
347 ll0 = XDisplayHeight(curDisp, screen);
348 res = lstNewInteger(ll0);
349 break;
350 case 10: /* height */
351 ll0 = XDisplayWidth(curDisp, screen);
352 res = lstNewInteger(ll0);
353 break;
354 default: return NULL;
356 break;
358 case 8: /* flush */
359 if (curDisp != NULL) XFlush(curDisp);
360 break;
362 case 10: /* init [dispname]; return true or false */
363 if (LST_PRIMARGC > 1) {
364 op = LST_PRIMARG(1);
365 if (op != lstNilObj) {
366 if (!LST_IS_BYTES_EX(op)) return NULL;
367 str = lstGetStringPtr(op);
370 res = initializeX11Subsystem(str) ? lstFalseObj : lstTrueObj;
371 break;
372 case 11: /* deinit */
373 deinitializeX11Subsystem();
374 break;
376 case 14: /* GetEvent */
377 if (!initialized) return NULL;
378 x11LoopStep();
379 if (eqCount > 0) res = x11GetEvent();
380 break;
382 case 15: /* XSelectInput window interestFlag (no other args for now) */
383 /* interestFlag is not nil, not false, not true: do not get motion events */
384 if (LST_PRIMARGC < 3 || !initialized) return NULL;
385 GETWIN(win, 1);
386 if (win == rootWin) return NULL;
387 op = LST_PRIMARG(2);
388 XSelectInput(curDisp, win, op==lstNilObj||op==lstFalseObj ? 0 :
389 ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
390 ExposureMask | FocusChangeMask | KeymapStateMask | KeyPressMask | KeyReleaseMask |
391 StructureNotifyMask | /*SubstructureNotifyMask |*/ VisibilityChangeMask |
392 (op==lstTrueObj ? ButtonMotionMask | PointerMotionMask : 0));
393 break;
395 case 20: /* XCreateSimpleWindow parent x y wdt hgt bgcolor */
396 if (LST_PRIMARGC < 7 || !initialized) return NULL;
397 GETWIN(parent, 1);
398 GETINT(x, 2);
399 GETINT(y, 3);
400 GETINT(w, 4);
401 GETINT(h, 5);
402 GETINT(bc, 6);
403 win = XCreateSimpleWindow(curDisp, parent, x, y, w, h, 0, bc, bc);
404 if (win <= LastExtensionError) return NULL;
405 XSetWMProtocols(curDisp, win, &wmDeleteWindow, 1);
406 res = newX11(&xi, X11_WINDOW);
407 if (xi) {
408 xi->win = win;
409 if ((wmh = XGetWMHints(curDisp, win))) {
410 wmh->flags = InputHint;
411 wmh->input = False;
412 XSetWMHints(curDisp, win, wmh);
413 XFree(wmh);
415 } else XDestroyWindow(curDisp, win);
416 break;
417 case 21: /* XDestroyWindow win */
418 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
419 if (!(xi = getX11As(LST_PRIMARG(1), X11_WINDOW))) return NULL;
420 /*deinitX11Obj(xi);*/
421 if (xi->inited && !xi->dontKill) {
422 XDestroyWindow(curDisp, xi->win);
423 xi->dontKill = 1;
425 break;
426 case 22: /* XStoreName win name */
427 if (LST_PRIMARGC < 3 || !initialized) return NULL;
428 GETWIN(win, 1);
429 if (LST_PRIMARGC > 2) {
430 op = LST_PRIMARG(2);
431 if (op != lstNilObj) {
432 if (!LST_IS_BYTES_EX(op)) return NULL;
433 str = lstGetStringPtr(op);
436 XStoreName(curDisp, win, str?str:"");
437 break;
438 case 23: /* map/unmap window: win mapflag */
439 if (LST_PRIMARGC < 3 || !initialized) return NULL;
440 GETWIN(win, 1);
441 op = LST_PRIMARG(2);
442 if (op == lstNilObj || op == lstFalseObj) XUnmapWindow(curDisp, win);
443 else XMapWindow(curDisp, win);
444 break;
445 case 24: /* XGetGeometry wid type */
446 if (LST_PRIMARGC < 3) return NULL;
447 GETWIN(win, 1);
448 GETINT(act, 2);
449 if (XGetGeometry(curDisp, win, &parent, &x, &y, &wdt, &hgt, &border, &depth) == BadDrawable) return NULL;
450 switch (act) {
451 case 0: {
452 LST_ENTER_BLOCK();
453 LST_NEW_TEMP(nfo);
454 nfo = lstNewArray(6);
455 nfo->data[0] = lstNewInt((int)x);
456 nfo->data[1] = lstNewInt((int)y);
457 nfo->data[2] = lstNewInt((int)wdt);
458 nfo->data[3] = lstNewInt((int)hgt);
459 nfo->data[4] = lstNewInt((int)border);
460 nfo->data[5] = lstNewInt((int)depth);
461 LST_LEAVE_BLOCK();
462 res = nfo;
463 } break;
464 case 1: res = lstNewInteger((int)x); break;
465 case 2: res = lstNewInteger((int)y); break;
466 case 3: res = lstNewInteger((int)wdt); break;
467 case 4: res = lstNewInteger((int)hgt); break;
468 case 5: res = lstNewInteger((int)border); break;
469 case 6: res = lstNewInteger((int)depth); break;
470 default: return NULL;
472 break;
473 case 25: /* XSetWindowBackground win color */
474 if (LST_PRIMARGC < 3 || !initialized) return NULL;
475 GETWIN(win, 1);
476 GETINT(tmp, 2);
477 upix = tmp;
478 XSetWindowBackground(curDisp, win, upix);
479 break;
480 case 26: /* XClearWindow wid */
481 if (LST_PRIMARGC < 2 || !initialized) return NULL;
482 GETWIN(win, 1);
483 XClearWindow(curDisp, win);
484 break;
485 case 27: /* window movement: type wid nx ny [nw nh] */
486 if (LST_PRIMARGC < 5 || !initialized) return NULL;
487 GETWIN(win, 1);
488 GETINT(tmp, 2);
489 GETINT(x, 3);
490 GETINT(y, 4);
491 switch (tmp) {
492 case 1: /* move */
493 XMoveWindow(curDisp, win, x, y);
494 break;
495 case 2: /* resize */
496 XResizeWindow(curDisp, win, x, y);
497 break;
498 case 3: /* both */
499 if (LST_PRIMARGC < 7) return NULL;
500 GETINT(w, 5);
501 GETINT(h, 6);
502 XMoveResizeWindow(curDisp, win, x, y, w, h);
503 break;
504 default: return NULL;
506 break;
507 case 28: /* InputFocus win[true: query] */
508 if (LST_PRIMARGC < 2 || !initialized) return NULL;
509 op = LST_PRIMARG(1);
510 if (op == lstNilObj || op == lstFalseObj) win = None;
511 else if (op == lstTrueObj) {
512 if (XGetInputFocus(curDisp, &win, &tmp)) break;
513 if (win != None && win != PointerRoot) {
514 res = newX11(&xi, X11_WINDOW);
515 xi->dontKill = 1;
516 xi->win = win;
518 } else {
519 if (!(xi = getX11As(op, X11_WINDOW))) return NULL;
520 win = xi->win;
521 /*fprintf(stderr, "SETTING FOCUS TO: %p\n", (void *)win);*/
522 /*XFlush(curDisp);*/
523 XSetInputFocus(curDisp, win, RevertToParent, CurrentTime);
525 break;
527 case 30: /* XCreateGC window [fgcolor] */
528 if (LST_PRIMARGC < 2 || !initialized) return NULL;
529 GETWIN(win, 1);
530 if (LST_PRIMARGC > 2) {
531 GETINT(tmp, 2);
532 } else {
533 tmp = XWhitePixel(curDisp, screen);
535 gc = XCreateGC(curDisp, win, 0, NULL);
536 XSetForeground(curDisp, gc, tmp);
537 res = newX11(&xi, X11_GC);
538 if (xi) xi->gc = gc; else XFreeGC(curDisp, gc);
539 break;
540 case 31: /* XFreeGC gc */
541 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
542 if (!(xi = getX11As(LST_PRIMARG(1), X11_GC))) return NULL;
543 deinitX11Obj(xi);
544 break;
545 case 32: /* XNewColor r g b */
546 if (LST_PRIMARGC < 4 || !initialized) return NULL;
547 if (getColor(LST_PRIMARG(1), &cc.red)) return NULL;
548 if (getColor(LST_PRIMARG(2), &cc.green)) return NULL;
549 if (getColor(LST_PRIMARG(3), &cc.blue)) return NULL;
550 Status rc = XAllocColor(curDisp, curCMap, &cc);
551 if (rc) {
552 ++aliveObjects;
553 tmp = cc.pixel;
554 res = lstNewInteger(tmp);
556 break;
557 case 33: /* XFreeColor int */
558 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
559 GETINT(tmp, 1);
560 upix = tmp;
561 tmp = XFreeColors(curDisp, curCMap, &upix, 1, 0);
562 if (tmp == 1) {
563 if (--aliveObjects <= 0 && !initialized) {
564 XCloseDisplay(curDisp); curDisp = NULL;
567 break;
568 case 34: /* XSetColor gc color [backflag] */
569 if (LST_PRIMARGC < 3 || !initialized) return NULL;
570 GETGC(gc, 1);
571 GETINT(tmp, 2);
572 if (LST_PRIMARGC > 3) op = LST_PRIMARG(3); else op = lstNilObj;
573 if (op != lstFalseObj && op != lstNilObj) XSetBackground(curDisp, gc, tmp);
574 else XSetForeground(curDisp, gc, tmp);
575 break;
576 case 35: /* XSetLineAttr gc width line cap join */
577 if (LST_PRIMARGC < 6 || !initialized) return NULL;
578 GETGC(gc, 1);
579 GETINT(w, 2);
580 GETINT(tmp, 3);
581 GETINT(x, 4);
582 GETINT(y, 5);
583 if (w < 0) w = 0;
584 switch (tmp) {
585 case 1: tmp = LineDoubleDash; break;
586 case 2: tmp = LineOnOffDash; break;
587 default: tmp = LineSolid; break;
589 switch (x) {
590 case 0: x = CapNotLast; break;
591 /*case 1: x = CapButt; break;*/
592 case 2: x = CapRound; break;
593 case 3: x = CapProjecting; break;
594 default: x = CapButt; break;
596 switch (y) {
597 case 1: y = JoinRound; break;
598 case 2: y = JoinBevel; break;
599 default: y = JoinMiter; break;
601 XSetLineAttributes(curDisp, gc, w, tmp, x, y);
602 break;
604 case 40: /* XDrawPoint wid gc x y */
605 if (LST_PRIMARGC < 5 || !initialized) return NULL;
606 GETWIN(win, 1);
607 GETGC(gc, 2);
608 GETINT(x, 3);
609 GETINT(y, 4);
610 XDrawPoint(curDisp, win, gc, x, y);
611 break;
612 case 41: /* XDrawLine wid gc x0 y0 x1 y1 */
613 if (LST_PRIMARGC < 7 || !initialized) return NULL;
614 GETWIN(win, 1);
615 GETGC(gc, 2);
616 GETINT(x0, 3);
617 GETINT(y0, 4);
618 GETINT(x1, 5);
619 GETINT(y1, 6);
620 XDrawLine(curDisp, win, gc, x0, y0, x1, y1);
621 break;
622 case 42: /* XDrawRect wid gc x0 y0 wdt hgt */
623 if (LST_PRIMARGC < 7 || !initialized) return NULL;
624 GETWIN(win, 1);
625 GETGC(gc, 2);
626 GETINT(x, 3);
627 GETINT(y, 4);
628 GETINT(w, 5);
629 GETINT(h, 6);
630 XDrawRectangle(curDisp, win, gc, x, y, w, h);
631 break;
632 case 43: /* XFillRect wid gc x y w h */
633 if (LST_PRIMARGC < 7 || !initialized) return NULL;
634 GETWIN(win, 1);
635 GETGC(gc, 2);
636 GETINT(x, 3);
637 GETINT(y, 4);
638 GETINT(w, 5);
639 GETINT(h, 6);
640 XFillRectangle(curDisp, win, gc, x, y, w, h);
641 break;
643 case 50: /* XftFontOpen name */
644 if (LST_PRIMARGC < 2 || !initialized) return NULL;
645 GETSTR(str, 1);
646 font = XftFontOpenName(curDisp, screen, str);
647 if (!font) return NULL;
648 res = newX11(&xi, X11_XFTFONT);
649 if (xi) xi->font = font; else XftFontClose(curDisp, font);
650 break;
651 case 51: /* XftFontClose font */
652 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
653 if (!(xi = getX11As(LST_PRIMARG(1), X11_XFTFONT))) return NULL;
654 deinitX11Obj(xi);
655 break;
656 case 52: /* XftDrawCreate win */
657 if (LST_PRIMARGC < 2 || !initialized) return NULL;
658 GETWIN(win, 1);
659 xd = XftDrawCreate(curDisp, win, vis, curCMap);
660 if (!xd) return NULL;
661 res = newX11(&xi, X11_XFTDRAW);
662 if (xi) xi->xd = xd; else XftDrawDestroy(xd);
663 break;
664 case 53: /* XftDrawDestroy drw */
665 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
666 if (!(xi = getX11As(LST_PRIMARG(1), X11_XFTDRAW))) return NULL;
667 deinitX11Obj(xi);
668 break;
669 case 54: /* XNewTextColor r g b */
670 if (LST_PRIMARGC < 4 || !initialized) return NULL;
671 if (getColor(LST_PRIMARG(1), &rcc.red)) return NULL;
672 if (getColor(LST_PRIMARG(2), &rcc.green)) return NULL;
673 if (getColor(LST_PRIMARG(3), &rcc.blue)) return NULL;
674 rcc.alpha = 0;
675 XftColorAllocValue(curDisp, vis, curCMap, &rcc, &tc);
676 res = newX11(&xi, X11_XFTCOLOR);
677 if (xi) xi->tc = tc; else XftColorFree(curDisp, vis, curCMap, &tc);
678 break;
679 case 55: /* XFreeTextColor tc */
680 if (LST_PRIMARGC < 2 || !curDisp) return NULL;
681 if (!(xi = getX11As(LST_PRIMARG(1), X11_XFTCOLOR))) return NULL;
682 deinitX11Obj(xi);
683 break;
684 case 56: /* XftFontDraw drw color font x y text */
685 if (LST_PRIMARGC < 7 || !initialized) return NULL;
686 GETDRAW(xd, 1);
687 GETTC(tc, 2);
688 GETFONT(font, 3);
689 GETINT(x, 4);
690 GETINT(y, 5);
691 GETSTR(str, 6);
692 XftDrawStringUtf8(xd, &tc, font, x, y, (const XftChar8 *)(str), strlen(str)); /*FIXME: use object size here*/
693 break;
694 case 57: /* XftTextExtents font text [what] */
695 if (LST_PRIMARGC < 3 || !initialized) return NULL;
696 GETFONT(font, 1);
697 GETSTR(str, 2);
698 XftTextExtentsUtf8(curDisp, font, (const XftChar8 *)(str), strlen(str), &info); /*FIXME: use object size here*/
699 if (LST_PRIMARGC > 3) {
700 op = LST_PRIMARG(3); if (!LST_IS_SMALLINT(op)) return NULL;
701 switch (lstIntValue(op)) {
702 case 0: goto xftTextExtentsGetAll;
703 case 1: res = lstNewInt((int)info.width); break;
704 case 2: res = lstNewInt((int)info.height); break;
705 case 3: res = lstNewInt(info.x); break;
706 case 4: res = lstNewInt(info.y); break;
707 case 5: res = lstNewInt(info.xOff); break;
708 case 6: res = lstNewInt(info.yOff); break;
709 default: return NULL;
711 } else {
712 xftTextExtentsGetAll: (void)0;
713 LST_ENTER_BLOCK();
714 LST_NEW_TEMP(nfo);
715 nfo = lstNewArray(6);
716 nfo->data[0] = lstNewInt((int)info.width);
717 nfo->data[1] = lstNewInt((int)info.height);
718 nfo->data[2] = lstNewInt(info.x);
719 nfo->data[3] = lstNewInt(info.y);
720 nfo->data[4] = lstNewInt(info.xOff);
721 nfo->data[5] = lstNewInt(info.yOff);
722 LST_LEAVE_BLOCK();
723 res = nfo;
725 break;
726 case 80: /* withdraw/iconify win true:withdraw */
727 if (LST_PRIMARGC < 3 || !initialized) return NULL;
728 GETWIN(win, 1);
729 op = LST_PRIMARG(2);
730 if (op == lstTrueObj) XWithdrawWindow(curDisp, win, screen);
731 else XIconifyWindow(curDisp, win, screen);
732 break;
733 case 81: /* wmhints win hintid val ... */
734 if (LST_PRIMARGC < 4 || !initialized) return NULL;
735 GETWIN(win, 1);
736 if (win == rootWin) return NULL;
737 GETINT(act, 2);
738 op = LST_PRIMARG(3);
739 wmh = XGetWMHints(curDisp, win);
740 if (!wmh) return NULL;
741 switch (act) {
742 case 0: /* input type */
743 wmh->flags |= InputHint;
744 wmh->input = op==lstNilObj||op==lstFalseObj ? False : True;
745 break;
746 case 1: /* urgency */
747 if (op == lstNilObj || op == lstFalseObj) wmh->flags &= ~(XUrgencyHint);
748 else wmh->flags |= XUrgencyHint;
749 break;
750 default: XFree(wmh); wmh = NULL; res = NULL; break;
752 if (wmh) {
753 XSetWMHints(curDisp, win, wmh);
754 XFree(wmh);
756 break;
757 default: return NULL;
759 /*if (curDisp) XFlush(curDisp);*/
760 return res;
764 void x11LoopStep (void) {
765 if (!curDisp) return;
766 /*x11_fd = ConnectionNumber(dis);*/
767 while (XPending(curDisp) > 0) {
768 if (eqCount >= MAX_QUEUE) return;
769 XNextEvent(curDisp, &eQueue[eqTail++]);
770 if (eqTail == MAX_QUEUE) eqTail = 0;
771 ++eqCount;
776 int x11HasEvent (void) {
777 return eqCount > 0;
781 #ifdef DEBUG_EVENTS
782 const struct {
783 const char *name;
784 int id;
785 } dbgEventNames[] = {
786 {"KeyPress", 2},
787 {"KeyRelease", 3},
788 {"ButtonPress", 4},
789 {"ButtonRelease", 5},
790 {"MotionNotify", 6},
791 {"EnterNotify", 7},
792 {"LeaveNotify", 8},
793 {"FocusIn", 9},
794 {"FocusOut", 10},
795 {"KeymapNotify", 11},
796 {"Expose", 12},
797 {"GraphicsExpose", 13},
798 {"NoExpose", 14},
799 {"VisibilityNotify", 15},
800 {"CreateNotify", 16},
801 {"DestroyNotify", 17},
802 {"UnmapNotify", 18},
803 {"MapNotify", 19},
804 {"MapRequest", 20},
805 {"ReparentNotify", 21},
806 {"ConfigureNotify", 22},
807 {"ConfigureRequest", 23},
808 {"GravityNotify", 24},
809 {"ResizeRequest", 25},
810 {"CirculateNotify", 26},
811 {"CirculateRequest", 27},
812 {"PropertyNotify", 28},
813 {"SelectionClear", 29},
814 {"SelectionRequest", 30},
815 {"SelectionNotify", 31},
816 {"ColormapNotify", 32},
817 {"ClientMessage", 33},
818 {"MappingNotify", 34},
819 {"GenericEvent", 35},
824 const char *dbgEentName (int id) {
825 int f;
826 for (f = 0; dbgEventNames[f].name; ++f) {
827 if (dbgEventNames[f].id == id) return dbgEventNames[f].name;
829 return "UnknownEventType";
831 #endif
834 static struct {
835 const char *name;
836 lstObject *sym; /* not need to register as root, 'cause symbols will not disappear */
837 } eventNames[] = {
838 /* 0*/{"MotionNotify", 0},
839 /* 1*/{"ButtonPress", 0},
840 /* 2*/{"ButtonRelease", 0},
841 /* 3*/{"EnterNotify", 0},
842 /* 4*/{"LeaveNotify", 0},
843 /* 5*/{"Expose", 0},
844 /* 6*/{"FocusIn", 0},
845 /* 7*/{"FocusOut", 0},
846 /* 8*/{"KeymapNotify", 0},
847 /* 9*/{"KeyPress", 0},
848 /*10*/{"KeyRelease", 0},
849 /*11*/{"MapNotify", 0},
850 /*12*/{"UnmapNotify", 0},
851 /*13*/{"VisibilityNotify", 0},
852 /*14*/{"DestroyNotify", 0},
853 /*15*/{"ConfigureNotify", 0},
854 /* WM specials */
855 /*16*/{"WMCloseRequest", 0},
859 lstObject *x11GetEvent (void) {
860 LST_ENTER_BLOCK();
861 LST_NEW_TEMP(res);
862 LST_NEW_TEMP(win);
863 while (eqCount > 0) {
864 X11Object *xi;
865 XEvent *e = &eQueue[eqHead++];
866 if (eqHead == MAX_QUEUE) eqHead = 0;
867 --eqCount;
868 #ifdef DEBUG_EVENTS
869 fprintf(stderr, "GETEVENT: event=%d; win=%p (%s)\n", e->type, (void *)e->xany.window, dbgEentName(e->type));
870 #endif
871 win = newX11(&xi, X11_WINDOW);
872 xi->dontKill = 1;
873 xi->win = e->xany.window;
874 switch (e->type) {
875 case MotionNotify: {
876 XMotionEvent *ee = (XMotionEvent *)e;
877 /* type, window, x, y, keymask, xroot, yroot */
878 res = lstNewArray(7);
879 res->data[0] = eventNames[0].sym;
880 res->data[1] = win;
881 res->data[2] = lstNewInteger(ee->x);
882 res->data[3] = lstNewInteger(ee->y);
883 res->data[4] = lstNewInteger(ee->state);
884 res->data[5] = lstNewInteger(ee->x_root);
885 res->data[6] = lstNewInteger(ee->y_root);
886 LST_LEAVE_BLOCK();
887 return res; }
888 case ButtonPress:
889 case ButtonRelease: {
890 /* type, window, x, y, keymask, xroot, yroot, button */
891 XButtonEvent *ee = (XButtonEvent *)e;
892 res = lstNewArray(8);
893 res->data[0] = e->type==ButtonPress ? eventNames[1].sym : eventNames[2].sym;
894 res->data[1] = win;
895 res->data[2] = lstNewInteger(ee->x);
896 res->data[3] = lstNewInteger(ee->y);
897 res->data[4] = lstNewInteger(ee->state);
898 res->data[5] = lstNewInteger(ee->x_root);
899 res->data[6] = lstNewInteger(ee->y_root);
900 res->data[7] = lstNewInteger(ee->button);
901 LST_LEAVE_BLOCK();
902 return res; }
903 case EnterNotify:
904 case LeaveNotify: {
905 /* type window focus */
906 XCrossingEvent *ee = (XCrossingEvent *)e;
907 res = lstNewArray(3);
908 res->data[0] = e->type==EnterNotify ? eventNames[3].sym : eventNames[4].sym;
909 res->data[1] = win;
910 res->data[2] = ee->focus ? lstTrueObj : lstFalseObj;
911 LST_LEAVE_BLOCK();
912 return res; }
913 case Expose: {
914 /* type window x y wdt hgt count */
915 XExposeEvent *ee = (XExposeEvent *)e;
916 res = lstNewArray(7);
917 res->data[0] = eventNames[5].sym;
918 res->data[1] = win;
919 res->data[2] = lstNewInteger(ee->x);
920 res->data[3] = lstNewInteger(ee->y);
921 res->data[4] = lstNewInteger(ee->width);
922 res->data[5] = lstNewInteger(ee->height);
923 res->data[6] = lstNewInteger(ee->count);
924 LST_LEAVE_BLOCK();
925 return res; }
926 case FocusIn:
927 case FocusOut: {
928 /* type window */
929 /*XFocusChangeEvent *ee = (XFocusChangeEvent *)e;*/
930 res = lstNewArray(2);
931 res->data[0] = e->type==FocusIn ? eventNames[6].sym : eventNames[7].sym;
932 res->data[1] = win;
933 LST_LEAVE_BLOCK();
934 return res; }
935 case KeymapNotify: {
937 res = lstNewArray(2);
938 res->data[0] = eventNames[8].sym;
939 res->data[1] = win;
940 LST_LEAVE_BLOCK();
941 return res;
943 break; }
944 case KeyPress:
945 case KeyRelease: {
946 /*TODO: i18n*/
947 static char keyName[32];
948 KeySym key;
949 /* type window x, y, keymask, xroot, yroot, key */
950 XKeyEvent *ee = (XKeyEvent *)e;
951 res = lstNewArray(9);
952 res->data[0] = e->type==KeyPress ? eventNames[9].sym : eventNames[10].sym;
953 res->data[1] = win;
954 res->data[2] = lstNewInteger(ee->x);
955 res->data[3] = lstNewInteger(ee->y);
956 res->data[4] = lstNewInteger(ee->state);
957 res->data[5] = lstNewInteger(ee->x_root);
958 res->data[6] = lstNewInteger(ee->y_root);
959 res->data[7] = lstNewInteger(ee->keycode);
960 int xslen = XLookupString(ee, keyName, sizeof(keyName), &key, 0);
961 if (xslen < 0) xslen = 0;
962 keyName[xslen] = '\0';
963 /*fprintf(stderr, "[%s]\n", keyName);*/
964 if (xslen == 1) {
965 res->data[8] = lstNewChar(keyName[0]);
966 } else {
967 res->data[8] = lstNewString(keyName);
969 LST_LEAVE_BLOCK();
970 return res; }
971 break;
972 case MapNotify: {
973 /* type, window */
974 /*XMapEvent *ee = (XMapEvent *)e;*/
975 res = lstNewArray(2);
976 res->data[0] = eventNames[11].sym;
977 res->data[1] = win;
978 LST_LEAVE_BLOCK();
979 return res; }
980 case UnmapNotify: {
981 /* type, window */
982 /*XMapEvent *ee = (XMapEvent *)e;*/
983 res = lstNewArray(2);
984 res->data[0] = eventNames[12].sym;
985 res->data[1] = win;
986 LST_LEAVE_BLOCK();
987 return res; }
988 case VisibilityNotify: {
989 /* type, window visflag */
990 XVisibilityEvent *ee = (XVisibilityEvent *)e;
991 res = lstNewArray(3);
992 res->data[0] = eventNames[13].sym;
993 res->data[1] = win;
994 res->data[2] = ee->state==VisibilityFullyObscured ? lstNilObj :
995 (ee->state==VisibilityPartiallyObscured ? lstFalseObj : lstTrueObj);
996 LST_LEAVE_BLOCK();
997 return res; }
998 case DestroyNotify: {
999 /* type, window */
1000 /*XMapEvent *ee = (XMapEvent *)e;*/
1001 res = lstNewArray(2);
1002 res->data[0] = eventNames[14].sym;
1003 res->data[1] = win;
1004 LST_LEAVE_BLOCK();
1005 return res; }
1006 case ConfigureNotify: {
1007 /* type, window */
1008 XConfigureEvent *ee = (XConfigureEvent *)e;
1009 res = lstNewArray(6);
1010 res->data[0] = eventNames[15].sym;
1011 res->data[1] = win;
1012 res->data[2] = lstNewInteger(ee->x);
1013 res->data[3] = lstNewInteger(ee->y);
1014 res->data[4] = lstNewInteger(ee->width);
1015 res->data[5] = lstNewInteger(ee->height);
1016 LST_LEAVE_BLOCK();
1017 return res; }
1018 case ClientMessage: {
1019 XClientMessageEvent *ee = (XClientMessageEvent *)e;
1020 /* Window Manager or something else */
1021 if ((Atom)ee->data.l[0] == wmDeleteWindow) {
1022 res = lstNewArray(2);
1023 res->data[0] = eventNames[16].sym;
1024 res->data[1] = win;
1025 LST_LEAVE_BLOCK();
1026 return res;
1031 LST_LEAVE_BLOCK();
1032 return lstNilObj;
1036 static const LSTExtPrimitiveTable primTbl[] = {
1037 {"X11Do", lpX11Do, NULL},
1038 {0}};
1041 void lstInitX11Library (void) {
1042 int f;
1043 for (f = 0; eventNames[f].name; ++f) {
1044 eventNames[f].sym = lstNewSymbol(eventNames[f].name);
1045 lstAddStaticRoot(&eventNames[f].sym);
1049 void lstInitPrimitivesX11 (void) {
1050 lstRegisterExtPrimitiveTable(primTbl);