518 lines
12 KiB
C
518 lines
12 KiB
C
/**
|
|
* 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;
|
|
}
|