17 #include <sys/types.h>
20 extern char **environ
;
31 XFontSet global_fontset
= NULL
;
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))
62 #define CHECKIMG(n) (*(XImage **) CHECKMETA(n,"<image>"))
63 #define CHECKFONT(n) ((myfont_t *) CHECKMETA(n,"<font>"))
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)
88 if (w < 0 || h < 0) { \
90 XGetGeometry(dpy, win, NULL, NULL, NULL, &w, &h, NULL, NULL); \
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
)
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);
132 //DEBUG("top %d", lua_gettop(L));
136 void *checkmeta(lua_State
*L
, int n
, char *name
, char *expect
)
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));
149 void kill_xid(lua_State
*L
, XID xid
)
151 lua_getfield(L
, LUA_REGISTRYINDEX
, "destruct");
153 lua_rawseti(L
, -2, xid
);
155 lua_getfield(L
, LUA_REGISTRYINDEX
, "xid");
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
)
166 if (!xid
) { lua_pushnil(L
); return 1; };
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)) {
173 XID
*x
= lua_touserdata(L
, -1);
175 DEBUG("%d %p", lua_gettop(L
), destruct
);
176 if (*x
) return 1; /* got valid one */
180 xidp
= lua_newuserdata(L
, sizeof(XID
));
183 /* -2 xid table, -1 = udata */
185 lua_getfield(L
, LUA_REGISTRYINDEX
, name
);
186 assert(!lua_isnil(L
, -1));
187 lua_setmetatable(L
, -2);
191 lua_getfield(L
, LUA_REGISTRYINDEX
, "destruct");
192 lua_pushvalue(L
, -2);
193 lua_pushlightuserdata(L
, destruct
);
198 lua_pushvalue(L
, -1);
199 lua_rawseti(L
, -3, xid
);
202 DEBUG("%d", lua_gettop(L
));
203 assert(lua_touserdata(L
, -1)==xidp
);
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);
216 void *newudata(lua_State
*L
, size_t size
, const char *name
)
218 void *p
= lua_newuserdata(L
, size
);
220 lua_getfield(L
, LUA_REGISTRYINDEX
, name
);
221 lua_setmetatable(L
, -2);
227 Drawable
checkdraw(lua_State
*L
, int n
)
234 if (!strcmp(s
, "<window>") || !strcmp(s
,"<pixmap>"))
235 d
= lua_touserdata(L
, n
);
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; };
246 if (waitpid(p
->pid
, NULL
, WNOHANG
)>0) {
247 /* make it less so .. */
248 kill(-p
->pid
, SIGKILL
);
249 waitpid(p
->pid
, NULL
, 0);
254 /* remove from context table */
255 lua_getfield(L
, LUA_REGISTRYINDEX
, "<spawn.context>");
256 lua_pushuserdata(L
, p
);
263 /* process incoming reads on spawned processes (infd) */
264 METHOD(read_processes
)
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 = {} } */
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
))) {
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 */
295 lua_pop(L
, 1); /* remove val */
298 /* seems good, append line */
299 lua_getfield(L
, -2, "line");
301 /* found newlines? */
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 */
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
);
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 */
329 * x.spawn(cmd,{func=fun,argv={...},envp={...}})
337 const char *scmd
= CHECKSTR(1);
338 const char **env
= (const char**)environ
;
342 if (lua_isfunction(L
, 2)) {
344 lua_pushvalue(L
, -2);
345 lua_setfield(L
, -2, "func");
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 */
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);
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);
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);
385 /* scmd, env, arg should be ready */
386 pr
= newudata(L
, sizeof(pr
[0]), "<process>");
389 MAXFD(pipein
[0]); MAXFD(pipeout
[0]); MAXFD(pipein
[1]); MAXFD(pipeout
[1]);
390 pr
->infd
= pipein
[0];
391 pr
->outfd
= pipeout
[1];
400 n
= waitpid(pr
->pid
, &ret
, 0);
401 } while ((n
==-1) && errno
==EINTR
);
402 if (n
!= pr
->pid
|| ret
<0) {
410 if (pr
->pid
< 0) return 0;
414 // XXX close on exec on everything by default?
417 for (i
= 2; i
<= maxfd
; i
++) close(i
);
418 execve(scmd
, (char**)arg
,(char**) env
);
425 /********************************************************
427 ********************************************************/
433 XNextEvent(dpy
, &ev
);
435 lua_pushnumber(L
, ev
.type
);
436 lua_setfield(L
, -2, "type");
438 DEBUG("got type %d", ev
.type
);
453 EVW(motion
,subwindow
);
461 EVB(motion
,same_screen
);
465 DEBUG("enter notify?");
466 EVW(crossing
,window
);
468 EVW(crossing
,subwindow
);
472 EVI(crossing
,x_root
);
473 EVI(crossing
,y_root
);
475 EVI(crossing
,detail
);
476 EVB(crossing
,same_screen
);
495 EVW(createwindow
,parent
);
496 EVW(createwindow
,window
);
499 EVI(createwindow
,width
);
500 EVI(createwindow
,height
);
501 EVI(createwindow
,border_width
);
502 EVB(createwindow
,override_redirect
);
505 EVB(unmap
,from_configure
);
507 EVW(destroywindow
,event
);
508 EVW(destroywindow
,window
);
513 EVW(map
,override_redirect
);
516 EVW(maprequest
,parent
);
517 EVW(maprequest
,window
);
521 EVW(reparent
,window
);
522 EVW(reparent
,parent
);
525 EVB(reparent
,override_redirect
);
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
);
539 case ConfigureNotify
:
540 EVW(configure
,event
);
541 EVW(configure
,window
);
544 EVI(configure
,width
);
545 EVI(configure
,height
);
546 EVI(configure
,border_width
);
547 EVW(configure
,above
);
548 EVB(configure
,override_redirect
);
551 EVW(property
,window
);
557 DEBUG("something broke");
564 /* x.get_event(ms) */
569 double w
= luaL_optnumber(L
, 1, -1);
571 tv
.tv_usec
= ((long)w
%1000)*1000;
575 return read_x_event(L
);
578 memcpy(&trfds
, &rfds
, sizeof(rfds
));
579 got
= select(maxfd
+1,&trfds
,NULL
,NULL
,w
<0?NULL
:&tv
);
580 if (got
<= 0) return 0;
586 /* something happening on X connection */
587 if (FD_ISSET(xfd
, &trfds
)) {
589 return read_x_event(L
);
599 PUSHNUM(DisplayWidth(dpy
, scr
));
600 PUSHNUM(DisplayHeight(dpy
, scr
));
605 METHOD(x_string2keysym
)
607 KeySym ks
= XStringToKeysym(CHECKSTR(1));
608 if (ks
== NoSymbol
) return 0;
609 lua_pushnumber(L
,ks
);
613 METHOD(x_keysym2string
)
615 char *s
= XKeysymToString(CHECKINT(1));
617 lua_pushstring(L
, s
);
621 METHOD(x_keysym2keycode
)
623 KeyCode kc
= XKeysymToKeycode(dpy
, CHECKINT(1));
625 lua_pushnumber(L
,kc
);
629 METHOD(x_keycode2keysym
)
631 KeySym ks
= XKeycodeToKeysym(dpy
, CHECKINT(1), 0);
632 if (ks
== NoSymbol
) return 0;
633 lua_pushnumber(L
,ks
);
637 /* if argument passed, set a new modmap */
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
++) {
648 for (j
= 0; j
< map
->max_keypermod
; j
++) {
649 KeyCode kc
= map
->modifiermap
[i
* map
->max_keypermod
+ j
];
651 lua_pushnumber(L
, kc
);
652 } else lua_pushnil(L
);
653 lua_rawseti(L
, -2, j
+1);
655 lua_rawseti(L
, -2, i
+1);
660 /* setting new one */
661 map
= XNewModifiermap(8);
662 for (i
= 0; i
< 8; i
++) {
663 lua_rawgeti(L
, 1, i
+1);
665 while (lua_next(L
, -2)) {
666 XInsertModifiermapEntry(map
, TOINT(-1), i
);
667 /* remove the "value", keep key */
671 XSetModifierMapping(dpy
, map
);
672 XFreeModifiermap(map
);
677 METHOD(x_intern_atom
)
679 const char *name
= CHECKSTR(1);
680 int ifexist
= CHECKBOOL(2);
681 Atom a
= XInternAtom(dpy
, name
, ifexist
);
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
)
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
);
710 return MKCURSOR(XCreatePixmapCursor(dpy
, p1
, p2
, &fg
, &bg
, x
, y
));
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) */
724 Cursor c
= CHECKCURSOR(1);
725 int fgp
= OPTINT(2,-1);
726 int bgp
= OPTINT(3,-1);
731 XQueryColor(dpy
, cmap
, &fg
);
735 XQueryColor(dpy
, cmap
, &bg
);
738 XRecolorCursor(dpy
,c
,&fg
,&bg
);
744 Cursor cur
= CHECKCURSOR(1);
746 XFreeCursor(dpy
, cur
);
753 const char *fn
= CHECKSTR(1);
756 myfont_t
*myf
= newudata(L
,sizeof(*myf
),"<font>");
758 // myf->set = XCreateFontSet(dpy, fn, &missing, &n, &def);
760 if (missing
) XFreeStringList(missing
);
763 XFontSetExtents
*font_extents
;
764 XFontStruct
**xfonts
;
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
);
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);
787 /* x.color("#ff0000") */
791 XAllocNamedColor(dpy
,cmap
,CHECKSTR(1),&color
,&color
);
792 lua_pushnumber(L
, color
.pixel
);
798 XSetForeground(dpy
, gc
, TOCOLOR(1));
804 XSetBackground(dpy
, gc
, TOCOLOR(1));
810 myfont_t
*f
= CHECKFONT(1);
812 global_fontset
= f
->set
;
813 DEBUG("set global fontset");
815 global_fontset
= NULL
;
816 XSetFont(dpy
, gc
, f
->xfont
->fid
);
824 dpy
= XOpenDisplay(NULL
);
826 scr
= DefaultScreen(dpy
);
827 root
= RootWindow(dpy
, scr
);
828 cmap
= DefaultColormap(dpy
, scr
);
829 xfd
= ConnectionNumber(dpy
);
833 gc
=XCreateGC(dpy
, root
, 0, 0);
840 XSync(dpy
, lua_toboolean(L
, 1));
852 Window d1
, d2
, *wins
= NULL
;
855 if (!XQueryTree(dpy
, root
, &d1
, &d2
, &wins
, &num
))
858 for (i
= 0; i
< num
; i
++) {
860 lua_rawseti(L
, -2, i
+1);
866 /********************************************************
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
);
894 /* win:set_attributes{attr_tab} */
895 METHOD(win_set_attributes
)
897 Window win
= CHECKWIN(1);
899 XSetWindowAttributes wa
;
901 wmask
= copy_attr(L
, 2, &wa
);
902 XChangeWindowAttributes(dpy
, win
, wmask
, &wa
);
906 /* create a (sub)window:
907 * x.root:create_window(0,0[,w,h,border,{attributes...}])
909 METHOD(win_create_window
)
911 Window nw
, win
= CHECKWIN(1);
914 unsigned int w
= OPTINT(4,-1);
915 unsigned int h
= OPTINT(5,-1);
916 int bw
= OPTINT(6,0);
918 XSetWindowAttributes wa
;
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
);
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
)
942 if (!XQueryPointer(dpy
, CHECKWIN(1), &a
.root
, &a
.win
, &a
.root_x
, &a
.root_y
, &a
.x
, &a
.y
, &a
.mod
)) return 0;
946 QRI(root_x
); QRI(root_y
);
956 METHOD(win_list_properties
)
959 Window win
= CHECKWIN(1);
961 Atom
*al
= XListProperties(dpy
, win
, &nret
);
964 for (i
= 0; i
< nret
; i
++) {
966 lua_rawseti(L
, -2, i
+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);
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
);
988 /* win:get_property(atom[,atom2,off,len]) = {ilist}, real */
989 METHOD(win_get_property
)
994 Window win
= CHECKWIN(1);
995 Atom a1
= CHECKATOM(2);
996 Atom a2
= OPTATOM(3,a1
);
997 unsigned long off
, len
,n
,extra
;
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);
1006 for (i
= 0; i
< n
; i
+= (format
/8)) {
1009 lua_pushnumber(L
, p
[0]);
1012 lua_pushnumber(L
, *(uint16_t *)(p
));
1015 lua_pushnumber(L
, *(uint32_t *)(p
));
1019 luaL_error(L
, "invalid property format");
1021 lua_rawseti(L
, -2, i
+1);
1023 lua_pushnumber(L
, real
);
1028 METHOD(win_get_attributes
)
1030 Window win
= CHECKWIN(1);
1031 XWindowAttributes a
;
1033 if (!XGetWindowAttributes(dpy
, win
, &a
)) return 0;
1036 ATTRI(width
); ATTRI(height
);
1037 ATTRI(border_width
);
1043 ATTRI(backing_store
);
1044 ATTRI(backing_planes
);
1045 ATTRI(backing_pixel
);
1047 ATTRB(map_installed
);
1049 ATTRI(all_event_masks
);
1050 ATTRB(override_redirect
);
1054 /* keycode, modmask */
1055 METHOD(win_grab_key
)
1057 XGrabKey(dpy
, CHECKINT(2), OPTINT(3,AnyModifier
), CHECKWIN(1), True
, GrabModeAsync
, GrabModeAsync
);
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
);
1069 METHOD(win_ungrab_button
)
1071 XUngrabButton(dpy
, CHECKINT(2), OPTINT(3, AnyModifier
), CHECKWIN(1));
1075 METHOD(win_ungrab_key
)
1077 XUngrabKey(dpy
, TOINT(1), TOINT(2), CHECKWIN(1));
1082 * w:send_message(atom)
1084 METHOD(win_send_message
)
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
);
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
)
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
);
1125 /* w:configure {table} */
1126 METHOD(win_configure
)
1129 XWindowChanges
*wa
= &wc
;
1130 unsigned long mask
= 0;
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
);
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));
1156 XClearArea(dpy
, win
, x
, y
, w
, h
, ex
);
1160 /* explicitly kill window */
1163 Window win
= CHECKWIN(1);
1165 /* to avoid destroying twice .. */
1168 XDestroyWindow(dpy
, win
);
1174 Window win
= CHECKWIN(1);
1175 XMapWindow(dpy
, win
);
1181 Window win
= CHECKWIN(1);
1182 XMapWindow(dpy
, win
);
1187 /********************************************************
1189 ********************************************************/
1191 /* pix,hotx,hoty,w,h = root:read_bitmap_file(name) */
1192 METHOD(g_read_bitmap_file
)
1194 Drawable win
= CHECKDRAW(1);
1199 if (XReadBitmapFile(dpy
, win
, CHECKSTR(2), &w
, &h
, &pix
, &x
, &y
) != BitmapSuccess
)
1202 lua_pushnumber(L
, x
);
1203 lua_pushnumber(L
, y
);
1204 lua_pushnumber(L
, w
);
1205 lua_pushnumber(L
, h
);
1211 Pixmap pix
= CHECKPIX(1);
1213 XFreePixmap(dpy
,pix
);
1217 /* win:get_image(x,y,w,h) */
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);
1227 ximg
= XGetImage(dpy
, win
, x
, y
, w
, h
, ~0, ZPixmap
);
1228 if (!ximg
) return 0;
1229 newptr(L
,ximg
, "<image>");
1233 /* win:put_image(img,[sx],[sy],[dx],[dy],[w],[h]) */
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
);
1248 METHOD(g_create_pixmap
)
1250 Drawable win
= CHECKDRAW(1);
1251 unsigned int w
= OPTINT(2, -1);
1252 unsigned int h
= OPTINT(3, -1);
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);
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);
1279 if (lua_toboolean(L
, 3))
1280 XFillRectangles(dpy
, win
, gc
, xr
, count
);
1282 XDrawRectangles(dpy
, win
, gc
, xr
, count
);
1288 Drawable win
= CHECKDRAW(1);
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);
1311 if (lua_toboolean(L
, 3))
1312 XFillArcs(dpy
, win
, gc
, xa
, count
);
1314 XDrawArcs(dpy
, win
, gc
, xa
, count
);
1320 METHOD(g_draw_segments
)
1322 Drawable win
= CHECKDRAW(1);
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);
1341 XDrawSegments(dpy
, win
, gc
, xs
, count
);
1346 METHOD(g_draw_points
)
1348 Drawable win
= CHECKDRAW(1);
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);
1363 XDrawPoints(dpy
, win
, gc
, xp
, count
, CHECKBOOL(3));
1367 METHOD(g_draw_lines
)
1369 Drawable win
= CHECKDRAW(1);
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);
1384 XDrawLines(dpy
, win
, gc
, xp
, count
, CHECKBOOL(3));
1388 /* win:draw_string(str,[x,y]) */
1389 METHOD(g_draw_string
)
1391 Drawable win
= CHECKDRAW(1);
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
);
1404 l
= lua_objlen(L
, 2);
1405 us
= alloca(sizeof(us
[0])*l
);
1406 for (i
= 0; i
< l
; i
++) {
1409 lua_rawgeti(L
, 2, i
+1);
1410 n
= lua_tonumber(L
, -1);
1412 us
[i
].byte2
= n
&0xff;
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);
1423 // XDrawString(dpy, win, gc, x, y, (void*)s, l);
1427 /********************************************************
1429 ********************************************************/
1434 myfont_t
*f
= CHECKFONT(1);
1436 if (f
->set
== global_fontset
)
1437 global_fontset
= NULL
;
1438 XFreeFontSet(dpy
, f
->set
);
1447 myfont_t
*f
= CHECKFONT(1);
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");
1457 myfont_t
*f
= CHECKFONT(1);
1459 const char *text
= luaL_checklstring(L
, 2, &tl
);
1463 XmbTextExtents(f
->set
, text
, tl
, NULL
, &r
);
1464 lua_pushnumber(L
, r
.width
);
1465 } else lua_pushnumber(L
, XTextWidth(f
->xfont
, text
, tl
));
1469 /* unified destructor for window, pixmap, cursor */
1472 void (*destruct
)(Display
*,XID
);
1473 XID xid
, *x
= lua_touserdata(L
, 1);
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");
1487 lua_rawseti(L
, -2, xid
); /* remove destruct .. */
1488 lua_rawseti(L
, -3, xid
); /* and xid */
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
},
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
)
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
)
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
)
1530 static luaL_reg pix_method
[] = {
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
)
1542 static luaL_reg cur_method
[] = {
1543 CUR(destroy
) CUR(recolor
)
1555 void meta_register(lua_State
*L
, const char *mname
, luaL_reg
*meth
)
1557 luaL_newmetatable(L
, mname
);
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
));
1574 /* weak mode metatable */
1576 lua_pushstring(L
, mode
);
1577 lua_setfield(L
, -2, "__mode");
1578 lua_setmetatable(L
, -2);
1579 DEBUG("top=%d",lua_gettop(L
));
1585 lua_State
*L
= lua_open();
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");
1597 lua_setfield(L
, LUA_REGISTRYINDEX
, "<spawn.context>");
1600 lua_setfield(L
, LUA_REGISTRYINDEX
, "xid");
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
);
1626 if(!(dpy
=XOpenDisplay(NULL
))) {
1627 fprintf(stderr
, "ERROR: could not open display\n");
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
,
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
);
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;