This commit is contained in:
Jeff Carr 2024-11-24 06:57:05 -06:00
commit cfd6fdadd7
16 changed files with 5247 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.swp
go.mod
go.sum
files/
xstartplacement

35
Makefile Normal file
View File

@ -0,0 +1,35 @@
.PHONY: build
VERSION = $(shell git describe --tags)
BUILDTIME = $(shell date +%Y.%m.%d)
build:
GO111MODULE=off go build \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
verbose:
GO111MODULE=off go build -v -x \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
install:
GO111MODULE=off go install \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
# makes a .deb package
debian:
rm -f ~/incoming/virtigo*deb
go-deb --no-gui --repo go.wit.com/apps/virtigo
# autofixes your import headers in your golang files
goimports:
goimports -w *.go
# remake the go.mod and go.sum files
redomod:
rm -f go.*
GO111MODULE= go mod init
GO111MODULE= go mod tidy
clean:
rm -f go.*
rm -f virtigo*

29
devilspie/compat.h Normal file
View File

@ -0,0 +1,29 @@
/**
* This file is part of devilspie2
* Copyright (C) 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/>.
*/
#ifndef __HEADER_COMPAT_
#define __HEADER_COMPAT_
#if defined(__GNUC__) || defined(__clang__)
# define ATTR_MALLOC __attribute__((malloc))
#else
# define ATTR_MALLOC
#endif
#endif /* __HEADER_COMPAT_ */

282
devilspie/config.c Normal file
View File

@ -0,0 +1,282 @@
/**
* This file is part of devilspie2
* Copyright (C) 2013-2017 Andreas Rönnquist
*
* 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/>.
*/
#include <glib.h>
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
#include <glib/gi18n.h>
#include <locale.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "script.h"
#include "script_functions.h"
#include "config.h"
/**
*
*/
GSList *event_lists[W_NUM_EVENTS] = { NULL, NULL, NULL, NULL, NULL };
const char *const event_names[W_NUM_EVENTS] = {
"window_open",
"window_close",
"window_focus",
"window_blur",
"window_name_change",
};
/**
* filename_list_sortfunc
* function to sort the inserted filenames, to be able to determine
* which order files are loaded.
*/
static gint filename_list_sortfunc(gconstpointer a,gconstpointer b)
{
gchar *file1 = (gchar*)a;
gchar *file2 = (gchar*)b;
return g_ascii_strcasecmp(file1, file2);
}
/**
*
*/
static GSList *add_lua_file_to_list(GSList *list, gchar *filename)
{
gchar *temp_filename = g_strdup(filename);
list=g_slist_insert_sorted(list,
temp_filename,
filename_list_sortfunc);
return list;
}
/**
*
*/
static GSList *get_table_of_strings(lua_State *luastate,
gchar *script_folder,
gchar *table_name)
{
GSList *list = NULL;
if (luastate) {
lua_getglobal(luastate, table_name);
// Do we have a value?
if (lua_isnil(luastate, -1)) {
goto EXITPOINT;
}
// Is it a table?
if (!lua_istable(luastate, -1)) {
goto EXITPOINT;
}
lua_pushnil(luastate);
while(lua_next(luastate, -2)) {
if (lua_isstring(luastate, -1)) {
char *temp = (char *)lua_tostring(luastate, -1);
gchar *added_filename = g_build_path(G_DIR_SEPARATOR_S,
script_folder,
temp,
NULL);
list = add_lua_file_to_list(list, added_filename);
}
lua_pop(luastate, 1);
}
lua_pop(luastate, 1);
}
EXITPOINT:
return list;
}
/**
* is_in_list
* Go through _one_ list, and check if the filename is in this list
*/
static gboolean is_in_list(GSList *list, gchar *filename)
{
gboolean result = FALSE;
if (list) {
GSList *temp_list = list;
while (temp_list) {
gchar *list_filename = (gchar*)temp_list->data;
if (list_filename) {
if (g_ascii_strcasecmp(list_filename, filename) == 0) {
result = TRUE;
}
}
temp_list = temp_list->next;
}
}
return result;
}
/**
* is_in_any_list
* Go through our lists, and check if the file is already in any of them
*/
static gboolean is_in_any_list(gchar *filename)
{
win_event_type i;
for (i=0; i < W_NUM_EVENTS; i++) {
if (is_in_list(event_lists[i], filename))
return TRUE;
}
return FALSE;
}
/**
* load_config
* Load configuration from a file - From this we set up the lists of files
* which decides what script to load on what wnck event.
*/
int load_config(gchar *filename)
{
lua_State *config_lua_state = NULL;
int result = 0;
const gchar *current_file = NULL;
GSList *temp_window_open_file_list = NULL;
// First get list of Lua files in folder - Then read variables from
// devilspie2.lua and put the files in the required lists.
gchar *script_folder = g_path_get_dirname(filename);
GDir *dir = g_dir_open(script_folder, 0, NULL);
if (!g_file_test(script_folder, G_FILE_TEST_IS_DIR)) {
printf("%s\n", _("script_folder isn't a folder."));
return -1;
}
int total_number_of_files = 0;
config_lua_state = init_script();
if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
if (run_script(config_lua_state, filename) != 0) {
printf(_("Error: %s\n"), filename);
result = -1;
goto EXITPOINT;
}
event_lists[W_CLOSE] = get_table_of_strings(config_lua_state,
script_folder,
"scripts_window_close");
event_lists[W_FOCUS] = get_table_of_strings(config_lua_state,
script_folder,
"scripts_window_focus");
event_lists[W_BLUR] = get_table_of_strings(config_lua_state,
script_folder,
"scripts_window_blur");
event_lists[W_NAME_CHANGED] = get_table_of_strings(config_lua_state,
script_folder,
"scripts_window_name_change");
}
// add the files in the folder to our linked list
while ((current_file = g_dir_read_name(dir))) {
gchar *temp_filename = g_build_path(G_DIR_SEPARATOR_S,
script_folder,
current_file,
NULL);
// we only bother with *.lua in the folder
// we also ignore dot files
if (current_file[0] != '.' && g_str_has_suffix(current_file, ".lua")) {
if (!is_in_any_list(temp_filename)) {
temp_window_open_file_list =
add_lua_file_to_list(temp_window_open_file_list, temp_filename);
}
total_number_of_files++;
}
g_free(temp_filename);
}
event_lists[W_OPEN] = temp_window_open_file_list;
EXITPOINT:
if (config_lua_state)
done_script(config_lua_state);
return result;
}
/**
*
*/
static void unallocate_file_list(GSList *file_list)
{
if (file_list) {
while(file_list) {
g_free ((gchar*)file_list->data);
file_list = file_list->next;
}
}
}
/**
*
*/
void clear_file_lists()
{
win_event_type i = 0;
for (i = 0; i < W_NUM_EVENTS; i++) {
if (event_lists[i]) {
unallocate_file_list(event_lists[i]);
g_slist_free(event_lists[i]);
event_lists[i] = NULL;
}
}
}

