build remove -f to hostname since some OS don't support it
[awesome.git] / lua.c
blobc4532e87e1e5b7354832dd4dab04b28464cbd7be
1 /*
2 * lua.c - Lua configuration management
4 * Copyright © 2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <errno.h>
23 #include <stdio.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
30 #include <ev.h>
32 #include <lua.h>
33 #include <lauxlib.h>
34 #include <lualib.h>
36 #include <xcb/xcb.h>
37 #include <xcb/xcb_aux.h>
39 #include "awesome-version-internal.h"
40 #include "ewmh.h"
41 #include "config.h"
42 #include "lua.h"
43 #include "tag.h"
44 #include "client.h"
45 #include "statusbar.h"
46 #include "titlebar.h"
47 #include "screen.h"
48 #include "layouts/tile.h"
49 #include "common/socket.h"
51 extern awesome_t globalconf;
53 extern const struct luaL_reg awesome_keygrabber_lib[];
54 extern const struct luaL_reg awesome_mouse_methods[];
55 extern const struct luaL_reg awesome_mouse_meta[];
56 extern const struct luaL_reg awesome_screen_methods[];
57 extern const struct luaL_reg awesome_screen_meta[];
58 extern const struct luaL_reg awesome_client_methods[];
59 extern const struct luaL_reg awesome_client_meta[];
60 extern const struct luaL_reg awesome_titlebar_methods[];
61 extern const struct luaL_reg awesome_titlebar_meta[];
62 extern const struct luaL_reg awesome_tag_methods[];
63 extern const struct luaL_reg awesome_tag_meta[];
64 extern const struct luaL_reg awesome_widget_methods[];
65 extern const struct luaL_reg awesome_widget_meta[];
66 extern const struct luaL_reg awesome_statusbar_methods[];
67 extern const struct luaL_reg awesome_statusbar_meta[];
68 extern const struct luaL_reg awesome_keybinding_methods[];
69 extern const struct luaL_reg awesome_keybinding_meta[];
71 static struct sockaddr_un *addr;
72 static ev_io csio = { .fd = -1 };
74 /** Add a global mouse binding. This binding will be available when you'll
75 * click on root window.
76 * \param L The Lua VM state.
78 * \luastack
79 * \lparam A mouse button binding.
81 static int
82 luaA_mouse_add(lua_State *L)
84 button_t **button = luaA_checkudata(L, 1, "mouse");
86 button_list_push(&globalconf.buttons.root, *button);
87 button_ref(button);
89 return 0;
92 /** Quit awesome.
94 static int
95 luaA_quit(lua_State *L __attribute__ ((unused)))
97 ev_unloop(globalconf.loop, 1);
98 return 0;
101 /** Execute another application, probably a window manager, to replace
102 * awesome.
103 * \param L The Lua VM state.
105 * \luastack
106 * \lparam The command line to execute.
108 static int
109 luaA_exec(lua_State *L)
111 client_t *c;
112 const char *cmd = luaL_checkstring(L, 1);
114 for(c = globalconf.clients; c; c = c->next)
115 client_unban(c);
117 xcb_aux_sync(globalconf.connection);
118 xcb_disconnect(globalconf.connection);
120 a_exec(cmd);
121 return 0;
124 /** Restart awesome.
126 static int
127 luaA_restart(lua_State *L __attribute__ ((unused)))
129 ewmh_restart();
130 return 0;
133 /** Set the function called each time a client gets focus. This function is
134 * called with the client object as argument.
135 * \param L The Lua VM state.
136 * \return The number of elements pushed on stack.
138 * \luastack
139 * \lparam A function to call each time a client gets focus.
141 static int
142 luaA_hooks_focus(lua_State *L)
144 return luaA_registerfct(L, &globalconf.hooks.focus);
147 /** Set the function called each time a client loses focus. This function is
148 * called with the client object as argument.
149 * \param L The Lua VM state.
151 * \luastack
152 * \lparam A function to call each time a client loses focus.
154 static int
155 luaA_hooks_unfocus(lua_State *L)
157 return luaA_registerfct(L, &globalconf.hooks.unfocus);
160 /** Set the function called each time a new client appears. This function is
161 * called with the client object as argument.
162 * \param L The Lua VM state.
164 * \luastack
165 * \lparam A function to call on each new client.
167 static int
168 luaA_hooks_manage(lua_State *L)
170 return luaA_registerfct(L, &globalconf.hooks.manage);
173 /** Set the function called each time a client goes away. This function is
174 * called with the client object as argument.
175 * \param L The Lua VM state.
177 * \luastack
178 * \lparam A function to call when a client goes away.
180 static int
181 luaA_hooks_unmanage(lua_State *L)
183 return luaA_registerfct(L, &globalconf.hooks.unmanage);
186 /** Set the function called each time the mouse enter a new window. This
187 * function is called with the client object as argument.
188 * \param L The Lua VM state.
190 * \luastack
191 * \lparam A function to call each time a client gets mouse over it.
193 static int
194 luaA_hooks_mouseover(lua_State *L)
196 return luaA_registerfct(L, &globalconf.hooks.mouseover);
199 /** Set the function called on each screen arrange. This function is called
200 * with the screen number as argument.
201 * \param L The Lua VM state.
203 * \luastack
204 * \lparam A function to call on each screen arrange.
206 static int
207 luaA_hooks_arrange(lua_State *L)
209 return luaA_registerfct(L, &globalconf.hooks.arrange);
212 /** Set the function called on each title update. This function is called with
213 * the client object as argument.
214 * \param L The Lua VM state.
216 * \luastack
217 * \lparam A function to call on each title update of each client.
219 static int
220 luaA_hooks_titleupdate(lua_State *L)
222 return luaA_registerfct(L, &globalconf.hooks.titleupdate);
225 /** Set the function called when a client get urgency flag. This function is called with
226 * the client object as argument.
227 * \param L The Lua VM state.
229 * \luastack
230 * \lparam A function to call when a client get the urgent flag.
232 static int
233 luaA_hooks_urgent(lua_State *L)
235 return luaA_registerfct(L, &globalconf.hooks.urgent);
238 /** Set the function to be called every N seconds.
239 * \param L The Lua VM state.
241 * \luastack
242 * \lparam The number of seconds to run function every. Set 0 to disable.
243 * \lparam A function to call every N seconds (optional).
245 static int
246 luaA_hooks_timer(lua_State *L)
248 globalconf.timer.repeat = luaL_checknumber(L, 1);
250 if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
251 luaA_registerfct(L, &globalconf.hooks.timer);
253 ev_timer_again(globalconf.loop, &globalconf.timer);
254 return 0;
257 /** Set default font.
258 * \param L The Lua VM state.
260 * \luastack
261 * \lparam A string with a font name in Pango format.
263 static int
264 luaA_font_set(lua_State *L)
266 const char *font = luaL_checkstring(L, 1);
267 draw_font_delete(&globalconf.font);
268 globalconf.font = draw_font_new(globalconf.connection,
269 globalconf.default_screen, font);
270 return 0;
273 /** Set default colors.
274 * \param L The Lua VM state.
276 * \luastack
277 * \lparam A table with `fg' and `bg' elements, containing colors.
279 static int
280 luaA_colors_set(lua_State *L)
282 const char *buf;
283 size_t len;
284 int8_t colors_nbr = -1, i;
285 xcolor_init_request_t reqs[2];
287 luaA_checktable(L, 1);
289 if((buf = luaA_getopt_lstring(L, 1, "fg", NULL, &len)))
290 reqs[++colors_nbr] = xcolor_init_unchecked(globalconf.connection,
291 &globalconf.colors.fg,
292 globalconf.default_screen,
293 buf, len);
295 if((buf = luaA_getopt_lstring(L, 1, "bg", NULL, &len)))
296 reqs[++colors_nbr] = xcolor_init_unchecked(globalconf.connection,
297 &globalconf.colors.bg,
298 globalconf.default_screen,
299 buf, len);
301 for(i = 0; i <= colors_nbr; i++)
302 xcolor_init_reply(globalconf.connection, reqs[i]);
304 return 0;
307 /** Push a pointer onto the stack according to its type.
308 * \param p The pointer.
309 * \param type Its type.
311 void
312 luaA_pushpointer(lua_State *L, void *p, awesome_type_t type)
314 switch(type)
316 case AWESOME_TYPE_STATUSBAR:
317 luaA_statusbar_userdata_new(L, p);
318 break;
319 case AWESOME_TYPE_TITLEBAR:
320 luaA_titlebar_userdata_new(L, p);
321 break;
325 static void
326 luaA_openlib(lua_State *L, const char *name,
327 const struct luaL_reg methods[],
328 const struct luaL_reg meta[])
330 luaL_newmetatable(L, name); /* 1 */
331 lua_pushvalue(L, -1); /* dup metatable 2 */
332 lua_setfield(L, -2, "__index"); /* metatable.__index = metatable 1 */
334 luaL_register(L, NULL, meta); /* 1 */
335 luaL_register(L, name, methods); /* 2 */
336 lua_pushvalue(L, -1); /* dup self as metatable 3 */
337 lua_setmetatable(L, -2); /* set self as metatable 2 */
338 lua_pop(L, 2);
341 static int
342 luaA_mbstrlen(lua_State *L)
344 const char *cmd = luaL_checkstring(L, 1);
345 lua_pushnumber(L, mbstowcs(NULL, NONULL(cmd), 0));
346 return 1;
349 static int
350 luaA_next(lua_State *L)
352 if(luaL_getmetafield(L, 1, "__next"))
354 lua_insert(L, 1);
355 lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
356 return lua_gettop(L);
359 luaL_checktype(L, 1, LUA_TTABLE);
360 lua_settop(L, 2);
361 if(lua_next(L, 1))
362 return 2;
363 lua_pushnil(L);
364 return 1;
367 static int
368 luaA_pairs(lua_State *L)
370 if(luaL_getmetafield(L, 1, "__pairs"))
372 lua_insert(L, 1);
373 lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
374 return lua_gettop(L);
377 luaL_checktype(L, 1, LUA_TTABLE);
378 return luaA_generic_pairs(L);
381 static void
382 luaA_fixups(lua_State *L)
384 lua_getglobal(L, "string");
385 lua_pushcfunction(L, luaA_mbstrlen);
386 lua_setfield(L, -2, "len");
387 lua_pop(L, 1);
388 lua_pushliteral(L, "next");
389 lua_pushcfunction(L, luaA_next);
390 lua_settable(L, LUA_GLOBALSINDEX);
391 lua_pushliteral(L, "pairs");
392 lua_pushcfunction(L, luaA_next);
393 lua_pushcclosure(L, luaA_pairs, 1); /* pairs get next as upvalue */
394 lua_settable(L, LUA_GLOBALSINDEX);
397 /** Object table.
398 * This table can use safely object as key.
399 * \param L The Lua VM state.
400 * \return The number of elements pushed on stack.
403 luaA_otable_index(lua_State *L)
405 void **obj, **v;
407 if((obj = lua_touserdata(L, 2)))
409 /* begins at nil */
410 lua_pushnil(L);
411 while(lua_next(L, 1))
413 /* check the key against the key if the key is a userdata,
414 * otherwise check it again the value. */
415 if((v = lua_touserdata(L, -2))
416 && *v == *obj)
417 /* return value */
418 return 1;
419 else if((v = lua_touserdata(L, -1))
420 && *v == *obj)
422 /* return key */
423 lua_pop(L, 1);
424 return 1;
426 /* removes 'value'; keeps 'key' for next iteration */
427 lua_pop(L, 1);
429 return 0;
432 lua_rawget(L, 1);
433 return 1;
436 /** Object table.
437 * This table can use safely object as key.
438 * \param L The Lua VM state.
439 * \return The number of elements pushed on stack.
441 static int
442 luaA_otable_newindex(lua_State *L)
444 void **obj, **v;
446 if((obj = lua_touserdata(L, 2)))
448 /* begins at nil */
449 lua_pushnil(L);
450 while(lua_next(L, 1))
452 if((v = lua_touserdata(L, -2))
453 && *v == *obj)
455 /* remove value */
456 lua_pop(L, 1);
457 /* push new value on top */
458 lua_pushvalue(L, 3);
459 /* set in table key = value */
460 lua_rawset(L, 1);
461 return 0;
463 /* removes 'value'; keeps 'key' for next iteration */
464 lua_pop(L, 1);
468 lua_rawset(L, 1);
470 return 0;
473 /** Spawn a program.
474 * This function is multi-head (Zaphod) aware and will set display to
475 * the right screen according to mouse position.
476 * \param L The Lua VM state.
477 * \return The number of elements pushed on stack
478 * \luastack
479 * \lparam The command to launch.
480 * \lparam The optional screen number to spawn the command on.
482 static int
483 luaA_spawn(lua_State *L)
485 static const char *shell = NULL;
486 char display[128], *tmp, newdisplay[128];
487 const char *cmd;
488 int screen = 0;
490 if(!shell && !(shell = getenv("SHELL")))
491 shell = "/bin/sh";
493 if(lua_gettop(L) == 2)
495 screen = luaL_checknumber(L, 2) - 1;
496 luaA_checkscreen(screen);
499 cmd = luaL_checkstring(L, 1);
501 if(!globalconf.screens_info->xinerama_is_active
502 && (tmp = getenv("DISPLAY")))
504 a_strcpy(display, sizeof(display) - 1, tmp);
505 if((tmp = strrchr(display, '.')))
506 *tmp = '\0';
507 snprintf(newdisplay, sizeof(newdisplay) - 1, "%s.%d", display, screen);
508 setenv("DISPLAY", newdisplay, 1);
511 /* The double-fork construct avoids zombie processes and keeps the code
512 * clean from stupid signal handlers. */
513 if(fork() == 0)
515 if(fork() == 0)
517 if(globalconf.connection)
518 xcb_disconnect(globalconf.connection);
519 setsid();
520 execl(shell, shell, "-c", cmd, NULL);
521 warn("execl '%s -c %s' failed: %s\n", shell, cmd, strerror(errno));
523 exit(EXIT_SUCCESS);
525 wait(0);
527 return 0;
530 /** Initialize the Lua VM
532 void
533 luaA_init(void)
535 lua_State *L;
537 static const struct luaL_reg otable_methods[] =
539 { "__call", luaA_otable_new },
540 { NULL, NULL }
542 static const struct luaL_reg otable_meta[] =
544 { "__index", luaA_otable_index },
545 { "__newindex", luaA_otable_newindex },
546 { NULL, NULL }
548 static const struct luaL_reg awesome_lib[] =
550 { "quit", luaA_quit },
551 { "exec", luaA_exec },
552 { "spawn", luaA_spawn },
553 { "restart", luaA_restart },
554 { "mouse_add", luaA_mouse_add },
555 { "font_set", luaA_font_set },
556 { "colors_set", luaA_colors_set },
557 { NULL, NULL }
559 static const struct luaL_reg awesome_hooks_lib[] =
561 { "focus", luaA_hooks_focus },
562 { "unfocus", luaA_hooks_unfocus },
563 { "manage", luaA_hooks_manage },
564 { "unmanage", luaA_hooks_unmanage },
565 { "mouseover", luaA_hooks_mouseover },
566 { "arrange", luaA_hooks_arrange },
567 { "titleupdate", luaA_hooks_titleupdate },
568 { "urgent", luaA_hooks_urgent },
569 { "timer", luaA_hooks_timer },
570 { NULL, NULL }
573 L = globalconf.L = luaL_newstate();
575 luaL_openlibs(L);
577 luaA_fixups(L);
579 /* Export awesome lib */
580 luaL_register(L, "awesome", awesome_lib);
582 /* Export hooks lib */
583 luaL_register(L, "hooks", awesome_hooks_lib);
585 /* Export keygrabber lib */
586 luaL_register(L, "keygrabber", awesome_keygrabber_lib);
588 /* Export otable lib */
589 luaA_openlib(L, "otable", otable_methods, otable_meta);
591 /* Export screen */
592 luaA_openlib(L, "screen", awesome_screen_methods, awesome_screen_meta);
594 /* Export mouse */
595 luaA_openlib(L, "mouse", awesome_mouse_methods, awesome_mouse_meta);
597 /* Export tag */
598 luaA_openlib(L, "tag", awesome_tag_methods, awesome_tag_meta);
600 /* Export statusbar */
601 luaA_openlib(L, "statusbar", awesome_statusbar_methods, awesome_statusbar_meta);
603 /* Export widget */
604 luaA_openlib(L, "widget", awesome_widget_methods, awesome_widget_meta);
606 /* Export client */
607 luaA_openlib(L, "client", awesome_client_methods, awesome_client_meta);
609 /* Export titlebar */
610 luaA_openlib(L, "titlebar", awesome_titlebar_methods, awesome_titlebar_meta);
612 /* Export keys */
613 luaA_openlib(L, "keybinding", awesome_keybinding_methods, awesome_keybinding_meta);
615 lua_pushliteral(L, "AWESOME_VERSION");
616 lua_pushstring(L, AWESOME_VERSION);
617 lua_settable(L, LUA_GLOBALSINDEX);
619 luaA_dostring(L, "package.path = package.path .. \";" AWESOME_LUA_LIB_PATH "/?.lua\"");
620 luaA_dostring(L, "package.path = package.path .. \";" AWESOME_LUA_LIB_PATH "/?/init.lua\"");
622 /* init hooks */
623 globalconf.hooks.manage = LUA_REFNIL;
624 globalconf.hooks.unmanage = LUA_REFNIL;
625 globalconf.hooks.focus = LUA_REFNIL;
626 globalconf.hooks.unfocus = LUA_REFNIL;
627 globalconf.hooks.mouseover = LUA_REFNIL;
628 globalconf.hooks.arrange = LUA_REFNIL;
629 globalconf.hooks.titleupdate = LUA_REFNIL;
630 globalconf.hooks.urgent = LUA_REFNIL;
631 globalconf.hooks.timer = LUA_REFNIL;
634 #define XDG_CONFIG_HOME_DEFAULT "/.config"
636 #define AWESOME_CONFIG_FILE "/awesome/rc.lua"
638 /** Load a configuration file.
639 * \param rcfile The configuration file to load.
641 void
642 luaA_parserc(const char *confpatharg)
644 int screen;
645 const char *confdir, *xdg_config_dirs;
646 char *confpath = NULL, **xdg_files, **buf, path[1024];
647 ssize_t len;
649 if(confpatharg)
651 if(luaL_dofile(globalconf.L, confpatharg))
652 fprintf(stderr, "%s\n", lua_tostring(globalconf.L, -1));
653 else
654 goto bailout;
657 confdir = getenv("XDG_CONFIG_HOME");
659 if((len = a_strlen(confdir)))
661 len += sizeof(AWESOME_CONFIG_FILE);
662 confpath = p_new(char, len);
663 a_strcpy(confpath, len, confdir);
664 /* update package.path */
665 snprintf(path, sizeof(path) - 1, "package.path = package.path .. \";%s/awesome/?.lua\"", confdir);
666 luaA_dostring(globalconf.L, path);
668 else
670 confdir = getenv("HOME");
671 len = a_strlen(confdir) + sizeof(AWESOME_CONFIG_FILE)-1 + sizeof(XDG_CONFIG_HOME_DEFAULT);
672 confpath = p_new(char, len);
673 a_strcpy(confpath, len, confdir);
674 a_strcat(confpath, len, XDG_CONFIG_HOME_DEFAULT);
675 /* update package.path */
676 snprintf(path, sizeof(path) - 1, "package.path = package.path .. \";%s" XDG_CONFIG_HOME_DEFAULT "/awesome/?.lua\"", confdir);
677 luaA_dostring(globalconf.L, path);
679 a_strcat(confpath, len, AWESOME_CONFIG_FILE);
681 if(luaL_dofile(globalconf.L, confpath))
682 fprintf(stderr, "%s\n", lua_tostring(globalconf.L, -1));
683 else
684 goto bailout;
686 xdg_config_dirs = getenv("XDG_CONFIG_DIRS");
688 if(!(len = a_strlen(xdg_config_dirs)))
690 xdg_config_dirs = XDG_CONFIG_DIR;
691 len = sizeof(XDG_CONFIG_DIR) - 1;
694 xdg_files = a_strsplit(xdg_config_dirs, len, ':');
696 for(buf = xdg_files; *buf; buf++)
698 p_delete(&confpath);
699 len = a_strlen(*buf) + sizeof("AWESOME_CONFIG_FILE");
700 confpath = p_new(char, len);
701 a_strcpy(confpath, len, *buf);
702 a_strcat(confpath, len, AWESOME_CONFIG_FILE);
703 snprintf(path, sizeof(path) - 1, "package.path = package.path .. \";%s/awesome/?.lua\"", *buf);
704 luaA_dostring(globalconf.L, path);
705 if(luaL_dofile(globalconf.L, confpath))
706 fprintf(stderr, "%s\n", lua_tostring(globalconf.L, -1));
707 else
708 break;
711 for(buf = xdg_files; *buf; buf++)
712 p_delete(buf);
713 p_delete(&xdg_files);
715 bailout:
716 /* Assure there's at least one tag */
717 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
718 if(!globalconf.screens[screen].tags.len)
719 tag_append_to_screen(tag_new("default", sizeof("default")-1, layout_tile, 0.5, 1, 0),
720 &globalconf.screens[screen]);
722 p_delete(&confpath);
725 /** Parse a command.
726 * \param cmd The buffer to parse.
727 * \return true on succes, false on failure.
729 static void
730 luaA_docmd(const char *cmd)
732 char *p;
734 while((p = strchr(cmd, '\n')))
736 *p = '\0';
737 luaA_dostring(globalconf.L, cmd);
738 cmd = p + 1;
742 static void
743 luaA_cb(EV_P_ ev_io *w, int revents)
745 char buf[1024];
746 int r;
748 switch(r = recv(w->fd, buf, sizeof(buf)-1, MSG_TRUNC))
750 case -1:
751 warn("error reading UNIX domain socket: %s", strerror(errno));
752 luaA_cs_cleanup();
753 break;
754 case 0:
755 break;
756 default:
757 if(r >= ssizeof(buf))
758 break;
759 buf[r] = '\0';
760 luaA_docmd(buf);
762 layout_refresh();
763 statusbar_refresh();
764 titlebar_refresh();
767 void
768 luaA_cs_init(void)
770 int csfd = socket_getclient();
772 if (csfd < 0)
773 return;
774 addr = socket_getaddr(getenv("DISPLAY"));
776 if(bind(csfd, (const struct sockaddr *) addr, SUN_LEN(addr)))
778 if(errno == EADDRINUSE)
780 if(unlink(addr->sun_path))
781 warn("error unlinking existing file: %s", strerror(errno));
782 if(bind(csfd, (const struct sockaddr *) addr, SUN_LEN(addr)))
783 warn("error binding UNIX domain socket: %s", strerror(errno));
785 else
786 warn("error binding UNIX domain socket: %s", strerror(errno));
788 ev_io_init(&csio, &luaA_cb, csfd, EV_READ);
789 ev_io_start(EV_DEFAULT_UC_ &csio);
790 ev_unref(EV_DEFAULT_UC);
793 void
794 luaA_cs_cleanup(void)
796 if(csio.fd < 0)
797 return;
798 ev_ref(EV_DEFAULT_UC);
799 ev_io_stop(EV_DEFAULT_UC_ &csio);
800 if (close(csio.fd))
801 warn("error closing UNIX domain socket: %s", strerror(errno));
802 if(unlink(addr->sun_path))
803 warn("error unlinking UNIX domain socket: %s", strerror(errno));
804 p_delete(&addr);
805 csio.fd = -1;
808 void
809 luaA_on_timer(EV_P_ ev_timer *w, int revents)
811 luaA_dofunction(globalconf.L, globalconf.hooks.timer, 0, 0);
812 layout_refresh();
813 statusbar_refresh();
814 titlebar_refresh();
817 void
818 luaA_pushcolor(lua_State *L, const xcolor_t *c)
820 uint8_t r = (unsigned)c->red * 0xff / 0xffff;
821 uint8_t g = (unsigned)c->green * 0xff / 0xffff;
822 uint8_t b = (unsigned)c->blue * 0xff / 0xffff;
823 uint8_t a = (unsigned)c->alpha * 0xff / 0xffff;
824 char s[10];
825 snprintf(s, sizeof(s), "#%02x%02x%02x%02x", r, g, b, a);
826 lua_pushlstring(L, s, sizeof(s));