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.
16 #include <X11/keysym.h>
17 #include <X11/Xatom.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
[] = {
43 static Display
*curDisp
= NULL
;
44 static Colormap curCMap
;
45 static int cmapIsHere
= 0;
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
);
65 static void killDisplay (X11Info
*i
) {
67 XCloseDisplay((Display
*)i
->handle
);
73 static void killWindow (X11Info
*i
) {
75 XDestroyWindow(i
->dpy
, (Window
)i
->handle
);
81 static void killGC (X11Info
*i
) {
83 XFreeGC(i
->dpy
, (GC
)i
->handle
);
89 LST_FINALIZER(lpX11Finalizer
) {
90 X11Info
*re
= (X11Info
*)udata
;
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; }
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");
108 re
->subtype
= subtype
;
109 re
->handle
= (intptr_t)handle
;
111 lstSetFinalizer(res
, lpX11Finalizer
, re
);
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
;
124 static int getColor (lstObject
*o
, unsigned short *c
) {
126 if (LST_IS_SMALLINT(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);
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
;
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
]);
155 case 1: { /* XOpenDisplay [name] */
156 if (LST_PRIMARGC
> 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
);
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; }
176 case 3: /* BlackWhitePixel nil/false==black */
177 if (!curDisp
) return NULL
;
179 if (op
== lstNilObj
|| op
== lstFalseObj
) tmp
= BlackPixel(curDisp
, DefaultScreen(curDisp
));
180 else tmp
= WhitePixel(curDisp
, DefaultScreen(curDisp
));
181 res
= lstNewInteger(tmp
);
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
;
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));
197 case 5: /* Get/SetDefaultDisplay */
198 if (LST_PRIMARGC
> 1) {
200 if (op
== lstNilObj
|| op
== lstFalseObj
) dpy
= NULL
;
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
);
217 if (curDisp
) res
= newX11(curDisp
, X11_DISPLAY
, NULL
);
220 case 6: /* GetEvent */
221 if (!curDisp
) return NULL
;
223 if (eqCount
> 0) res
= x11GetEvent();
225 case 7: { /* XCreateSimpleWindow parent x y wdt hgt bgcolor */
226 if (LST_PRIMARGC
< 7 || !curDisp
) return NULL
;
229 if (LST_PRIMARG(1) == lstNilObj
) {
230 parent
= DefaultRootWindow(curDisp
);
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
;
238 op
= LST_PRIMARG(3); if (!LST_IS_SMALLINT(op
)) return NULL
;
240 op
= LST_PRIMARG(4); if (!LST_IS_SMALLINT(op
)) return NULL
;
242 op
= LST_PRIMARG(5); if (!LST_IS_SMALLINT(op
)) return NULL
;
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
;
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
);
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
);
262 case 10: { /* XCreateGC window [fgcolor] */
263 if (LST_PRIMARGC
< 2 || !curDisp
) return NULL
;
265 if (LST_PRIMARG(1) == lstNilObj
) {
266 parent
= DefaultRootWindow(curDisp
);
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
;
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);
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);*/
299 /*if (rc == 0)*/ res
= lstNewInteger(pix
);
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
);
309 if (op
== lstTrueObj
) XSetForeground(curDisp
, gc
, clr
);
310 else XSetBackground(curDisp
, gc
, clr
);
312 case 13: { /* XDrawLine wid gc x0 y0 x1 y1 */
313 if (LST_PRIMARGC
< 7 || !curDisp
) return NULL
;
316 if (LST_PRIMARG(1) == lstNilObj
) {
317 parent
= DefaultRootWindow(curDisp
);
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
);
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
);
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
;
353 default: return NULL
;
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;
371 int x11HasEvent (void) {
378 lstObject
*sym
; /* not need to register as root, 'cause symbols will not disappear */
382 {"ButtonRelease", 0},
393 {"VisibilityNotify", 0},
394 {"DestroyNotify", 0},
398 lstObject
*x11GetEvent (void) {
401 while (eqCount
> 0) {
402 XEvent
*e
= &eQueue
[eqHead
++];
403 if (eqHead
== MAX_QUEUE
) eqHead
= 0;
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
);
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
);
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
;
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
);
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
);
472 static char keyName
[32];
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);*/
490 res
->data
[8] = lstNewChar(keyName
[0]);
492 res
->data
[8] = lstNewString(keyName
);
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
);
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
);
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
);
523 case DestroyNotify
: {
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
);
537 static const LSTExtPrimitiveTable primTbl
[] = {
538 {"X11Do", lpX11Do
, NULL
},
542 void lstInitX11Library (void) {
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
);