43
devilspie/config.h Normal file
View File

@ -0,0 +1,43 @@
/**
* This file is part of devilspie2
* Copyright (C) 2013-2017 Andreas Rönnquist
*
* 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/>.
*/
#ifndef __HEADER_CONFIG_
#define __HEADER_CONFIG_
#include "glib.h"
int load_config(gchar *config_filename);
void clear_file_lists();
typedef enum {
W_OPEN,
W_CLOSE,
W_FOCUS,
W_BLUR,
W_NAME_CHANGED,
W_NUM_EVENTS /* keep this at the end */
} win_event_type;
extern GSList *event_lists[W_NUM_EVENTS];
extern const char *const event_names[W_NUM_EVENTS];
// Our git version which is defined through some magic in the build system
extern const char *gitversion;
#endif /*__HEADER_CONFIG_*/

517
devilspie/devilspie2.c Normal file
View File

@ -0,0 +1,517 @@
/**
* This file is part of devilspie2
* Copyright (C) 2005 Ross Burton, 2011-2017 Andreas Rönnquist
*
* 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/>.
*/
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <glib/gi18n.h>
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <locale.h>
#include "script.h"
#include "script_functions.h"
#include "error_strings.h"
#include "config.h"
#if (GTK_MAJOR_VERSION >= 3)
#define HAVE_GTK3
#endif
/**
*
*/
GMainLoop *loop = NULL;
static gboolean debug = FALSE;
static gboolean emulate = FALSE;
static gboolean show_version = FALSE;
// libwnck Version Information is only availible if you have
// libwnck 3.0 or later
static gboolean show_wnck_version = FALSE;
static gboolean show_lua_version = FALSE;
static gchar *script_folder = NULL;
static gchar *temp_folder = NULL;
GFileMonitor *mon = NULL;
gchar *config_filename = NULL;
WnckHandle *my_wnck_handle = NULL;
/**
*
*/
static void load_list_of_scripts(WnckScreen *screen G_GNUC_UNUSED, WnckWindow *window,
GSList *file_list)
{
GSList *temp_file_list = file_list;
// set the window to work on
set_current_window(window);
// for every file in the folder - load the script
if (event_lists[W_OPEN] != NULL) {
while(temp_file_list) {
gchar *filename = (gchar*)temp_file_list->data;
// is it a Lua file?
if (g_str_has_suffix((gchar*)filename, ".lua")) {
// init the script, run it
if (!run_script(global_lua_state, filename))
/**/;
}
temp_file_list=temp_file_list->next;
}
}
return;
}
static void window_name_changed_cb(WnckWindow *window)
{
WnckScreen * screen = wnck_window_get_screen(window);
if(screen == NULL) return;
// Handle duplicate name-change events
// Simple method: just track the most recent event regardless of window
static WnckWindow *previous = NULL;
static char *prevname = NULL;
const char *newname = wnck_window_get_name(window);
if (window == previous && prevname && !strcmp (prevname, newname))
return;
// Store the info for the next event
free(prevname);
prevname = strdup(newname);
previous = window;
load_list_of_scripts(screen, window, event_lists[W_NAME_CHANGED]);
}
/**
*
*/
static void window_opened_cb(WnckScreen *screen, WnckWindow *window)
{
load_list_of_scripts(screen, window, event_lists[W_OPEN]);
/*
Attach a listener to each window for window-specific changes
Safe to do this way as long as the 'user data' parameter is NULL
*/
g_signal_connect(window, "name-changed", (GCallback)window_name_changed_cb, NULL);
}
/**
*
*/
static void window_closed_cb(WnckScreen *screen, WnckWindow *window)
{
load_list_of_scripts(screen, window, event_lists[W_CLOSE]);
}
/**
*
*/
static void window_changed_cb(WnckScreen *screen, WnckWindow *window)
{
WnckWindow *cur;
load_list_of_scripts(screen, window, event_lists[W_BLUR]);
cur = wnck_screen_get_active_window(screen);
load_list_of_scripts(screen, cur, event_lists[W_FOCUS]);
}
/**
*
*/
void init_screens()
{
int i;
int num_screens;
#ifndef GDK_VERSION_3_10
num_screens = gdk_display_get_n_screens(gdk_display_get_default());
#else
num_screens = 1;
#endif
for (i=0; i<num_screens; i++) {
WnckScreen *screen = wnck_handle_get_screen(my_wnck_handle, i);
g_signal_connect(screen, "window-opened",
(GCallback)window_opened_cb, NULL);
g_signal_connect(screen, "window-closed",
(GCallback)window_closed_cb, NULL);
g_signal_connect(screen, "active-window-changed",
(GCallback)window_changed_cb, NULL);
}
}
/**
* atexit handler - kill the script
*/
void devilspie_exit()
{
clear_file_lists();
g_free(temp_folder);
if (mon)
g_object_unref(mon);
g_free(config_filename);
}
/**
* handle signals that are sent to the application
*/
static void signal_handler(int sig)
{
printf("\n%s %d (%s)\n", _("Received signal:"), sig, strsignal(sig));
done_script_error_messages();
if (sig == SIGINT) {
exit(EXIT_FAILURE);
}
}
/**
*
*/
void print_list(GSList *list)
{
GSList *temp_list;
if (list != NULL) {
temp_list = list;
while(temp_list) {
gchar *file_name = temp_list->data;
if (file_name) {
if (g_str_has_suffix((gchar*)file_name, ".lua")) {
printf("%s\n", (gchar*)file_name);
}
}
temp_list = temp_list->next;
}
}
}
/**
*
*/
void print_script_lists()
{
gboolean have_any_files = FALSE;
win_event_type i;
if (debug)
printf("------------\n");
for (i = 0; i < W_NUM_EVENTS; i++) {
if (event_lists[i])
have_any_files = TRUE;
// If we are running debug mode - print the list of files:
if (debug) {
printf(_("List of Lua files handling \"%s\" events in folder:"),
event_names[i]);
printf("\n");
if (event_lists[i]) {
print_list(event_lists[i]);
}
}
}
if (!have_any_files) {
printf("%s\n\n", _("No script files found in the script folder - exiting."));
exit(EXIT_SUCCESS);
}
}
/**
*
*/
void folder_changed_callback(GFileMonitor *mon G_GNUC_UNUSED,
GFile *first_file,
GFile *second_file G_GNUC_UNUSED,
GFileMonitorEvent event,
gpointer user_data)
{
gchar *our_filename = (gchar*)(user_data);
// If a file is created or deleted, we need to check the file lists again
if ((event == G_FILE_MONITOR_EVENT_CREATED) ||
(event == G_FILE_MONITOR_EVENT_DELETED)) {
clear_file_lists();
set_current_window(NULL);
load_config(our_filename);
if (debug)
printf("Files in folder updated!\n - new lists:\n\n");
print_script_lists();
if (debug)
printf("-----------\n");
}
// Also monitor if our devilspie2.lua file is changed - since it handles
// which files are window close or window open scripts.
if (event == G_FILE_MONITOR_EVENT_CHANGED) {
if (first_file) {
gchar *short_filename = g_file_get_basename(first_file);
if (g_strcmp0(short_filename, "devilspie2.lua")==0) {
clear_file_lists();
set_current_window(NULL);
load_config(our_filename);
print_script_lists();
if (debug)
printf("----------");
}
}
}
}
/**
* Program main entry
*/
int main(int argc, char *argv[])
{
static const GOptionEntry options[]= {
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
N_("Print debug info to stdout"), NULL
},
{ "emulate", 'e', 0, G_OPTION_ARG_NONE, &emulate,
N_("Don't apply any rules, only emulate execution"), NULL
},
{ "folder", 'f', 0, G_OPTION_ARG_STRING, &script_folder,
N_("Search for scripts in this folder"), N_("FOLDER")
},
{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
N_("Show Devilspie2 version and quit"), NULL
},
// libwnck Version Information is only availible if you have
// libwnck 3.0 or later
{ "wnck-version", 'w', 0, G_OPTION_ARG_NONE, &show_wnck_version,
N_("Show libwnck version and quit"), NULL
},
{ "lua-version", 'l', 0, G_OPTION_ARG_NONE, &show_lua_version,
N_("Show Lua version and quit"), NULL
},
{ NULL }
};
GError *error = NULL;
GOptionContext *context;
// Init gettext stuff
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
bind_textdomain_codeset(PACKAGE, "");
textdomain(PACKAGE);
gchar *devilspie2_description = g_strdup_printf(_("apply rules on windows"));
gchar *full_desc_string = g_strdup_printf("- %s", devilspie2_description);
context = g_option_context_new(full_desc_string);
g_option_context_add_main_entries(context, options, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print(_("option parsing failed: %s"), error->message);
printf("\n");
exit(EXIT_FAILURE);
}
gdk_init(&argc, &argv);
g_free(full_desc_string);
g_free(devilspie2_description);
// if the folder is NULL, default to ~/.config/devilspie2/
if (script_folder == NULL) {
temp_folder = g_build_path(G_DIR_SEPARATOR_S,
g_get_user_config_dir(),
"devilspie2",
NULL);
// check if the folder does exist
if (!g_file_test(temp_folder, G_FILE_TEST_IS_DIR)) {
// - and if it doesn't, create it.
if (g_mkdir(temp_folder, 0700) != 0) {
printf("%s\n", _("Couldn't create the default folder for devilspie2 scripts."));
exit(EXIT_FAILURE);
}
}
script_folder = temp_folder;
}
gboolean shown = FALSE;
if (show_version) {
printf("Devilspie2 v%s\n", DEVILSPIE2_VERSION);
shown = TRUE;
}
// libwnck Version Information is only availible if you have
// libwnck 3.0 or later
if (show_wnck_version) {
#ifdef _DEBUG
printf("GTK v%d.%d.%d\n",
GTK_MAJOR_VERSION,
GTK_MINOR_VERSION,
GTK_MICRO_VERSION);
#endif
#ifdef HAVE_GTK3
printf("libwnck v%d.%d.%d\n",
WNCK_MAJOR_VERSION,
WNCK_MINOR_VERSION,
WNCK_MICRO_VERSION);
#else
printf("libwnck v2.x\n");
#endif
shown = TRUE;
}
if (show_lua_version) {
puts(LUA_VERSION);
shown = TRUE;
}
if (shown)
exit(0);
#if (GTK_MAJOR_VERSION >= 3)
if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
puts(_("An X11 display is required for devilspie2."));
if (getenv("WAYLAND_DISPLAY"))
puts(_("Wayland & XWayland are not supported.\nSee https://github.com/dsalt/devilspie2/issues/7"));
puts("");
return EXIT_FAILURE;
}
#endif
if (init_script_error_messages()!=0) {
printf("%s\n", _("Couldn't init script error messages!"));
exit(EXIT_FAILURE);
}
// set the current window to NULL, we don't need to be able to modify
// the windows when reading the config
set_current_window(NULL);
config_filename =
g_build_filename(script_folder, "devilspie2.lua", NULL);
if (load_config(config_filename) != 0) {
devilspie_exit();
return EXIT_FAILURE;
}
if (debug) {
if (emulate) {
printf("%s\n\n", _("Running devilspie2 in debug and emulate mode."));
} else {
printf("%s\n\n", _("Running devilspie2 in debug mode."));
}
printf(_("Using scripts from folder: %s"), script_folder);
printf("\n");
devilspie2_debug = TRUE;
}
// Should we only run an emulation (don't modify any windows)
if (emulate) devilspie2_emulate = emulate;
GFile *directory_file;
directory_file = g_file_new_for_path(script_folder);
// mon = g_file_monitor_directory(directory_file, G_FILE_MONITOR_WATCH_MOUNTS,
mon = g_file_monitor_directory(directory_file, G_FILE_MONITOR_NONE,
NULL, NULL);
if (!mon) {
printf("%s\n", _("Couldn't create directory monitor!"));
return EXIT_FAILURE;
}
g_signal_connect(mon, "changed", G_CALLBACK(folder_changed_callback),
(gpointer)(config_filename));
global_lua_state = init_script();
print_script_lists();
if (debug) printf("------------\n");
// remove stuff cleanly
atexit(devilspie_exit);
struct sigaction signal_action;
sigemptyset(&signal_action.sa_mask);
signal_action.sa_flags = 0;
signal_action.sa_handler = signal_handler;
if (sigaction(SIGINT, &signal_action, NULL) == -1) {
exit(EXIT_FAILURE);
}
my_wnck_handle = wnck_handle_new(WNCK_CLIENT_TYPE_PAGER);
init_screens();
loop=g_main_loop_new(NULL, TRUE);
g_main_loop_run(loop);
return EXIT_SUCCESS;
}

