summaryrefslogtreecommitdiff
path: root/devilspie/devilspie2.c
diff options
context:
space:
mode:
Diffstat (limited to 'devilspie/devilspie2.c')
-rw-r--r--devilspie/devilspie2.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/devilspie/devilspie2.c b/devilspie/devilspie2.c
new file mode 100644
index 0000000..cf9c994
--- /dev/null
+++ b/devilspie/devilspie2.c
@@ -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;
+}