fixed fonts, wchar support, window mapping
[xlua.git] / x.c
blobcf8c55b4cf6c444cadb8d29364167aa3d6397965
1 #include <X11/Xlib.h>
2 #include <X11/Xutil.h>
4 #include <stdio.h>
5 #include <stdlib.h>
7 #include <lua.h>
8 #include <lualib.h>
9 #include <lauxlib.h>
10 #include <assert.h>
11 #include "shader.c"
12 #include <string.h>
13 #include <stdint.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
20 extern char **environ;
21 GC gc;
22 Window root;
23 Display *dpy;
24 Colormap cmap;
25 int scr,w,h;
26 int xfd;
27 int maxfd=-1;
29 fd_set rfds,trfds;
31 XFontSet global_fontset = NULL;
33 typedef struct {
34 XFontSet set;
35 XFontStruct *xfont;
36 int ascent;
37 int descent;
38 int height;
39 } myfont_t;
41 typedef struct {
42 int infd, outfd;
43 pid_t pid;
44 } process_t;
46 #define CLEAR(x) memset(&x, 0, sizeof(x))
47 #define MAXFD(fd) (maxfd=fd>maxfd?fd:maxfd)
48 #define CHECKSTR(n) luaL_checkstring(L, n)
49 #define OPTSTR(n,opt) luaL_optstring(L, n, opt)
50 #define CHECKBOOL(n) lua_toboolean(L, n)
51 #define OPTBOOL(n,v) CHECKBOOL(n)
52 #define CHECKINT(n) luaL_checknumber(L, n)
53 #define OPTINT(n,o) luaL_optnumber(L, n, o)
54 #define TOINT(n) lua_tointeger(L, n)
55 #define TOCOLOR(n) lua_tointeger(L, n)
56 #define PUSHNUM(n) lua_pushnumber(L, n)
58 #define CHECKMETA(n,name) checkmeta(L,n,name,name " expected")
59 //(((strcmp(getmeta(L,n), name))?luaL_argerror(L,n,name " expected"):0),lua_touserdata(L,n))
61 /* pointer types */
62 #define CHECKIMG(n) (*(XImage **) CHECKMETA(n,"<image>"))
63 #define CHECKFONT(n) ((myfont_t *) CHECKMETA(n,"<font>"))
65 /* xid types */
66 #define CHECKPIX(n) (*(Pixmap *) CHECKMETA(n, "<pixmap"))
67 #define CHECKWIN(n) (*(Window *) CHECKMETA(n,"<window>"))
68 #define CHECKCURSOR(n) (*(Cursor *) CHECKMETA(n, "<cursor>"))
69 #define OPTFONT(n,opt) (strcmp(getmeta(L,n),"<font>")?opt:(myfont_t *) lua_touserdata(L,n))
70 #define OPTPIX(n,opt) (strcmp(getmeta(L,n),"<pixmap>")?opt:(*(Pixmap *) lua_touserdata(L, n)))
72 #define CHECKDRAW(n) checkdraw(L,n)
73 #define CHECKATOM(a) CHECKINT(a)
74 #define OPTATOM(a,b) OPTINT(a,b)
76 #define METHOD(n) int n(lua_State *L)
78 #define ADOPTWIN(x) newxid(L, x, "<window>", NULL)
79 #define MKPIX(x) newxid(L, x, "<pixmap>", XFreePixmap);
80 #define MKWIN(x) newxid(L, x, "<window>", XDestroyWindow);
81 #define MKATOM(x) lua_pushnumber(L, x)
82 #define MKCURSOR(x) newxid(L, x, "<cursor>", XFreeCursor);
84 #define SET_READ(fd) FD_SET(fd, &rfds)
85 #define UNSET_READ(fd) FD_CLR(fd, &rfds)
87 #define FIXWHXY() \
88 if (w < 0 || h < 0) { \
89 x=y=0; \
90 XGetGeometry(dpy, win, NULL, NULL, NULL, &w, &h, NULL, NULL); \
92 #define FIXWH() \
93 if (w < 0 || h < 0) \
94 XGetGeometry(dpy, win, NULL, NULL, NULL, &w, &h, NULL, NULL);
96 #define SETI(n) { lua_pushnumber(L, n); lua_setfield(L, -2, #n); }
97 #define SETW(n) { ADOPTWIN(n); lua_setfield(L, -2, #n); }
98 #define SETA(n) { MKATOM(n); lua_setfield(L, -2, #n); }
99 #define SETB(n) { lua_pushboolean(L, n); lua_setfield(L, -2, #n); }
101 #define ATTRI(n) { lua_pushnumber(L, a.n); lua_setfield(L, -2, #n); }
102 #define ATTRW(n) { ADOPTWIN(a.n); lua_setfield(L, -2, #n); }
103 #define ATTRA(n) { MKATOM(a.n); lua_setfield(L, -2, #n); }
104 #define ATTRB(n) { lua_pushboolean(L, a.n); lua_setfield(L, -2, #n); }
106 #define EVI(u,n) { lua_pushnumber(L, ev.x##u.n); lua_setfield(L, -2, #n); }
107 #define EVW(u,n) { ADOPTWIN(ev.x##u.n); lua_setfield(L, -2, #n); }
108 #define EVA(u,n) { MKATOM(ev.x##u.n); lua_setfield(L, -2, #n); }
109 #define EVB(u,n) { lua_pushboolean(L, ev.x##u.n); lua_setfield(L, -2, #n); }
112 #define GETP(field,m) lua_getfield(L, idx, #field); if (!lua_isnil(L, -1)) { wa->field = OPTPIX(-1,CHECKINT(-1)); mask |= CW##m; }; lua_pop(L, 1);
113 #define GETI(field,m) lua_getfield(L, idx, #field); if (!lua_isnil(L, -1)) { wa->field = CHECKINT(-1); mask |= CW##m; } lua_pop(L, 1);
114 #define GETB(field,m) lua_getfield(L, idx, #field); if (!lua_isnil(L, -1)) { wa->field = lua_toboolean(L, -1); mask |= CW##m; } lua_pop(L, 1);
115 #define GETC(field,m) lua_getfield(L, idx, #field); if (!lua_isnil(L, -1)) { wa->field = CHECKCURSOR(-1); mask |= CW##m; } lua_pop(L, 1);
117 #define DEBUG(fmt...) { fprintf(stderr, "%d %s(): ", __LINE__, __func__); fprintf(stderr, fmt); fprintf(stderr, "\n"); fflush(stderr); }
118 //#define DEBUG(fmt...)
120 char *getmeta(lua_State *L, int n)
122 const char *s;
123 //assert(n>0);
124 //DEBUG("top %d", lua_gettop(L));
125 if (!lua_getmetatable(L, n)) return "";
126 //DEBUG("top %d", lua_gettop(L));
127 lua_rawget(L, LUA_REGISTRYINDEX);
128 s = lua_tostring(L, -1);
129 //DEBUG("getmeta(%d)=%s",n,s);
130 lua_pop(L, 1);
131 if (!s) s="";
132 //DEBUG("top %d", lua_gettop(L));
133 return (char*)s;
136 void *checkmeta(lua_State *L, int n, char *name, char *expect)
138 void *d;
139 //DEBUG("top %d", lua_gettop(L));
140 if (strcmp(getmeta(L,n), name))
141 luaL_argerror(L,n,expect);
142 d = lua_touserdata(L, n);
143 //DEBUG("top %d", lua_gettop(L));
144 assert(d!=NULL);
145 return d;
149 void kill_xid(lua_State *L, XID xid)
151 lua_getfield(L, LUA_REGISTRYINDEX, "destruct");
152 lua_pushnil(L);
153 lua_rawseti(L, -2, xid);
155 lua_getfield(L, LUA_REGISTRYINDEX, "xid");
156 lua_pushnil(L);
157 lua_rawseti(L, -2, xid);
161 /* create new XID pointer */
162 /* must return from lua function! xids may be gcable by their destructors (xid table is weak) */
163 int newxid(lua_State *L, XID xid, const char *name, void *destruct)
165 XID *xidp;
166 if (!xid) { lua_pushnil(L); return 1; };
167 assert(xid!=0);
168 DEBUG("%d %p", lua_gettop(L), destruct);
169 lua_getfield(L, LUA_REGISTRYINDEX, "xid");
170 lua_rawgeti(L, -1, xid);
171 if (!lua_isnil(L, -1)) {
172 DEBUG("cache hit");
173 XID *x = lua_touserdata(L, -1);
174 lua_remove(L, -2);
175 DEBUG("%d %p", lua_gettop(L), destruct);
176 if (*x) return 1; /* got valid one */
178 lua_pop(L, 1);
179 /* -1 xid table */
180 xidp = lua_newuserdata(L, sizeof(XID));
181 *xidp = xid;
183 /* -2 xid table, -1 = udata */
184 /* set meta */
185 lua_getfield(L, LUA_REGISTRYINDEX, name);
186 assert(!lua_isnil(L, -1));
187 lua_setmetatable(L, -2);
189 /* set to xid */
190 if (destruct) {
191 lua_getfield(L, LUA_REGISTRYINDEX, "destruct");
192 lua_pushvalue(L, -2);
193 lua_pushlightuserdata(L, destruct);
194 lua_rawset(L, -3);
195 lua_pop(L, 1);
198 lua_pushvalue(L, -1);
199 lua_rawseti(L, -3, xid);
201 lua_remove(L, -2);
202 DEBUG("%d", lua_gettop(L));
203 assert(lua_touserdata(L, -1)==xidp);
204 return 1;
207 /* plain pointers, always gcable */
208 int newptr(lua_State *L, void *p, const char *name)
210 *((void **) lua_newuserdata(L, sizeof(void *))) = p;
211 lua_getfield(L, LUA_REGISTRYINDEX, name);
212 lua_setmetatable(L, -2);
213 return 1;
216 void *newudata(lua_State *L, size_t size, const char *name)
218 void *p = lua_newuserdata(L, size);
219 memset(p, 0, size);
220 lua_getfield(L, LUA_REGISTRYINDEX, name);
221 lua_setmetatable(L, -2);
222 return p;
227 Drawable checkdraw(lua_State *L, int n)
229 Drawable *d = NULL;
230 const char *s;
232 assert(n>0);
233 s = getmeta(L, n);
234 if (!strcmp(s, "<window>") || !strcmp(s,"<pixmap>"))
235 d = lua_touserdata(L, n);
236 return *d;
237 return luaL_argerror(L,n,"<window> or <pixmap> expected");
240 void process_kill(lua_State *L, process_t *p)
242 if (p->infd>0) { close(p->infd); UNSET_READ(p->infd); p->infd = -1; };
243 if (p->outfd>0) { close(p->outfd); p->outfd = -1; };
244 if (p->pid>0) {
245 /* still running? */
246 if (waitpid(p->pid, NULL, WNOHANG)>0) {
247 /* make it less so .. */
248 kill(-p->pid, SIGKILL);
249 waitpid(p->pid, NULL, 0);
251 p->pid = -1;
254 /* remove from context table */
255 lua_getfield(L, LUA_REGISTRYINDEX, "<spawn.context>");
256 lua_pushuserdata(L, p);
257 lua_pushnil(L);
258 lua_rawset(L, -3);
259 lua_pop(L, 1);
263 /* process incoming reads on spawned processes (infd) */
264 METHOD(read_processes)
266 int n;
267 DEBUG("top=%d",lua_gettop(L));
268 /* find what process needs a read */
269 lua_getfield(L, LUA_REGISTRYINDEX, "<spawn.context>");
270 assert(!lua_isnil(L, -1));
271 /* key is process, value is table with { line = "", func = {} } */
272 lua_pushnil(L);
273 DEBUG("top=%d",lua_gettop(L));
274 while ((n=lua_next(L, -2))) {
275 process_t *pr = lua_touserdata(L, -2); /* key is p */
276 if (!pr) { lua_pop(L, 1); continue; };
277 DEBUG("process %p %d",pr,n);
278 /* alive and input for us */
279 if (pr->infd > 0 && (FD_ISSET(pr->infd, &trfds))) {
280 char readbuf[8192];
281 char *p, *nl;
282 int got = read(pr->infd, readbuf, sizeof(readbuf));
283 /* resolve handler function */
284 /* something's wrong, kill the process */
285 if (got == 0 || (got < 0 && errno != EAGAIN)) {
286 lua_getfield(L, -1, "func"); /* call func */
287 lua_pushvalue(L, -3); /* process */
288 lua_pushvalue(L, -3); /* context */
289 lua_pushnil(L); /* line */
290 lua_pushstring(L, "error"); /* reason */
291 lua_pushnumber(L, errno); /* errno */
292 /* process,context,line,errreason,errno */
293 lua_call(L,5,0);
294 process_kill(L, pr);
295 lua_pop(L, 1); /* remove val */
296 continue;
298 /* seems good, append line */
299 lua_getfield(L, -2, "line");
301 /* found newlines? */
302 p = readbuf;
303 while ((nl = memchr(p, got, '\n'))) {
304 int np = 1; /* shift from nl to next p */
305 if (nl > p && nl[-1] == '\r') { nl--; np++; };
306 lua_getfield(L, -1, "func"); /* for call */
307 lua_pushvalue(L, -3); /* process */
308 lua_pushvalue(L, -3); /* context */
309 /* first occurance, concatenate */
310 if (p == readbuf) {
311 lua_getfield(L, -2, "line");
312 lua_pushlstring(L, p, nl-p);
313 lua_concat(L, 2); /* -> results single line */
314 } else lua_pushlstring(L, p, nl-p);
315 lua_call(L, 3, 0);
316 p += np;
318 /* in case we didn't process everything, save the line */
319 lua_pushlstring(L, p, (readbuf+got)-p);
320 lua_setfield(L, -2, "line");
322 lua_pop(L, 1); /* remove val */
324 return 0;
328 * x.spawn(cmd,func)
329 * x.spawn(cmd,{func=fun,argv={...},envp={...}})
330 * */
332 METHOD(x_spawn)
334 int i;
335 int pipein[2];
336 int pipeout[2];
337 const char *scmd = CHECKSTR(1);
338 const char **env = (const char**)environ;
339 const char **arg;
340 process_t *pr;
341 lua_settop(L, 2);
342 if (lua_isfunction(L, 2)) {
343 lua_newtable(L);
344 lua_pushvalue(L, -2);
345 lua_setfield(L, -2, "func");
346 lua_replace(L, 2);
347 } else if (!lua_istable(L, 2)) {
348 return luaL_argerror(L, 2, "function or table expected as argument");
352 lua_getfield(L, -1, "arg");
353 if (lua_isnil(L, -1)) {
354 /* no argv - create new one */
355 lua_pop(L, 1);
356 lua_newtable(L);
357 lua_pushliteral(L, "/bin/sh"); lua_rawseti(L, -2, 1);
358 lua_pushliteral(L, "-c"); lua_rawseti(L, -2, 2);
359 lua_pushvalue(L, 1); lua_rawseti(L, -2, 3);
360 scmd = "/bin/sh";
362 /* construct real argv vector */
363 arg = alloca((lua_objlen(L, -1)+1)*sizeof(char *));
364 for (i = 1; i <= lua_objlen(L, -1); i++) {
365 lua_rawgeti(L, -1, i);
366 arg[i] = lua_tostring(L, -1);
367 lua_pop(L, 1);
369 arg[i] = NULL;
370 lua_pop(L, 1);
373 /* construct real envp vector */
374 lua_getfield(L, -1, "env");
375 if (!lua_isnil(L, -1)) {
376 env = alloca((lua_objlen(L, -1)+1)*sizeof(char*));
377 for (i = 1; i <= lua_objlen(L, -1); i++) {
378 lua_rawgeti(L, -1, i);
379 env[i] = lua_tostring(L, -1);
380 lua_pop(L, 1);
382 env[i] = NULL;
385 /* scmd, env, arg should be ready */
386 pr = newudata(L, sizeof(pr[0]), "<process>");
387 pipe(pipein);
388 pipe(pipeout);
389 MAXFD(pipein[0]); MAXFD(pipeout[0]); MAXFD(pipein[1]); MAXFD(pipeout[1]);
390 pr->infd = pipein[0];
391 pr->outfd = pipeout[1];
392 pr->pid = fork();
393 if (pr->pid > 0) {
394 int n,ret=-1;
395 /* parent */
396 SET_READ(pr->infd);
397 close(pipein[1]);
398 close(pipeout[0]);
399 do {
400 n = waitpid(pr->pid, &ret, 0);
401 } while ((n==-1) && errno==EINTR);
402 if (n != pr->pid || ret <0) {
403 process_kill(L,pr);
404 return 0;
406 /* spawned ok? */
407 return 1;
409 /* fork error */
410 if (pr->pid < 0) return 0;
411 /* child */
412 close(pr->infd);
413 close(pr->outfd);
414 // XXX close on exec on everything by default?
415 dup2(pipein[1], 1);
416 dup2(pipeout[0], 0);
417 for (i = 2; i <= maxfd; i++) close(i);
418 execve(scmd, (char**)arg,(char**) env);
419 exit(127);
420 /* NEVER REACHED */
421 return 0;
425 /********************************************************
426 * X11 utils
427 ********************************************************/
428 METHOD(read_x_event)
430 XEvent ev;
432 DEBUG("xevcent");
433 XNextEvent(dpy, &ev);
434 lua_newtable(L);
435 lua_pushnumber(L, ev.type);
436 lua_setfield(L, -2, "type");
438 DEBUG("got type %d", ev.type);
439 switch (ev.type) {
440 case KeyPress:
441 case KeyRelease:
442 EVI(key, state);
443 EVI(key, keycode);
444 break;
445 case ButtonPress:
446 case ButtonRelease:
447 EVI(button, state);
448 EVI(button, button);
449 break;
450 case MotionNotify:
451 EVW(motion,window);
452 EVW(motion,root);
453 EVW(motion,subwindow);
454 EVI(motion,time);
455 EVI(motion,x);
456 EVI(motion,y);
457 EVI(motion,x_root);
458 EVI(motion,y_root);
459 EVI(motion,state);
460 EVB(motion,is_hint);
461 EVB(motion,same_screen);
462 break;
463 case EnterNotify:
464 case LeaveNotify:
465 DEBUG("enter notify?");
466 EVW(crossing,window);
467 EVW(crossing,root);
468 EVW(crossing,subwindow);
469 EVI(crossing,time);
470 EVI(crossing,x);
471 EVI(crossing,y);
472 EVI(crossing,x_root);
473 EVI(crossing,y_root);
474 EVI(crossing,mode);
475 EVI(crossing,detail);
476 EVB(crossing,same_screen);
477 EVB(crossing,focus);
478 EVI(crossing,state);
479 break;
480 case FocusIn:
481 case FocusOut:
482 EVW(focus,window);
483 EVI(focus,mode);
484 EVI(focus,detail);
485 break;
486 case Expose:
487 EVW(expose,window);
488 EVI(expose,x);
489 EVI(expose,y);
490 EVI(expose,width);
491 EVI(expose,height);
492 EVI(expose,count);
493 break;
494 case CreateNotify:
495 EVW(createwindow,parent);
496 EVW(createwindow,window);
497 EVI(createwindow,x);
498 EVI(createwindow,y);
499 EVI(createwindow,width);
500 EVI(createwindow,height);
501 EVI(createwindow,border_width);
502 EVB(createwindow,override_redirect);
503 break;
504 case UnmapNotify:
505 EVB(unmap,from_configure);
506 case DestroyNotify:
507 EVW(destroywindow,event);
508 EVW(destroywindow,window);
509 break;
510 case MapNotify:
511 EVW(map,event);
512 EVW(map,window);
513 EVW(map,override_redirect);
514 break;
515 case MapRequest:
516 EVW(maprequest,parent);
517 EVW(maprequest,window);
518 break;
519 case ReparentNotify:
520 EVW(reparent,event);
521 EVW(reparent,window);
522 EVW(reparent,parent);
523 EVW(reparent,x);
524 EVW(reparent,y);
525 EVB(reparent,override_redirect);
526 break;
527 case ConfigureRequest:
528 EVW(configurerequest,parent);
529 EVW(configurerequest,window);
530 EVI(configurerequest,x);
531 EVI(configurerequest,y);
532 EVI(configurerequest,width);
533 EVI(configurerequest,height);
534 EVI(configurerequest,border_width);
535 EVW(configurerequest,above);
536 EVI(configurerequest,detail);
537 EVI(configurerequest,value_mask);
538 break;
539 case ConfigureNotify:
540 EVW(configure,event);
541 EVW(configure,window);
542 EVI(configure,x);
543 EVI(configure,y);
544 EVI(configure,width);
545 EVI(configure,height);
546 EVI(configure,border_width);
547 EVW(configure,above);
548 EVB(configure,override_redirect);
549 break;
550 case PropertyNotify:
551 EVW(property,window);
552 EVA(property,atom);
553 EVI(property,time);
554 EVI(property,state);
555 break;
556 default:
557 DEBUG("something broke");
559 DEBUG("left event");
560 return 1;
564 /* x.get_event(ms) */
565 METHOD(x_get_events)
567 int got;
568 struct timeval tv;
569 double w = luaL_optnumber(L, 1, -1);
570 tv.tv_sec = w/1000;
571 tv.tv_usec = ((long)w%1000)*1000;
573 DEBUG("stage");
574 if (XPending(dpy))
575 return read_x_event(L);
577 DEBUG("stage");
578 memcpy(&trfds, &rfds, sizeof(rfds));
579 got = select(maxfd+1,&trfds,NULL,NULL,w<0?NULL:&tv);
580 if (got <= 0) return 0;
582 DEBUG("stage");
583 read_processes(L);
585 DEBUG("stage");
586 /* something happening on X connection */
587 if (FD_ISSET(xfd, &trfds)) {
588 if (XPending(dpy))
589 return read_x_event(L);
591 DEBUG("stage");
592 return 0;
596 /* return w,h */
597 METHOD(x_geometry)
599 PUSHNUM(DisplayWidth(dpy, scr));
600 PUSHNUM(DisplayHeight(dpy, scr));
601 return 2;
605 METHOD(x_string2keysym)
607 KeySym ks = XStringToKeysym(CHECKSTR(1));
608 if (ks == NoSymbol) return 0;
609 lua_pushnumber(L,ks);
610 return 1;
613 METHOD(x_keysym2string)
615 char *s = XKeysymToString(CHECKINT(1));
616 if (!s) return 0;
617 lua_pushstring(L, s);
618 return 1;
621 METHOD(x_keysym2keycode)
623 KeyCode kc = XKeysymToKeycode(dpy, CHECKINT(1));
624 if (!kc) return 0;
625 lua_pushnumber(L,kc);
626 return 1;
629 METHOD(x_keycode2keysym)
631 KeySym ks = XKeycodeToKeysym(dpy, CHECKINT(1), 0);
632 if (ks == NoSymbol) return 0;
633 lua_pushnumber(L,ks);
634 return 1;
637 /* if argument passed, set a new modmap */
638 METHOD(x_modmap)
640 int i,j;
641 XModifierKeymap *map;
642 /* getting old one */
643 if (lua_isnil(L, 1)) {
644 map = XGetModifierMapping(dpy);
645 lua_createtable(L, 8, 0);
646 for (i = 0; i < 8; i++) {
647 lua_newtable(L);
648 for (j = 0; j < map->max_keypermod; j++) {
649 KeyCode kc = map->modifiermap[i * map->max_keypermod + j];
650 if (kc) {
651 lua_pushnumber(L, kc);
652 } else lua_pushnil(L);
653 lua_rawseti(L, -2, j+1);
655 lua_rawseti(L, -2, i+1);
657 return 1;
660 /* setting new one */
661 map = XNewModifiermap(8);
662 for (i = 0; i < 8; i++) {
663 lua_rawgeti(L, 1, i+1);
664 lua_pushnil(L);
665 while (lua_next(L, -2)) {
666 XInsertModifiermapEntry(map, TOINT(-1), i);
667 /* remove the "value", keep key */
668 lua_pop(L, 1);
671 XSetModifierMapping(dpy, map);
672 XFreeModifiermap(map);
673 lua_settop(L, 1);
674 return 1;
677 METHOD(x_intern_atom)
679 const char *name = CHECKSTR(1);
680 int ifexist = CHECKBOOL(2);
681 Atom a = XInternAtom(dpy, name, ifexist);
682 if (a == None)
683 return 0;
684 MKATOM(a);
685 return 1;
688 /* input either:
689 * number - default cursor
690 * pixmap, maskmask. fg, bg, x, y - pixmap cursor
691 * font, mfont, char, maskchar, fg, bg - glyph cursor */
692 METHOD(x_create_cursor)
694 XColor fg, bg;
695 int x, y;
696 myfont_t *f1, *f2;
697 const char *s1, *s2;
698 if (lua_isnumber(L, 1)) {
699 return MKCURSOR(XCreateFontCursor(dpy, lua_tonumber(L, 1)));
701 if (!strcmp(getmeta(L, 1),"<pixmap>")) {
702 Pixmap p1 = CHECKPIX(1);
703 Pixmap p2 = OPTPIX(2, None);
704 fg.pixel = OPTINT(3, 0xffffff);
705 bg.pixel = OPTINT(4, 0);
706 XQueryColor(dpy, cmap, &fg);
707 XQueryColor(dpy, cmap, &bg);
708 x = OPTINT(5, 0);
709 y = OPTINT(6, 0);
710 return MKCURSOR(XCreatePixmapCursor(dpy, p1, p2, &fg, &bg, x, y));
712 f1 = CHECKFONT(1);
713 f2 = OPTFONT(2,f1);
714 s1 = CHECKSTR(3);
715 s2 = OPTSTR(4, s1);
716 fg.pixel = OPTINT(5, 0xffffff);
717 bg.pixel = OPTINT(6, 0);
718 return MKCURSOR(XCreateGlyphCursor(dpy, f1->xfont->fid, f2->xfont->fid, s1[0], s2[0], &fg, &bg));
721 /* cur:recolor(fg,bg) */
722 METHOD(cur_recolor)
724 Cursor c = CHECKCURSOR(1);
725 int fgp = OPTINT(2,-1);
726 int bgp = OPTINT(3,-1);
727 XColor fg, bg;
728 fg.flags=bg.flags=0;
729 if (fgp>=0) {
730 fg.pixel=fgp;
731 XQueryColor(dpy, cmap, &fg);
733 if (bgp>0) {
734 bg.pixel=bgp;
735 XQueryColor(dpy, cmap, &bg);
738 XRecolorCursor(dpy,c,&fg,&bg);
739 return 0;
742 METHOD(cur_destroy)
744 Cursor cur = CHECKCURSOR(1);
745 kill_xid(L, cur);
746 XFreeCursor(dpy, cur);
747 return 0;
750 /* load the font */
751 METHOD(x_load_font)
753 const char *fn = CHECKSTR(1);
754 char **missing=NULL;
755 int n;
756 myfont_t *myf = newudata(L,sizeof(*myf),"<font>");
758 // myf->set = XCreateFontSet(dpy, fn, &missing, &n, &def);
760 if (missing) XFreeStringList(missing);
761 if (myf->set) {
762 int i;
763 XFontSetExtents *font_extents;
764 XFontStruct **xfonts;
765 char **font_names;
766 font_extents = XExtentsOfFontSet(myf->set);
767 n = XFontsOfFontSet(myf->set, &xfonts, &font_names);
768 myf->ascent = myf->descent = 0;
769 if (n) myf->xfont = *xfonts;
770 for (i = 0; i < n; i++) {
771 #define MAX(a,b) ((a)>(b))?(a):(b)
772 myf->ascent = MAX(myf->ascent, xfonts[i]->ascent);
773 myf->descent = MAX(myf->descent,xfonts[i]->descent);
774 #undef MAX
776 } else {
777 myf->xfont = XLoadQueryFont(dpy, fn);
778 if (!myf->xfont) return 0;
779 myf->ascent = myf->xfont->max_bounds.ascent;
780 myf->descent = myf->xfont->max_bounds.descent;
782 myf->height = myf->ascent+myf->descent;
783 // DEBUG("loaded font %d\n", myf->xfont->fid);
784 return 1;
787 /* x.color("#ff0000") */
788 METHOD(x_color)
790 XColor color;
791 XAllocNamedColor(dpy,cmap,CHECKSTR(1),&color,&color);
792 lua_pushnumber(L, color.pixel);
793 return 1;
796 METHOD(x_fg)
798 XSetForeground(dpy, gc, TOCOLOR(1));
799 return 0;
802 METHOD(x_bg)
804 XSetBackground(dpy, gc, TOCOLOR(1));
805 return 0;
808 METHOD(x_set_font)
810 myfont_t *f = CHECKFONT(1);
811 if (f->set) {
812 global_fontset = f->set;
813 DEBUG("set global fontset");
814 } else {
815 global_fontset = NULL;
816 XSetFont(dpy, gc, f->xfont->fid);
818 return 0;
821 METHOD(x_init)
823 if (!dpy) {
824 dpy = XOpenDisplay(NULL);
825 if (!dpy) return 0;
826 scr = DefaultScreen(dpy);
827 root = RootWindow(dpy, scr);
828 cmap = DefaultColormap(dpy, scr);
829 xfd = ConnectionNumber(dpy);
830 MAXFD(xfd);
831 SET_READ(xfd);
833 gc=XCreateGC(dpy, root, 0, 0);
834 ADOPTWIN(root);
835 return 1;
838 METHOD(x_sync)
840 XSync(dpy, lua_toboolean(L, 1));
841 return 0;
843 METHOD(x_flush)
845 XFlush(dpy);
846 return 0;
850 METHOD(x_query_tree)
852 Window d1, d2, *wins = NULL;
853 unsigned int i, num;
855 if (!XQueryTree(dpy, root, &d1, &d2, &wins, &num))
856 return 0;
857 lua_newtable(L);
858 for (i = 0; i < num; i++) {
859 ADOPTWIN(wins[i]);
860 lua_rawseti(L, -2, i+1);
862 XFree(wins);
863 return 1;
866 /********************************************************
867 * Window utils
868 ********************************************************/
870 static unsigned long copy_attr(lua_State *L, int idx, XSetWindowAttributes *wa)
872 unsigned long mask = 0;
873 /* no table, no args */
874 if (!lua_istable(L, idx)) return mask;
877 GETP(background_pixmap, BackPixmap);
878 GETI(background_pixel, BackPixel);
879 GETP(border_pixmap, BorderPixmap);
880 GETI(border_pixel, BorderPixel);
881 GETI(bit_gravity, BitGravity);
882 GETI(win_gravity, WinGravity);
883 GETI(backing_store, BackingPixel);
884 GETI(backing_planes, BackingPlanes);
885 GETI(backing_pixel, BackingPixel);
886 GETB(override_redirect, OverrideRedirect);
887 GETB(save_under, SaveUnder);
888 GETI(event_mask, EventMask);
889 GETI(do_not_propagate_mask, DontPropagate);
890 GETC(cursor, Cursor);
891 return mask;
894 /* win:set_attributes{attr_tab} */
895 METHOD(win_set_attributes)
897 Window win = CHECKWIN(1);
898 unsigned long wmask;
899 XSetWindowAttributes wa;
901 wmask = copy_attr(L, 2, &wa);
902 XChangeWindowAttributes(dpy, win, wmask, &wa);
903 return 0;
906 /* create a (sub)window:
907 * x.root:create_window(0,0[,w,h,border,{attributes...}])
908 * */
909 METHOD(win_create_window)
911 Window nw, win = CHECKWIN(1);
912 int x = CHECKINT(2);
913 int y = CHECKINT(3);
914 unsigned int w = OPTINT(4,-1);
915 unsigned int h = OPTINT(5,-1);
916 int bw = OPTINT(6,0);
917 unsigned long wmask;
918 XSetWindowAttributes wa;
920 FIXWHXY();
922 wmask = copy_attr(L, 6, &wa);
923 nw = XCreateWindow(dpy, root, x, y, w, h, bw, DefaultDepth(dpy, scr), CopyFromParent /*class*/, DefaultVisual(dpy, scr), wmask, &wa);
924 if (!nw) return 0;
925 return MKWIN(nw);
929 #define QRI(n) { lua_pushnumber(L, a.n); lua_setfield(L, -2, #n); }
930 #define QRW(n) { ADOPTWIN(a.n); lua_setfield(L, -2, #n); }
931 #define QRA(n) { MKATOM(a.n); lua_setfield(L, -2, #n); }
932 #define QRB(n) { lua_pushboolean(L, a.n); lua_setfield(L, -2, #n); }
933 METHOD(win_query_pointer)
935 struct {
936 Window root;
937 Window win;
938 int root_x, root_y;
939 int x, y;
940 unsigned int mod;
941 } a;
942 if (!XQueryPointer(dpy, CHECKWIN(1), &a.root, &a.win, &a.root_x, &a.root_y, &a.x, &a.y, &a.mod)) return 0;
943 lua_newtable(L);
944 QRW(root);
945 QRW(win);
946 QRI(root_x); QRI(root_y);
947 QRI(x); QRI(y);
948 QRI(mod);
949 return 1;
951 #undef QRI
952 #undef QRW
953 #undef QRA
954 #undef QRB
956 METHOD(win_list_properties)
958 int nret;
959 Window win = CHECKWIN(1);
960 int i;
961 Atom *al = XListProperties(dpy, win, &nret);
962 if (!al) return 0;
963 lua_newtable(L);
964 for (i = 0; i < nret; i++) {
965 MKATOM(al[i]);
966 lua_rawseti(L, -2, i+1);
968 XFree(al);
969 return 1;
972 /* win:set_property(atom,atom,data1, data2...) */
973 METHOD(win_set_property)
975 Window win = CHECKWIN(1);
976 Atom a1 = CHECKATOM(2);
977 Atom a2 = OPTATOM(3,2);
978 int i;
979 uint32_t *data;
980 lua_settop(L, 3);
981 data = alloca(lua_gettop(L)*sizeof(long));
982 for (i = 0; i < lua_gettop(L)-3; i++)
983 data[i] = lua_tonumber(L, i);
984 XChangeProperty(dpy, win, a1, a2, 32, PropModeReplace, (unsigned char*)data, i);
985 return 0;
988 /* win:get_property(atom[,atom2,off,len]) = {ilist}, real */
989 METHOD(win_get_property)
991 unsigned char *p;
992 Atom real;
993 int format;
994 Window win = CHECKWIN(1);
995 Atom a1 = CHECKATOM(2);
996 Atom a2 = OPTATOM(3,a1);
997 unsigned long off, len,n,extra;
998 int i;
1000 off = OPTINT(4, 0);
1001 len = OPTINT(5, 2);
1003 if (XGetWindowProperty(dpy, win, a1, off, len, False, a2, &real, &format, &n, &extra, &p) != Success) return 0;
1004 // DEBUG("nprop: %d %d", n, format);
1005 lua_newtable(L);
1006 for (i = 0; i < n; i += (format/8)) {
1007 switch (format) {
1008 case 8:
1009 lua_pushnumber(L, p[0]);
1010 break;
1011 case 16:
1012 lua_pushnumber(L, *(uint16_t *)(p));
1013 break;
1014 case 32:
1015 lua_pushnumber(L, *(uint32_t *)(p));
1016 break;
1017 default:
1018 XFree(p);
1019 luaL_error(L, "invalid property format");
1021 lua_rawseti(L, -2, i+1);
1023 lua_pushnumber(L, real);
1024 XFree(p);
1025 return 2;
1028 METHOD(win_get_attributes)
1030 Window win = CHECKWIN(1);
1031 XWindowAttributes a;
1032 CLEAR(a);
1033 if (!XGetWindowAttributes(dpy, win, &a)) return 0;
1034 lua_newtable(L);
1035 ATTRI(x); ATTRI(y);
1036 ATTRI(width); ATTRI(height);
1037 ATTRI(border_width);
1038 ATTRI(depth);
1039 ATTRW(root);
1040 ATTRI(class);
1041 ATTRI(bit_gravity);
1042 ATTRI(win_gravity);
1043 ATTRI(backing_store);
1044 ATTRI(backing_planes);
1045 ATTRI(backing_pixel);
1046 ATTRB(save_under);
1047 ATTRB(map_installed);
1048 ATTRI(map_state);
1049 ATTRI(all_event_masks);
1050 ATTRB(override_redirect);
1051 return 1;
1054 /* keycode, modmask */
1055 METHOD(win_grab_key)
1057 XGrabKey(dpy, CHECKINT(2), OPTINT(3,AnyModifier), CHECKWIN(1), True, GrabModeAsync, GrabModeAsync);
1058 return 0;
1061 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
1062 /* button, modifier, mask */
1063 METHOD(win_grab_button)
1065 XGrabButton(dpy, CHECKINT(2), OPTINT(3,AnyModifier), CHECKWIN(1), False, OPTINT(4,BUTTONMASK), GrabModeAsync, GrabModeSync, None, None);
1066 return 0;
1069 METHOD(win_ungrab_button)
1071 XUngrabButton(dpy, CHECKINT(2), OPTINT(3, AnyModifier), CHECKWIN(1));
1072 return 0;
1075 METHOD(win_ungrab_key)
1077 XUngrabKey(dpy, TOINT(1), TOINT(2), CHECKWIN(1));
1078 return 0;
1082 * w:send_message(atom)
1084 METHOD(win_send_message)
1086 XEvent ev;
1087 Window win = CHECKWIN(1);
1088 ev.type = ClientMessage;
1089 ev.xclient.display = dpy;
1090 ev.xclient.window = win;
1091 ev.xclient.format = 32;
1092 ev.xclient.data.l[0] = CHECKATOM(2);
1093 ev.xclient.data.l[1] = CurrentTime;
1094 XSendEvent(dpy, win, False, NoEventMask, &ev);
1095 return 0;
1098 #define READI(n) (lua_getfield(L, 2, #n), luaL_checknumber(L, -1)); lua_pop(L, 1);
1099 #define READW(n) (lua_getfield(L, 2, #n), CHECKWIN(-1)); lua_pop(L, 1);
1100 #define READB(n) (lua_getfield(L, 2, #n), lua_toboolean(L,-1)); lua_pop(L, 1);
1102 /* w:send_configure { table } */
1103 METHOD(win_send_configure)
1105 XEvent ev;
1106 Window win = CHECKWIN(1);
1107 ev.type = ConfigureNotify;
1108 ev.xconfigure.display = dpy;
1109 ev.xconfigure.event = win;
1110 ev.xconfigure.window = win;
1111 ev.xconfigure.x = READI(x);
1112 ev.xconfigure.y = READI(y);
1113 ev.xconfigure.width = READI(width);
1114 ev.xconfigure.height = READI(height);
1115 ev.xconfigure.border_width = READI(border_width);
1116 ev.xconfigure.above = READW(above);
1117 ev.xconfigure.override_redirect = READB(override_redirect);
1118 XSendEvent(dpy, win, False, NoEventMask, &ev);
1119 return 0;
1121 #undef READI
1122 #undef READW
1123 #undef READB
1125 /* w:configure {table} */
1126 METHOD(win_configure)
1128 XWindowChanges wc;
1129 XWindowChanges *wa = &wc;
1130 unsigned long mask = 0;
1131 int idx = 2;
1133 GETI(x, X);
1134 GETI(x, Y);
1135 GETI(width, Width);
1136 GETI(height, Height);
1137 GETI(border_width, BorderWidth);
1138 GETI(sibling, Sibling);
1139 GETI(stack_mode, StackMode);
1140 XConfigureWindow(dpy, CHECKWIN(1), mask, &wc);
1141 return 0;
1143 METHOD(win_clear)
1145 Window win = CHECKWIN(1);
1146 int x = OPTINT(2, 0);
1147 int y = OPTINT(3, 0);
1148 unsigned int w = OPTINT(4, -1);
1149 unsigned int h = OPTINT(5, -1);
1150 int ex = OPTBOOL(6, 0);
1151 if (lua_gettop(L)==1) {
1152 XClearWindow(dpy, CHECKWIN(1));
1153 return 0;
1155 FIXWHXY();
1156 XClearArea(dpy, win, x, y, w, h, ex);
1157 return 0;
1160 /* explicitly kill window */
1161 METHOD(win_destroy)
1163 Window win = CHECKWIN(1);
1165 /* to avoid destroying twice .. */
1166 kill_xid(L, win);
1168 XDestroyWindow(dpy, win);
1169 return 0;
1172 METHOD(win_map)
1174 Window win = CHECKWIN(1);
1175 XMapWindow(dpy, win);
1176 return 0;
1179 METHOD(win_unmap)
1181 Window win = CHECKWIN(1);
1182 XMapWindow(dpy, win);
1183 return 0;
1187 /********************************************************
1188 * GFX
1189 ********************************************************/
1191 /* pix,hotx,hoty,w,h = root:read_bitmap_file(name) */
1192 METHOD(g_read_bitmap_file)
1194 Drawable win = CHECKDRAW(1);
1195 Pixmap pix;
1196 unsigned w, h;
1197 int x,y;
1199 if (XReadBitmapFile(dpy, win, CHECKSTR(2), &w, &h, &pix, &x, &y) != BitmapSuccess)
1200 return 0;
1201 MKPIX(pix);
1202 lua_pushnumber(L, x);
1203 lua_pushnumber(L, y);
1204 lua_pushnumber(L, w);
1205 lua_pushnumber(L, h);
1206 return 5;
1209 METHOD(pix_destroy)
1211 Pixmap pix = CHECKPIX(1);
1212 kill_xid(L, pix);
1213 XFreePixmap(dpy,pix);
1214 return 0;
1217 /* win:get_image(x,y,w,h) */
1218 METHOD(g_get_image)
1220 Drawable win = CHECKDRAW(1);
1221 int x = OPTINT(2, 0);
1222 int y = OPTINT(3, 0);
1223 unsigned int w = OPTINT(4, -1);
1224 unsigned int h = OPTINT(5, -1);
1225 XImage *ximg;
1226 FIXWHXY();
1227 ximg = XGetImage(dpy, win, x, y, w, h, ~0, ZPixmap);
1228 if (!ximg) return 0;
1229 newptr(L,ximg, "<image>");
1230 return 1;
1233 /* win:put_image(img,[sx],[sy],[dx],[dy],[w],[h]) */
1234 METHOD(g_put_image)
1236 Drawable win = CHECKDRAW(1);
1237 XImage *img = CHECKIMG(2);
1238 int sx = OPTINT(3,0);
1239 int sy = OPTINT(4,0);
1240 int dx = OPTINT(5,0);
1241 int dy = OPTINT(6,0);
1242 int w = OPTINT(7,img->width);
1243 int h = OPTINT(8,img->height);
1244 XPutImage(dpy, win, gc, img, sx, sy, dx, dy, w, h);
1245 return 0;
1248 METHOD(g_create_pixmap)
1250 Drawable win = CHECKDRAW(1);
1251 unsigned int w = OPTINT(2, -1);
1252 unsigned int h = OPTINT(3, -1);
1253 FIXWH();
1254 return MKPIX(XCreatePixmap(dpy, win, w, h, DefaultDepth(dpy, scr)));
1257 /* win:draw_rectangles({...},fill) */
1258 METHOD(g_draw_rectangles)
1260 Drawable win = CHECKDRAW(1);
1261 int count,i;
1262 XRectangle *xr;
1263 lua_settop(L, 2);
1264 count = lua_objlen(L, 2);
1265 xr = alloca(sizeof(xr[0]) * count);
1266 for (i = 1; i <= count; i++) {
1267 lua_rawgeti(L, -1, i);
1268 if (lua_isnil(L, -1)) luaL_argerror(L, i, "premature end of rectangle array");
1269 lua_getfield(L, -1, "x");
1270 lua_getfield(L, -2, "y");
1271 lua_getfield(L, -3, "w");
1272 lua_getfield(L, -4, "h");
1273 xr[i].x = CHECKINT(-4);
1274 xr[i].y = CHECKINT(-3);
1275 xr[i].width = CHECKINT(-2);
1276 xr[i].height = CHECKINT(-1);
1277 lua_pop(L, 5);
1279 if (lua_toboolean(L, 3))
1280 XFillRectangles(dpy, win, gc, xr, count);
1281 else
1282 XDrawRectangles(dpy, win, gc, xr, count);
1283 return 0;
1286 METHOD(g_draw_arcs)
1288 Drawable win = CHECKDRAW(1);
1289 int count,i;
1290 XArc *xa;
1291 lua_settop(L, 2);
1292 count = lua_objlen(L, 2);
1293 xa = alloca(sizeof(xa[0]) * count);
1294 for (i = 1; i <= count; i++) {
1295 lua_rawgeti(L, -1, i);
1296 if (lua_isnil(L, -1)) luaL_argerror(L, i, "premature end of arc array");
1297 lua_getfield(L, -1, "x");
1298 lua_getfield(L, -2, "y");
1299 lua_getfield(L, -3, "w");
1300 lua_getfield(L, -4, "h");
1301 lua_getfield(L, -5, "a1");
1302 lua_getfield(L, -6, "a2");
1303 xa[i].x = CHECKINT(-6);
1304 xa[i].y = CHECKINT(-5);
1305 xa[i].width = CHECKINT(-4);
1306 xa[i].height = CHECKINT(-3);
1307 xa[i].angle1 = CHECKINT(-2);
1308 xa[i].angle2 = CHECKINT(-1);
1309 lua_pop(L, 7);
1311 if (lua_toboolean(L, 3))
1312 XFillArcs(dpy, win, gc, xa, count);
1313 else
1314 XDrawArcs(dpy, win, gc, xa, count);
1315 return 0;
1320 METHOD(g_draw_segments)
1322 Drawable win = CHECKDRAW(1);
1323 int count,i;
1324 XSegment *xs;
1325 lua_settop(L, 2);
1326 count = lua_objlen(L, 2);
1327 xs = alloca(sizeof(xs[0]) * count);
1328 for (i = 1; i <= count; i++) {
1329 lua_rawgeti(L, -1, i);
1330 if (lua_isnil(L, -1)) luaL_argerror(L, i, "premature end of rectangle array");
1331 lua_getfield(L, -1, "x");
1332 lua_getfield(L, -2, "y");
1333 lua_getfield(L, -3, "w");
1334 lua_getfield(L, -4, "h");
1335 xs[i].x1 = CHECKINT(-4);
1336 xs[i].y1 = CHECKINT(-3);
1337 xs[i].x2 = CHECKINT(-2);
1338 xs[i].y2 = CHECKINT(-1);
1339 lua_pop(L, 5);
1341 XDrawSegments(dpy, win, gc, xs, count);
1342 return 0;
1346 METHOD(g_draw_points)
1348 Drawable win = CHECKDRAW(1);
1349 int count,i;
1350 XPoint *xp;
1351 lua_settop(L, 2);
1352 count = lua_objlen(L, 2);
1353 xp = alloca(sizeof(xp[0]) * count);
1354 for (i = 1; i <= count; i++) {
1355 lua_rawgeti(L, -1, i);
1356 if (lua_isnil(L, -1)) luaL_argerror(L, i, "premature end of pixel array");
1357 lua_getfield(L, -1, "x");
1358 lua_getfield(L, -2, "y");
1359 xp[i].x = CHECKINT(-2);
1360 xp[i].y = CHECKINT(-1);
1361 lua_pop(L, 3);
1363 XDrawPoints(dpy, win, gc, xp, count, CHECKBOOL(3));
1364 return 0;
1367 METHOD(g_draw_lines)
1369 Drawable win = CHECKDRAW(1);
1370 int count,i;
1371 XPoint *xp;
1372 lua_settop(L, 2);
1373 count = lua_objlen(L, 2);
1374 xp = alloca(sizeof(xp[0]) * count);
1375 for (i = 1; i <= count; i++) {
1376 lua_rawgeti(L, -1, i);
1377 if (lua_isnil(L, -1)) luaL_argerror(L, i, "premature end of lines array");
1378 lua_getfield(L, -1, "x");
1379 lua_getfield(L, -2, "y");
1380 xp[i].x = CHECKINT(-2);
1381 xp[i].y = CHECKINT(-1);
1382 lua_pop(L, 3);
1384 XDrawLines(dpy, win, gc, xp, count, CHECKBOOL(3));
1385 return 0;
1388 /* win:draw_string(str,[x,y]) */
1389 METHOD(g_draw_string)
1391 Drawable win = CHECKDRAW(1);
1392 size_t l;
1393 const char *s;
1394 XChar2b *us;
1395 int x = OPTINT(3,0);
1396 int y = OPTINT(4,0);
1399 if (!lua_istable(L, 2)) {
1400 s = luaL_checklstring(L, 2, &l);
1401 XDrawString(dpy, win, gc, x, y, (void*)s, l);
1402 } else {
1403 int i;
1404 l = lua_objlen(L, 2);
1405 us = alloca(sizeof(us[0])*l);
1406 for (i = 0; i < l; i++) {
1407 uint16_t n;
1409 lua_rawgeti(L, 2, i+1);
1410 n = lua_tonumber(L, -1);
1411 us[i].byte1 = n>>8;
1412 us[i].byte2 = n&0xff;
1413 lua_pop(L, 1);
1415 XDrawString16(dpy, win, gc, x, y, us, l);
1418 /* if (global_fontset) {
1419 DEBUG("using global fontset");
1420 Xutf8DrawString(dpy, win, global_fontset, gc, x, y, s, l);
1422 else*/
1423 // XDrawString(dpy, win, gc, x, y, (void*)s, l);
1424 return 0;
1427 /********************************************************
1428 * Destructors
1429 ********************************************************/
1431 /* kill font */
1432 METHOD(font_gc)
1434 myfont_t *f = CHECKFONT(1);
1435 if (f->set) {
1436 if (f->set == global_fontset)
1437 global_fontset = NULL;
1438 XFreeFontSet(dpy, f->set);
1439 } else {
1440 XFree(f->xfont);
1442 return 0;
1445 METHOD(font_info)
1447 myfont_t *f = CHECKFONT(1);
1448 lua_newtable(L);
1449 lua_pushnumber(L, f->ascent); lua_setfield(L, -2, "ascent");
1450 lua_pushnumber(L, f->descent); lua_setfield(L, -2, "descent");
1451 lua_pushnumber(L, f->height); lua_setfield(L, -2, "height");
1452 return 1;
1455 METHOD(font_width)
1457 myfont_t *f = CHECKFONT(1);
1458 size_t tl;
1459 const char *text = luaL_checklstring(L, 2, &tl);
1460 XRectangle r;
1462 if (f->set) {
1463 XmbTextExtents(f->set, text, tl, NULL, &r);
1464 lua_pushnumber(L, r.width);
1465 } else lua_pushnumber(L, XTextWidth(f->xfont, text, tl));
1466 return 1;
1469 /* unified destructor for window, pixmap, cursor */
1470 METHOD(gc_xid)
1472 void (*destruct)(Display *,XID);
1473 XID xid, *x = lua_touserdata(L, 1);
1475 if (!x) return 0;
1476 xid = *x;
1477 *x = 0;
1479 lua_getfield(L, LUA_REGISTRYINDEX, "destruct");
1480 lua_rawgeti(L, -1, xid);
1481 if (lua_isnil(L, -1)) return 0;
1482 destruct = lua_touserdata(L, -1);
1483 lua_pop(L, 1); /* pop udata */
1484 if (destruct) destruct(dpy,xid);
1485 lua_getfield(L, LUA_REGISTRYINDEX, "xid");
1486 lua_pushnil(L);
1487 lua_rawseti(L, -2, xid); /* remove destruct .. */
1488 lua_rawseti(L, -3, xid); /* and xid */
1489 return 0;
1492 /* basic methods */
1493 #define X(name) { #name, x_##name },
1494 #define WIN(name) { #name, win_##name },
1495 #define FONT(name) { #name, font_##name },
1496 #define PIX(name) { #name, pix_##name },
1497 #define G(name) { #name, g_##name },
1498 #define CUR(name) { #name, cur_##name },
1500 static luaL_reg font_method[] = {
1501 FONT(info) FONT(width)
1502 { "__gc", font_gc },
1503 { NULL, NULL }
1506 static luaL_reg x_method[] = {
1507 X(spawn) X(get_events) X(geometry) X(string2keysym)
1508 X(keysym2string) X(keysym2keycode) X(keycode2keysym) X(modmap)
1509 X(intern_atom) X(create_cursor) X(load_font) X(color)
1510 X(fg) X(bg) X(set_font) X(init)
1511 X(query_tree) X(sync) X(flush)
1512 { NULL, NULL }
1515 static luaL_reg win_method[] = {
1516 WIN(set_attributes) WIN(create_window) WIN(query_pointer) WIN(list_properties)
1517 WIN(set_property) WIN(get_property) WIN(get_attributes) WIN(grab_key)
1518 WIN(grab_button) WIN(ungrab_key) WIN(ungrab_button) WIN(send_message)
1519 WIN(send_configure) WIN(configure) WIN(clear) WIN(destroy)
1520 WIN(map) WIN(unmap)
1522 G(put_image) G(get_image) G(draw_rectangles) G(draw_arcs)
1523 G(draw_points) G(draw_lines) G(draw_string)
1524 G(create_pixmap) G(read_bitmap_file)
1526 { "__gc", gc_xid },
1527 { NULL, NULL }
1530 static luaL_reg pix_method[] = {
1531 PIX(destroy)
1533 G(put_image) G(get_image) G(draw_rectangles) G(draw_arcs)
1534 G(draw_points) G(draw_lines) G(draw_string) G(create_pixmap)
1535 G(read_bitmap_file)
1537 { "__gc", gc_xid },
1538 { NULL, NULL }
1542 static luaL_reg cur_method[] = {
1543 CUR(destroy) CUR(recolor)
1544 { "__gc", gc_xid },
1545 { NULL, NULL }
1548 #undef CUR
1549 #undef G
1550 #undef PIX
1551 #undef WIN
1552 #undef X
1555 void meta_register(lua_State *L, const char *mname, luaL_reg *meth)
1557 luaL_newmetatable(L, mname);
1558 if (meth) {
1559 lua_pushvalue(L, -1);
1560 lua_setfield(L, -1, "__index");
1561 luaL_register(L, NULL, meth);
1563 lua_pushstring(L, mname);
1564 lua_rawset(L, LUA_REGISTRYINDEX);
1565 // lua_setfield(L, LUA_REGISTRYINDEX, mname);
1568 void weak_table(lua_State *L, const char *mode)
1570 DEBUG("top=%d",lua_gettop(L));
1571 /* the table */
1572 lua_newtable(L);
1574 /* weak mode metatable */
1575 lua_newtable(L);
1576 lua_pushstring(L, mode);
1577 lua_setfield(L, -2, "__mode");
1578 lua_setmetatable(L, -2);
1579 DEBUG("top=%d",lua_gettop(L));
1582 int main()
1584 int status;
1585 lua_State *L = lua_open();
1586 luaL_openlibs(L);
1587 luaL_register(L, "x", x_method);
1588 meta_register(L, "<window>", win_method);
1589 meta_register(L, "<pixmap>", pix_method);
1590 meta_register(L, "<cursor>", cur_method);
1591 meta_register(L, "<font>", font_method);
1593 /* process table contexes are weak keys (someone has
1594 * to hold the process handle */
1595 // weak_table(L, "k");
1596 lua_newtable(L);
1597 lua_setfield(L, LUA_REGISTRYINDEX, "<spawn.context>");
1599 weak_table(L, "v");
1600 lua_setfield(L, LUA_REGISTRYINDEX, "xid");
1602 lua_newtable(L);
1603 lua_setfield(L, LUA_REGISTRYINDEX, "destruct");
1605 status = luaL_dofile(L, "main.lua");
1606 if (status && !lua_isnil(L, -1)) {
1607 const char *msg = lua_tostring(L, -1);
1608 if (!msg) fprintf(stderr, "unknown error!");
1609 fprintf(stderr, "%s\n", msg);
1611 lua_close(L);
1612 return 0;
1615 #if 0
1616 Display *dpy;
1617 Window rootwin;
1618 Window win;
1619 Colormap cmap;
1620 XImage *ximg;
1621 XEvent e;
1622 int scr,w,h;
1623 GC gc;
1624 Pixmap pix;
1626 if(!(dpy=XOpenDisplay(NULL))) {
1627 fprintf(stderr, "ERROR: could not open display\n");
1628 exit(1);
1630 scr = DefaultScreen(dpy);
1631 rootwin = RootWindow(dpy, scr);
1632 cmap = DefaultColormap(dpy, scr);
1633 win=XCreateSimpleWindow(dpy, rootwin, 1, 1, 100, 50, 0,
1634 BlackPixel(dpy, scr), BlackPixel(dpy, scr));
1635 XStoreName(dpy, win, "hello");
1636 gc=XCreateGC(dpy, win, 0, NULL);
1639 pix = XCreatePixmap(dpy, rootwin,
1640 w = 1280,
1641 h = 720,
1642 DefaultDepth(dpy,scr));
1644 ximg = XGetImage(dpy, rootwin, 0, 0, w, h, ~0, ZPixmap);
1646 shade_ximage_generic(ximg, 128);
1647 XPutImage(dpy, pix, gc, ximg, 0,0, 0,0, w,h);
1648 // XSetWindowBackgroundPixmap(dpy, win, pix);
1649 XMapWindow(dpy, win);
1650 XSetForeground(dpy, gc, WhitePixel(dpy, scr));
1651 XSelectInput(dpy, win, ExposureMask|ButtonPressMask);
1653 while(1) {
1654 XNextEvent(dpy, &e);
1655 if(e.type==Expose && e.xexpose.count<1) {
1656 XCopyArea(dpy, pix, win, gc, 0, 0, 100, 50, 0, 0);
1657 XDrawString(dpy, win, gc, 10, 10, "Hello World!", 12);
1659 else if(e.type==ButtonPress) break;
1662 XCloseDisplay(dpy);
1663 #endif