117
devilspie/error_strings.c Normal file
View File

@ -0,0 +1,117 @@
/**
* This file is part of devilspie2
* Copyright (C) 2012-2017 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/>.
*/
#include <glib.h>
#include <glib/gi18n.h>
#include <stdio.h>
#include "error_strings.h"
const int max_indata_expected;
gchar *num_indata_expected_errors[] = {NULL, NULL, NULL, NULL, NULL};
gchar *n_or_m_indata_expected_error = NULL;
gchar *n_to_m_indata_expected_error = NULL;
gchar *at_least_four_indata_expected_error = NULL;
gchar *number_expected_as_indata_error = NULL;
gchar *boolean_expected_as_indata_error = NULL;
gchar *string_expected_as_indata_error = NULL;
gchar *number_or_string_expected_as_indata_error = NULL;
gchar *number_or_string_or_boolean_expected_as_indata_error = NULL;
gchar *integer_greater_than_zero_expected_error = NULL;
gchar *could_not_find_current_viewport_error = NULL;
gchar *setting_viewport_failed_error = NULL;
gchar *failed_string = NULL;
/**
*
*/
#define ALLOCATE_ERROR_STRING _("Couldn't allocate error string!")
#define INIT_ERRMSG(errvar, errtxt) \
{ \
errvar = g_strdup(errtxt); \
if (!errvar) { \
printf("%s: \"%s\"\n", ALLOCATE_ERROR_STRING, errtxt); \
return -1; \
} \
}
int init_script_error_messages()
{
INIT_ERRMSG(num_indata_expected_errors[0], _("No parameters expected"));
INIT_ERRMSG(num_indata_expected_errors[1], _("One parameter expected"));
INIT_ERRMSG(num_indata_expected_errors[2], _("Two parameters expected"));
INIT_ERRMSG(num_indata_expected_errors[3], _("Three parameters expected"));
INIT_ERRMSG(num_indata_expected_errors[4], _("Four parameters expected"));
INIT_ERRMSG(n_or_m_indata_expected_error, _("%d or %d parameters expected"));
INIT_ERRMSG(n_to_m_indata_expected_error, _("%d to %d parameters expected"));
INIT_ERRMSG(at_least_four_indata_expected_error, _("At least four parameters expected"));
INIT_ERRMSG(number_expected_as_indata_error, _("Number expected as parameter"));
INIT_ERRMSG(boolean_expected_as_indata_error, _("Boolean expected as parameter"));
INIT_ERRMSG(string_expected_as_indata_error, _("String expected as parameter"));
INIT_ERRMSG(number_or_string_expected_as_indata_error, _("Number or string expected as parameter"));
INIT_ERRMSG(number_or_string_or_boolean_expected_as_indata_error, _("Number or string or boolean expected as parameter"));
INIT_ERRMSG(integer_greater_than_zero_expected_error, _("Integer greater than zero expected"));
INIT_ERRMSG(could_not_find_current_viewport_error, _("Could not find current viewport"));
INIT_ERRMSG(setting_viewport_failed_error, _("Setting viewport failed"));
INIT_ERRMSG(failed_string, _("Failed!"));
return 0;
}
/**
*
*/
void done_script_error_messages()
{
for (int i = 0; i <= max_indata_expected; i++) {
g_free(num_indata_expected_errors[i]);
}
g_free(n_or_m_indata_expected_error);
g_free(n_to_m_indata_expected_error);
g_free(at_least_four_indata_expected_error);
g_free(number_expected_as_indata_error);
g_free(boolean_expected_as_indata_error);
g_free(string_expected_as_indata_error);
g_free(number_or_string_expected_as_indata_error);
g_free(number_or_string_or_boolean_expected_as_indata_error);
g_free(integer_greater_than_zero_expected_error);
g_free(could_not_find_current_viewport_error);
g_free(setting_viewport_failed_error);
g_free(failed_string);
}

