353 lines
8.3 KiB
C
353 lines
8.3 KiB
C
/**
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
|
|
#include <libwnck/libwnck.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <gdk/gdk.h>
|
|
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
#include <lauxlib.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#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;
|
|
}
|
|
|