/** * This file is part of devilspie2 * Copyright (C) 2011-2019 Andreas Rönnquist * Copyright (C) 2019-2021 Darren Salt * * devilspie2 is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * devilspie2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with devilspie2. * If not, see . */ #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include #include #include #include #include #include #include #include "compat.h" #include "intl.h" #include "script.h" #if (GTK_MAJOR_VERSION >= 3) #define HAVE_GTK3 #endif #include "script_functions.h" /** * */ gboolean devilspie2_debug = FALSE; gboolean devilspie2_emulate = FALSE; lua_State *global_lua_state = NULL; /** * */ lua_State * init_script() { lua_State *lua = luaL_newstate(); luaL_openlibs(lua); register_cfunctions(lua); return lua; } /** * */ #define DP2_REGISTER(lua, name) lua_register(lua, #name, c_##name) void register_cfunctions(lua_State *lua) { DP2_REGISTER(lua, use_utf8); DP2_REGISTER(lua, get_window_name); DP2_REGISTER(lua, get_window_has_name); DP2_REGISTER(lua, set_window_position); DP2_REGISTER(lua, set_window_position2); DP2_REGISTER(lua, set_window_size); DP2_REGISTER(lua, set_window_strut); DP2_REGISTER(lua, set_window_geometry); DP2_REGISTER(lua, set_window_geometry2); DP2_REGISTER(lua, get_application_name); DP2_REGISTER(lua, debug_print); DP2_REGISTER(lua, shade); DP2_REGISTER(lua, unshade); DP2_REGISTER(lua, maximize); lua_register(lua, "maximise", c_maximize); DP2_REGISTER(lua, maximize_horisontally); // deprecated DP2_REGISTER(lua, maximize_horizontally); lua_register(lua, "maximise_horizontally", c_maximize_horizontally); DP2_REGISTER(lua, maximize_vertically); lua_register(lua, "maximise_vertically", c_maximize_vertically); DP2_REGISTER(lua, unmaximize); lua_register(lua, "unmaximise", c_unmaximize); DP2_REGISTER(lua, minimize); lua_register(lua, "minimise", c_minimize); DP2_REGISTER(lua, unminimize); lua_register(lua, "unminimise", c_unminimize); DP2_REGISTER(lua, decorate_window); DP2_REGISTER(lua, undecorate_window); DP2_REGISTER(lua, set_window_workspace); DP2_REGISTER(lua, change_workspace); DP2_REGISTER(lua, get_workspace_count); DP2_REGISTER(lua, pin_window); DP2_REGISTER(lua, unpin_window); DP2_REGISTER(lua, stick_window); DP2_REGISTER(lua, unstick_window); DP2_REGISTER(lua, close_window); DP2_REGISTER(lua, set_adjust_for_decoration); DP2_REGISTER(lua, get_window_geometry); DP2_REGISTER(lua, get_window_client_geometry); DP2_REGISTER(lua, get_window_frame_extents); DP2_REGISTER(lua, set_skip_tasklist); DP2_REGISTER(lua, set_skip_pager); DP2_REGISTER(lua, get_window_is_maximized); lua_register(lua, "get_window_is_maximised", c_get_window_is_maximized); DP2_REGISTER(lua, get_window_is_maximized_vertically); lua_register(lua, "get_window_is_maximised_vertically", c_get_window_is_maximized_vertically); lua_register(lua, "get_window_is_maximized_horisontally", // deprecated c_get_window_is_maximized_horisontally); DP2_REGISTER(lua, get_window_is_maximized_horizontally); lua_register(lua, "get_window_is_maximised_horizontally", c_get_window_is_maximized_horizontally); DP2_REGISTER(lua, get_window_is_pinned); DP2_REGISTER(lua, get_window_is_decorated); DP2_REGISTER(lua, set_window_below); DP2_REGISTER(lua, set_window_above); DP2_REGISTER(lua, set_window_fullscreen); DP2_REGISTER(lua, make_always_on_top); DP2_REGISTER(lua, set_on_top); DP2_REGISTER(lua, set_on_bottom); DP2_REGISTER(lua, get_window_type); DP2_REGISTER(lua, get_window_property); DP2_REGISTER(lua, window_property_is_utf8); DP2_REGISTER(lua, get_window_property_full); DP2_REGISTER(lua, get_window_role); DP2_REGISTER(lua, get_window_xid); DP2_REGISTER(lua, get_window_class); DP2_REGISTER(lua, set_window_property); DP2_REGISTER(lua, delete_window_property); DP2_REGISTER(lua, set_viewport); DP2_REGISTER(lua, center); lua_register(lua, "centre", c_center); DP2_REGISTER(lua, set_window_opacity); lua_register(lua, "set_opacity", c_set_window_opacity); DP2_REGISTER(lua, set_window_type); DP2_REGISTER(lua, get_screen_geometry); DP2_REGISTER(lua, get_window_fullscreen); lua_register(lua, "get_fullscreen", c_get_window_fullscreen); DP2_REGISTER(lua, get_window_strut); // wnck_window_get_class_{instance,group}_name are only availible on wnck 3 and later DP2_REGISTER(lua, get_class_instance_name); DP2_REGISTER(lua, get_class_group_name); DP2_REGISTER(lua, focus); lua_register(lua, "focus_window", c_focus); DP2_REGISTER(lua, get_monitor_index); DP2_REGISTER(lua, get_monitor_geometry); DP2_REGISTER(lua, xy); DP2_REGISTER(lua, xywh); DP2_REGISTER(lua, on_geometry_changed); DP2_REGISTER(lua, get_process_name); DP2_REGISTER(lua, millisleep); } /** * */ static ATTR_MALLOC gchar *error_add_backtrace(lua_State *lua, const char *msg) { int r, level = 0, lines = 0; lua_Debug state; const gchar *header = _("Backtrace:\n"); gchar *backtrace = (gchar*)msg; while ((r = lua_getstack(lua, level, &state)) != 0) { lua_getinfo(lua, "Sln", &state); // only report script locations; // C code has name="[C]" & currentline=-1 if (state.currentline > 0) { char *traced = state.name ? g_strdup_printf("%s\n%s %s:%d [%-6s] %s", backtrace, header, state.short_src, state.currentline, state.namewhat, state.name) : g_strdup_printf("%s\n%s %s:%d [%-6s]", backtrace, header, state.short_src, state.currentline, state.what); header = ""; if (backtrace != msg) g_free(backtrace); backtrace = traced; ++lines; } ++level; } if (lines > 1) return backtrace; if (backtrace != msg) g_free(backtrace); return g_strdup(msg); } static ATTR_MALLOC gchar *error_add_location(lua_State* lua, const char *msg) { lua_Debug state; int r = lua_getstack(lua, 0, &state); if (r == 0) return g_strdup(msg); lua_getinfo(lua, "Sln", &state); // the error handler will add a backtrace, so no need for function info return g_strdup_printf("%s:%d: %s", state.short_src, state.currentline, msg); } static gboolean timedout = FALSE; static void timeout_script(int sig) { timedout = TRUE; } static void check_timeout_script(lua_State *lua, lua_Debug *state) { // state is invalid? if (!timedout) return; // don't add backtrace etc. here; just the location gchar *msg = error_add_location(lua, _("script timed out")); lua_pushstring(lua, msg); g_free(msg); lua_error(lua); } static int script_error(lua_State *lua) { const char *msg = lua_tostring(lua, -1); lua_pop(lua, 1); // only the backtrace here, as the location's probably present already gchar *fullmsg = error_add_backtrace(lua, msg); lua_pushstring(lua, fullmsg); g_free(fullmsg); return 1; } /** * */ int run_script(lua_State *lua, const char *filename) { #define SCRIPT_TIMEOUT_SECONDS 5 if (!lua) return -1; lua_pushcfunction(lua, script_error); int errpos = lua_gettop(lua); int result = luaL_loadfile(lua, filename); if (result) { // We got an error, print it printf(_("Error: %s\n"), lua_tostring(lua, -1)); lua_remove(lua, errpos); // unstack the error handler lua_pop(lua, 1); return -1; } // Okay, loaded the script; now run it struct sigaction newact, oldact; newact.sa_handler = timeout_script; sigemptyset(&newact.sa_mask); newact.sa_flags = 0; timedout = FALSE; lua_sethook(lua, check_timeout_script, LUA_MASKCOUNT, 1); sigaction(SIGALRM, &newact, &oldact); alarm(SCRIPT_TIMEOUT_SECONDS); int s = lua_pcall(lua, 0, LUA_MULTRET, errpos); alarm(0); sigaction(SIGALRM, &oldact, NULL); lua_remove(lua, errpos); // unstack the error handler if (s) { // no info to add here; just output the error printf(_("Error: %s\n"), lua_tostring(lua, -1)); lua_pop(lua, 1); // else we leak it } return 0; } /** * */ void done_script(lua_State *lua) { if (lua) lua_close(lua); //lua=NULL; }