55
devilspie/error_strings.h Normal file
View File

@ -0,0 +1,55 @@
/**
* This file is part of devilspie2
* Copyright (C) 2012-2017 Andreas Rönnquist
*
* 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/>.
*/
#ifndef __HEADER_ERROR_STRINGS_
#define __HEADER_ERROR_STRINGS_
/**
*
*/
extern const int max_indata_expected;
extern gchar *num_indata_expected_errors[];
extern gchar *n_or_m_indata_expected_error;
extern gchar *n_to_m_indata_expected_error;
extern gchar *at_least_four_indata_expected_error;
extern gchar *number_expected_as_indata_error;
extern gchar *boolean_expected_as_indata_error;
extern gchar *string_expected_as_indata_error;
extern gchar *number_or_string_expected_as_indata_error;
extern gchar *number_or_string_or_boolean_expected_as_indata_error;
extern gchar *integer_greater_than_zero_expected_error;
extern gchar *could_not_find_current_viewport_error;
extern gchar *setting_viewport_failed_error;
extern gchar *failed_string;
/**
*
*/
int init_script_error_messages();
void done_script_error_messages();
#endif /*__HEADER_ERROR_STRINGS_*/

28
devilspie/intl.h Normal file
View File

@ -0,0 +1,28 @@
/**
* This file is part of devilspie2
* Copyright (C) 2001 Havoc Pennington, 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/>.
*/
#ifndef __HEADER_INTL__
#define __HEADER_INTL__
#include <libintl.h>
#define _(String) gettext (String)
#define gettext_noop(String) String
#define N_(String) gettext_noop (String)
#endif

352
devilspie/script.c Normal file
View File

@ -0,0 +1,352 @@
/**
* 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;
}

42
devilspie/script.h Normal file
View File

@ -0,0 +1,42 @@
/**
* This file is part of devilspie2
* Copyright (C) 2011-2019 Andreas Rönnquist
*
* 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/>.
*/
#ifndef __HEADER_SCRIPT_
#define __HEADER_SCRIPT_
/**
*
*/
/**
*
*/
lua_State *init_script();
void register_cfunctions(lua_State *lua);
int run_script(lua_State *lua, const char *filename);
void done_script(lua_State *lua);
extern gboolean devilspie2_debug;
extern gboolean devilspie2_emulate;
extern lua_State *global_lua_state;
#endif /*__HEADER_SCRIPT_*/

2659
devilspie/script_functions.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/**
* This file is part of devilspie2
* Copyright (C) 2011-2019 Andreas Rönnquist
*
* 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/>.
*/
#ifndef __HEADER_SCRIPT_FUNCTIONS_
#define __HEADER_SCRIPT_FUNCTIONS_
/**
*
*/
#include "lua.h"
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include "libwnck/libwnck.h"
int c_use_utf8(lua_State *lua);
int c_get_window_name(lua_State *lua);
int c_get_window_has_name(lua_State *lua);