day 1
This commit is contained in:
commit
cfd6fdadd7
|
@ -0,0 +1,7 @@
|
||||||
|
*.swp
|
||||||
|
go.mod
|
||||||
|
go.sum
|
||||||
|
|
||||||
|
files/
|
||||||
|
|
||||||
|
xstartplacement
|
|
@ -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*
|
|
@ -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_ */
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_*/
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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_*/
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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_*/
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
|
||||||
|
int c_set_window_position(lua_State *lua);
|
||||||
|
int c_set_window_position2(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_geometry(lua_State *lua);
|
||||||
|
int c_set_window_geometry2(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_size(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_strut(lua_State *lua);
|
||||||
|
int c_get_window_strut(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_application_name(lua_State *lua);
|
||||||
|
|
||||||
|
int c_debug_print(lua_State *lua);
|
||||||
|
|
||||||
|
int c_shade(lua_State *lua);
|
||||||
|
int c_unshade(lua_State *lua);
|
||||||
|
|
||||||
|
int c_minimize(lua_State *lua);
|
||||||
|
int c_unminimize(lua_State *lua);
|
||||||
|
|
||||||
|
int c_decorate_window(lua_State *lua);
|
||||||
|
int c_undecorate_window(lua_State *lua);
|
||||||
|
int c_get_window_is_decorated(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_workspace(lua_State *lua);
|
||||||
|
int c_change_workspace(lua_State *lua);
|
||||||
|
int c_get_workspace_count(lua_State *lua);
|
||||||
|
|
||||||
|
int c_unmaximize(lua_State *lua);
|
||||||
|
int c_maximize(lua_State *lua);
|
||||||
|
int c_maximize_vertically(lua_State *lua);
|
||||||
|
int c_maximize_horisontally(lua_State *lua); // deprecated
|
||||||
|
int c_maximize_horizontally(lua_State *lua);
|
||||||
|
|
||||||
|
int c_pin_window(lua_State *lua);
|
||||||
|
int c_unpin_window(lua_State *lua);
|
||||||
|
int c_stick_window(lua_State *lua);
|
||||||
|
int c_unstick_window(lua_State *lua);
|
||||||
|
|
||||||
|
int c_close_window(lua_State *lua);
|
||||||
|
|
||||||
|
void set_current_window(WnckWindow *window);
|
||||||
|
WnckWindow *get_current_window();
|
||||||
|
|
||||||
|
int c_set_adjust_for_decoration(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_geometry(lua_State *lua);
|
||||||
|
int c_get_window_client_geometry(lua_State *lua);
|
||||||
|
int c_get_window_frame_extents(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_skip_tasklist(lua_State *lua);
|
||||||
|
int c_set_skip_pager(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_is_maximized(lua_State *lua);
|
||||||
|
int c_get_window_is_maximized_vertically(lua_State *lua);
|
||||||
|
int c_get_window_is_maximized_horisontally(lua_State *lua); // deprecated
|
||||||
|
int c_get_window_is_maximized_horizontally(lua_State *lua);
|
||||||
|
int c_get_window_is_pinned(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_fullscreen(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_above(lua_State *lua);
|
||||||
|
int c_set_window_below(lua_State *lua);
|
||||||
|
|
||||||
|
int c_make_always_on_top(lua_State *lua);
|
||||||
|
int c_set_on_top(lua_State *lua);
|
||||||
|
int c_set_on_bottom(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_type(lua_State *lua);
|
||||||
|
|
||||||
|
// these two require GTK 3 or later
|
||||||
|
int c_get_class_instance_name(lua_State *lua);
|
||||||
|
int c_get_class_group_name(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_property(lua_State *lua);
|
||||||
|
int c_window_property_is_utf8(lua_State *lua);
|
||||||
|
int c_get_window_property_full(lua_State *lua);
|
||||||
|
int c_get_window_role(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_xid(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_class(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_property(lua_State *lua);
|
||||||
|
int c_delete_window_property(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_viewport(lua_State *lua);
|
||||||
|
|
||||||
|
int c_center(lua_State *lua);
|
||||||
|
|
||||||
|
int c_set_window_opacity(lua_State *lua);
|
||||||
|
int c_set_window_type(lua_State *lua);
|
||||||
|
|
||||||
|
|
||||||
|
int c_get_screen_geometry(lua_State *lua);
|
||||||
|
|
||||||
|
int c_focus(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_window_fullscreen(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_monitor_index(lua_State *lua);
|
||||||
|
int c_get_monitor_geometry(lua_State *lua);
|
||||||
|
|
||||||
|
int c_xy(lua_State *lua);
|
||||||
|
int c_xywh(lua_State *lua);
|
||||||
|
|
||||||
|
int c_on_geometry_changed(lua_State *lua);
|
||||||
|
|
||||||
|
int c_get_process_name(lua_State *lua);
|
||||||
|
|
||||||
|
int c_millisleep(lua_State *lua);
|
||||||
|
|
||||||
|
#endif /*__HEADER_SCRIPT_FUNCTIONS_*/
|
|
@ -0,0 +1,779 @@
|
||||||
|
/**
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// FIXME: retrieve screen position via wnck
|
||||||
|
#include <X11/extensions/Xinerama.h>
|
||||||
|
|
||||||
|
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
|
||||||
|
#include <libwnck/libwnck.h>
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#include "intl.h"
|
||||||
|
#include "xutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if (GTK_MAJOR_VERSION >= 3)
|
||||||
|
#define HAVE_GTK3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static GHashTable *atom_hash = NULL;
|
||||||
|
static GHashTable *reverse_atom_hash = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Atom my_wnck_atom_get(const char *atom_name)
|
||||||
|
{
|
||||||
|
Atom retval;
|
||||||
|
|
||||||
|
g_return_val_if_fail (atom_name != NULL, None);
|
||||||
|
|
||||||
|
if (!atom_hash) {
|
||||||
|
atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
|
reverse_atom_hash = g_hash_table_new (NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = GPOINTER_TO_UINT (g_hash_table_lookup (atom_hash, atom_name));
|
||||||
|
if (!retval) {
|
||||||
|
retval = XInternAtom (gdk_x11_get_default_xdisplay(), atom_name, FALSE);
|
||||||
|
|
||||||
|
if (retval != None) {
|
||||||
|
char *name_copy;
|
||||||
|
|
||||||
|
name_copy = g_strdup (atom_name);
|
||||||
|
|
||||||
|
g_hash_table_insert (atom_hash,
|
||||||
|
name_copy,
|
||||||
|
GUINT_TO_POINTER (retval));
|
||||||
|
g_hash_table_insert (reverse_atom_hash,
|
||||||
|
GUINT_TO_POINTER (retval),
|
||||||
|
name_copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void devilspie2_change_state(Screen *screen, Window xwindow,
|
||||||
|
gboolean add,
|
||||||
|
Atom state1,
|
||||||
|
Atom state2)
|
||||||
|
{
|
||||||
|
XEvent xev;
|
||||||
|
|
||||||
|
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
|
||||||
|
#define _NET_WM_STATE_ADD 1 /* add/set property */
|
||||||
|
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
|
||||||
|
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.serial = 0;
|
||||||
|
xev.xclient.send_event = True;
|
||||||
|
xev.xclient.display = gdk_x11_get_default_xdisplay();
|
||||||
|
xev.xclient.window = xwindow;
|
||||||
|
xev.xclient.message_type = my_wnck_atom_get ("_NET_WM_STATE");
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
||||||
|
xev.xclient.data.l[1] = state1;
|
||||||
|
xev.xclient.data.l[2] = state2;
|
||||||
|
|
||||||
|
XSendEvent (gdk_x11_get_default_xdisplay(),
|
||||||
|
RootWindowOfScreen (screen),
|
||||||
|
False,
|
||||||
|
SubstructureRedirectMask | SubstructureNotifyMask,
|
||||||
|
&xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void devilspie2_error_trap_push()
|
||||||
|
{
|
||||||
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
||||||
|
gdk_x11_display_error_trap_push(gdk_display_get_default());
|
||||||
|
#else
|
||||||
|
gdk_error_trap_push();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int devilspie2_error_trap_pop()
|
||||||
|
{
|
||||||
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
||||||
|
return gdk_x11_display_error_trap_pop(gdk_display_get_default());
|
||||||
|
#else
|
||||||
|
XSync(gdk_x11_get_default_xdisplay(),False);
|
||||||
|
return gdk_error_trap_pop();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void set_decorations(Window xid /*WnckWindow *window*/, gboolean decorate)
|
||||||
|
{
|
||||||
|
#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
|
||||||
|
#define MWM_HINTS_DECORATIONS (1L << 1)
|
||||||
|
struct {
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned long functions;
|
||||||
|
unsigned long decorations;
|
||||||
|
long inputMode;
|
||||||
|
unsigned long status;
|
||||||
|
} hints = {0,};
|
||||||
|
|
||||||
|
hints.flags = MWM_HINTS_DECORATIONS;
|
||||||
|
hints.decorations = decorate ? 1 : 0;
|
||||||
|
|
||||||
|
/* Set Motif hints, most window managers handle these */
|
||||||
|
XChangeProperty(gdk_x11_get_default_xdisplay(), xid /*wnck_window_get_xid (window)*/,
|
||||||
|
my_wnck_atom_get ("_MOTIF_WM_HINTS"),
|
||||||
|
my_wnck_atom_get ("_MOTIF_WM_HINTS"), 32, PropModeReplace,
|
||||||
|
(unsigned char *)&hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
|
||||||
|
|
||||||
|
|
||||||
|
//Window xid;
|
||||||
|
XWindowAttributes attrs;
|
||||||
|
|
||||||
|
//xid = wnck_window_get_xid (window);
|
||||||
|
XGetWindowAttributes(gdk_x11_get_default_xdisplay(), xid, &attrs);
|
||||||
|
|
||||||
|
/* Apart from OpenBox, which doesn't respect it changing after mapping.
|
||||||
|
* Instead it has this workaround.
|
||||||
|
*/
|
||||||
|
devilspie2_change_state (attrs.screen,
|
||||||
|
xid /*wnck_window_get_xid(window)*/, !decorate,
|
||||||
|
my_wnck_atom_get ("_OB_WM_STATE_UNDECORATED"), 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
gboolean decorate_window(Window xid)
|
||||||
|
{
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
|
||||||
|
set_decorations(xid, TRUE);
|
||||||
|
|
||||||
|
if (devilspie2_error_trap_pop()) {
|
||||||
|
g_printerr("decorate_window %s\n", _("Failed!"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
gboolean undecorate_window(Window xid)
|
||||||
|
{
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
|
||||||
|
set_decorations(xid, FALSE);
|
||||||
|
|
||||||
|
if (devilspie2_error_trap_pop()) {
|
||||||
|
g_printerr("decorate_window %s\n", _("Failed!"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
gboolean get_decorated(Window xid /*WnckWindow *window*/)
|
||||||
|
{
|
||||||
|
Display *disp = gdk_x11_get_default_xdisplay();
|
||||||
|
Atom type_ret;
|
||||||
|
Atom hints_atom = XInternAtom(disp, "_MOTIF_WM_HINTS", False);
|
||||||
|
int format_ret;
|
||||||
|
int err, result = 0;
|
||||||
|
unsigned long nitems_ret, bytes_after_ret, *prop_ret;
|
||||||
|
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
XGetWindowProperty(disp, xid, hints_atom, 0,
|
||||||
|
PROP_MOTIF_WM_HINTS_ELEMENTS, 0, hints_atom,
|
||||||
|
&type_ret, &format_ret, &nitems_ret,
|
||||||
|
&bytes_after_ret, (unsigned char **)&prop_ret);
|
||||||
|
|
||||||
|
err = devilspie2_error_trap_pop ();
|
||||||
|
if (err != Success || result != Success)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return type_ret != hints_atom || nitems_ret < 3 || prop_ret[2] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Screen *devilspie2_window_get_xscreen(Window xid)
|
||||||
|
{
|
||||||
|
XWindowAttributes attrs;
|
||||||
|
|
||||||
|
XGetWindowAttributes(gdk_x11_get_default_xdisplay(), xid, &attrs);
|
||||||
|
|
||||||
|
return attrs.screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char* my_wnck_get_string_property(Window xwindow, Atom atom, gboolean *utf8)
|
||||||
|
{
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
gulong nitems;
|
||||||
|
gulong bytes_after;
|
||||||
|
unsigned char *property;
|
||||||
|
int err, result;
|
||||||
|
char *retval;
|
||||||
|
Atom XA_UTF8_STRING;
|
||||||
|
gboolean is_utf8 = True;
|
||||||
|
|
||||||
|
if (utf8)
|
||||||
|
*utf8 = False;
|
||||||
|
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
property = NULL;
|
||||||
|
result = XGetWindowProperty (gdk_x11_get_default_xdisplay (),
|
||||||
|
xwindow, atom,
|
||||||
|
0, G_MAXLONG,
|
||||||
|
False, AnyPropertyType, &type,
|
||||||
|
&format, &nitems,
|
||||||
|
&bytes_after, &property);
|
||||||
|
|
||||||
|
err = devilspie2_error_trap_pop ();
|
||||||
|
if (err != Success || result != Success)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
retval = NULL;
|
||||||
|
XA_UTF8_STRING = XInternAtom(gdk_x11_get_default_xdisplay(), "UTF8_STRING", False);
|
||||||
|
|
||||||
|
if (utf8)
|
||||||
|
*utf8 = False;
|
||||||
|
|
||||||
|
if (type == XA_STRING) {
|
||||||
|
is_utf8 = False;
|
||||||
|
retval = g_strdup ((char*)property);
|
||||||
|
} else if (type == XA_UTF8_STRING) {
|
||||||
|
retval = g_strdup ((char*)property);
|
||||||
|
} else if (type == XA_ATOM && nitems > 0 && format == 32) {
|
||||||
|
long *pp;
|
||||||
|
|
||||||
|
pp = (long *)property; // we can assume (long *) since format == 32
|
||||||
|
if (nitems == 1) {
|
||||||
|
char* prop_name;
|
||||||
|
prop_name = XGetAtomName (gdk_x11_get_default_xdisplay (), *pp);
|
||||||
|
if (prop_name) {
|
||||||
|
retval = g_strdup (prop_name);
|
||||||
|
XFree (prop_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gulong i;
|
||||||
|
char** prop_names;
|
||||||
|
|
||||||
|
prop_names = g_new (char *, nitems + 1);
|
||||||
|
prop_names[nitems] = NULL;
|
||||||
|
for (i=0; i < nitems; i++) {
|
||||||
|
prop_names[i] = XGetAtomName (gdk_x11_get_default_xdisplay (),
|
||||||
|
*pp++);
|
||||||
|
}
|
||||||
|
retval = g_strjoinv (", ", prop_names);
|
||||||
|
for (i=0; i < nitems; i++) {
|
||||||
|
if (prop_names[i]) XFree (prop_names[i]);
|
||||||
|
}
|
||||||
|
g_free (prop_names);
|
||||||
|
}
|
||||||
|
} else if (type == XA_CARDINAL && nitems == 1) {
|
||||||
|
switch(format) {
|
||||||
|
case 32:
|
||||||
|
retval = g_strdup_printf("%lu", *(unsigned long*)property);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
retval = g_strdup_printf("%u", *(unsigned int*)property);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
retval = g_strdup_printf("%c", *(unsigned char*)property);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (type == XA_WINDOW && nitems == 1) {
|
||||||
|
/* unsinged long is the same format used for XID by libwnck:
|
||||||
|
* https://git.gnome.org/browse/libwnck/tree/libwnck/window.c?h=3.14.0#n763
|
||||||
|
*/
|
||||||
|
retval = g_strdup_printf("%lu", (gulong) *(Window *)property);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree (property);
|
||||||
|
if (utf8)
|
||||||
|
*utf8 = is_utf8;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void my_wnck_set_string_property(Window xwindow, Atom atom, const gchar *const string, gboolean utf8)
|
||||||
|
{
|
||||||
|
const unsigned char *const str = (const unsigned char *)string;
|
||||||
|
Display *display = gdk_x11_get_default_xdisplay();
|
||||||
|
Atom type = utf8 ? XInternAtom(display, "UTF8_STRING", False) : XA_STRING;
|
||||||
|
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
XChangeProperty (display, xwindow, atom, type, 8, PropModeReplace, str, strlen(string));
|
||||||
|
devilspie2_error_trap_pop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void my_wnck_set_cardinal_property(Window xwindow, Atom atom, int32_t value)
|
||||||
|
{
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
XChangeProperty (gdk_x11_get_default_xdisplay (),
|
||||||
|
xwindow, atom, XA_CARDINAL, 32,
|
||||||
|
PropModeReplace, (unsigned char *)&value, 1);
|
||||||
|
devilspie2_error_trap_pop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void my_wnck_delete_property(Window xwindow, Atom atom)
|
||||||
|
{
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
XDeleteProperty (gdk_x11_get_default_xdisplay (), xwindow, atom);
|
||||||
|
devilspie2_error_trap_pop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
my_wnck_get_cardinal_list (Window xwindow, Atom atom,
|
||||||
|
gulong **cardinals, int *len)
|
||||||
|
{
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
gulong nitems;
|
||||||
|
gulong bytes_after;
|
||||||
|
gulong *nums;
|
||||||
|
int err, result;
|
||||||
|
|
||||||
|
*cardinals = NULL;
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
devilspie2_error_trap_push();
|
||||||
|
type = None;
|
||||||
|
result = XGetWindowProperty(gdk_x11_get_default_xdisplay (),
|
||||||
|
xwindow,
|
||||||
|
atom,
|
||||||
|
0, G_MAXLONG,
|
||||||
|
False, XA_CARDINAL, &type, &format, &nitems,
|
||||||
|
&bytes_after, (void*)&nums);
|
||||||
|
|
||||||
|
err = devilspie2_error_trap_pop();
|
||||||
|
|
||||||
|
if ((err != Success) || (result != Success))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (type != XA_CARDINAL) {
|
||||||
|
XFree (nums);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cardinals = g_new(gulong, nitems);
|
||||||
|
memcpy(*cardinals, nums, sizeof (gulong) * nitems);
|
||||||
|
*len = nitems;
|
||||||
|
|
||||||
|
XFree(nums);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get viewport start coordinates to the x and y integers,
|
||||||
|
* returns 0 on success and non-zero on error.
|
||||||
|
*/
|
||||||
|
int devilspie2_get_viewport_start(Window xid, int *x, int *y)
|
||||||
|
{
|
||||||
|
gulong *list;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
int result = -1;
|
||||||
|
|
||||||
|
my_wnck_get_cardinal_list(RootWindowOfScreen(devilspie2_window_get_xscreen(xid)),
|
||||||
|
my_wnck_atom_get("_NET_DESKTOP_VIEWPORT"),
|
||||||
|
&list, &len);
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
*x = list[0];
|
||||||
|
*y = list[1];
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(list);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void my_window_set_window_type(Window xid, gchar *window_type)
|
||||||
|
{
|
||||||
|
Display *display = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
|
Atom atoms[10];
|
||||||
|
|
||||||
|
/*
|
||||||
|
_NET_WM_WINDOW_TYPE_DESKTOP, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_DOCK, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_TOOLBAR, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_MENU, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_UTILITY, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_SPLASH, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_DIALOG, ATOM
|
||||||
|
_NET_WM_WINDOW_TYPE_NORMAL, ATOM
|
||||||
|
*/
|
||||||
|
|
||||||
|
gchar *type = NULL;
|
||||||
|
|
||||||
|
// Make it a recognized _NET_WM_TYPE
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_DESKTOP") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_DESKTOP");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_DOCK") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_DOCK");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_TOOLBAR") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_TOOLBAR");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_MENU") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_MENU");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_UTILITY") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_UTILITY");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_SPLASH") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_SPLASH");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_DIALOG") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_DIALOG");
|
||||||
|
|
||||||
|
} else if (g_ascii_strcasecmp(window_type, "WINDOW_TYPE_NORMAL") == 0) {
|
||||||
|
type = g_strdup("_NET_WM_WINDOW_TYPE_NORMAL");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
type = g_strdup(window_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
atoms[0] = XInternAtom(display, type, False);
|
||||||
|
|
||||||
|
XChangeProperty(gdk_x11_get_default_xdisplay(), xid,
|
||||||
|
XInternAtom(display, "_NET_WM_WINDOW_TYPE", False), XA_ATOM, 32,
|
||||||
|
PropModeReplace, (unsigned char *) &atoms, 1);
|
||||||
|
|
||||||
|
g_free(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void my_window_set_opacity(Window xid, double value)
|
||||||
|
{
|
||||||
|
Display *display = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
|
unsigned int opacity = (uint)(0xffffffff * value);
|
||||||
|
Atom atom_net_wm_opacity = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False);
|
||||||
|
|
||||||
|
|
||||||
|
XChangeProperty(gdk_x11_get_default_xdisplay(), xid,
|
||||||
|
atom_net_wm_opacity, XA_CARDINAL, 32,
|
||||||
|
PropModeReplace, (unsigned char *) &opacity, 1L);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void adjust_for_decoration(WnckWindow *window, int *x, int *y, int *w, int *h)
|
||||||
|
{
|
||||||
|
GdkRectangle geom, geom_undec;
|
||||||
|
|
||||||
|
wnck_window_get_geometry(window, &geom.x, &geom.y, &geom.width, &geom.height);
|
||||||
|
wnck_window_get_client_window_geometry(window, &geom_undec.x, &geom_undec.y, &geom_undec.width, &geom_undec.height);
|
||||||
|
|
||||||
|
if (x) *x -= geom_undec.x - geom.x;
|
||||||
|
if (y) *y -= geom_undec.y - geom.y;
|
||||||
|
if (w) *w -= geom_undec.width - geom.width;
|
||||||
|
if (h) *h -= geom_undec.height - geom.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void set_window_geometry(WnckWindow *window, int x, int y, int w, int h, gboolean adjusting_for_decoration)
|
||||||
|
{
|
||||||
|
if (window) {
|
||||||
|
WnckScreen *screen = wnck_window_get_screen(window);
|
||||||
|
int sw = wnck_screen_get_width(screen);
|
||||||
|
int sh = wnck_screen_get_height(screen);
|
||||||
|
|
||||||
|
int gravity = WNCK_WINDOW_GRAVITY_CURRENT;
|
||||||
|
if (x >= 0 && y >= 0)
|
||||||
|
gravity = WNCK_WINDOW_GRAVITY_NORTHWEST;
|
||||||
|
if (x >= 0 && y < 0)
|
||||||
|
gravity = WNCK_WINDOW_GRAVITY_SOUTHWEST;
|
||||||
|
if (x < 0 && y >= 0)
|
||||||
|
gravity = WNCK_WINDOW_GRAVITY_NORTHEAST;
|
||||||
|
if (x < 0 && y < 0)
|
||||||
|
gravity = WNCK_WINDOW_GRAVITY_SOUTHEAST;
|
||||||
|
if (x < 0)
|
||||||
|
x = sw + x;
|
||||||
|
if (y < 0)
|
||||||
|
y = sh + y;
|
||||||
|
|
||||||
|
if (adjusting_for_decoration)
|
||||||
|
adjust_for_decoration(window, &x, &y, &w, &h);
|
||||||
|
|
||||||
|
wnck_window_set_geometry(window,
|
||||||
|
gravity,
|
||||||
|
WNCK_WINDOW_CHANGE_X +
|
||||||
|
WNCK_WINDOW_CHANGE_Y +
|
||||||
|
WNCK_WINDOW_CHANGE_WIDTH +
|
||||||
|
WNCK_WINDOW_CHANGE_HEIGHT,
|
||||||
|
x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int get_monitor_count(void)
|
||||||
|
{
|
||||||
|
// FIXME: retrieve monitor count via wnck
|
||||||
|
// For now, use Xinerama directly
|
||||||
|
Display *dpy = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
|
if (!XineramaIsActive(dpy))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Normally, we'd use the return value, but we only want the number of entries
|
||||||
|
int monitor_count = 0;
|
||||||
|
XineramaQueryScreens(dpy, &monitor_count);
|
||||||
|
|
||||||
|
return monitor_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int get_monitor_index_geometry(WnckWindow *window, const GdkRectangle *window_r_in, GdkRectangle *monitor_r)
|
||||||
|
{
|
||||||
|
// monitor_r is always filled in unless the return value is -1
|
||||||
|
|
||||||
|
// FIXME: retrieve monitor info via wnck
|
||||||
|
// For now, use Xinerama directly
|
||||||
|
int id = -1;
|
||||||
|
int monitor_count = 0;
|
||||||
|
XineramaScreenInfo *monitor_list = NULL;
|
||||||
|
Display *dpy = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
|
if (XineramaIsActive(dpy))
|
||||||
|
monitor_list = XineramaQueryScreens(dpy, &monitor_count);
|
||||||
|
|
||||||
|
// bail out if no Xinermama or no monitors
|
||||||
|
if (!monitor_list || !monitor_count)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// find which monitor the window's centre is on
|
||||||
|
GdkRectangle window_r;
|
||||||
|
if (window)
|
||||||
|
wnck_window_get_geometry(window, &window_r.x, &window_r.y, &window_r.width, &window_r.height);
|
||||||
|
else
|
||||||
|
window_r = *window_r_in;
|
||||||
|
|
||||||
|
GdkPoint centre = { window_r.x + window_r.width / 2, window_r.y + window_r.height / 2 };
|
||||||
|
|
||||||
|
for (int i = 0; i < monitor_count; ++i) {
|
||||||
|
if (centre.x >= monitor_list[i].x_org &&
|
||||||
|
centre.x < monitor_list[i].x_org + monitor_list[i].width &&
|
||||||
|
centre.y >= monitor_list[i].y_org &&
|
||||||
|
centre.y < monitor_list[i].y_org + monitor_list[i].height) {
|
||||||
|
id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if that fails, try intersection of rectangles
|
||||||
|
// just use the first matching
|
||||||
|
// FIXME?: should find whichever shows most of the window (if tied, closest to window centre)
|
||||||
|
if (id < 0) {
|
||||||
|
for (int i = 0; i < monitor_count; ++i) {
|
||||||
|
GdkRectangle r = {
|
||||||
|
monitor_list[i].x_org, monitor_list[i].y_org,
|
||||||
|
monitor_list[i].x_org + monitor_list[i].width,
|
||||||
|
monitor_list[i].y_org + monitor_list[i].height
|
||||||
|
};
|
||||||
|
if (gdk_rectangle_intersect(&window_r, &r, NULL)) {
|
||||||
|
id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and if that too fails, use the default
|
||||||
|
if (id < 0)
|
||||||
|
id = 0; // FIXME: primary monitor
|
||||||
|
|
||||||
|
if (monitor_r) {
|
||||||
|
monitor_r->x = monitor_list[id].x_org;
|
||||||
|
monitor_r->y = monitor_list[id].y_org;
|
||||||
|
monitor_r->width = monitor_list[id].width;
|
||||||
|
monitor_r->height = monitor_list[id].height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int get_monitor_geometry(int index, GdkRectangle *monitor_r)
|
||||||
|
{
|
||||||
|
// if out of range, output is for monitor 0 (if present) else this:
|
||||||
|
*monitor_r = (GdkRectangle){ 0, 0, 640, 480 };
|
||||||
|
|
||||||
|
// FIXME: retrieve monitor info via wnck
|
||||||
|
// For now, use Xinerama directly
|
||||||
|
int monitor_count = 0;
|
||||||
|
XineramaScreenInfo *monitor_list = NULL;
|
||||||
|
Display *dpy = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
|
if (XineramaIsActive(dpy))
|
||||||
|
monitor_list = XineramaQueryScreens(dpy, &monitor_count);
|
||||||
|
|
||||||
|
// bail out if no Xinermama or no monitors
|
||||||
|
if (!monitor_list || !monitor_count)
|
||||||
|
return -1; // no xinerama!
|
||||||
|
|
||||||
|
// FIXME: default to primary monitor
|
||||||
|
if (index < 0 || index >= monitor_count)
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
monitor_r->x = monitor_list[index].x_org;
|
||||||
|
monitor_r->y = monitor_list[index].y_org;
|
||||||
|
monitor_r->width = monitor_list[index].width;
|
||||||
|
monitor_r->height = monitor_list[index].height;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int get_window_workspace_geometry(WnckWindow *window, GdkRectangle *geom)
|
||||||
|
{
|
||||||
|
WnckScreen *screen = wnck_window_get_screen(window);
|
||||||
|
WnckWorkspace *workspace = wnck_screen_get_active_workspace(screen);
|
||||||
|
|
||||||
|
if (workspace == NULL) {
|
||||||
|
workspace = wnck_screen_get_workspace(screen, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace == NULL) {
|
||||||
|
g_printerr(_("Could not get workspace"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
geom->x = 0;
|
||||||
|
geom->y = 0;
|
||||||
|
geom->width = wnck_workspace_get_width(workspace);
|
||||||
|
geom->height = wnck_workspace_get_height(workspace);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for the above geometry-reading functions
|
||||||
|
* Selects according to monitor number
|
||||||
|
* Returns the monitor index, MONITOR_ALL or, on error, MONITOR_NONE
|
||||||
|
*/
|
||||||
|
int get_monitor_or_workspace_geometry(int monitor_no, WnckWindow *window, GdkRectangle *bounds)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (monitor_no)
|
||||||
|
{
|
||||||
|
case MONITOR_ALL:
|
||||||
|
return get_window_workspace_geometry(window, bounds) ? MONITOR_NONE : MONITOR_ALL;
|
||||||
|
|
||||||
|
case MONITOR_WINDOW:
|
||||||
|
ret = get_monitor_index_geometry(window, NULL, bounds);
|
||||||
|
return ret < 0 ? MONITOR_NONE : ret;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (monitor_no < 0 || monitor_no >= get_monitor_count())
|
||||||
|
return MONITOR_NONE;
|
||||||
|
return get_monitor_geometry(monitor_no, bounds) < 0 ? MONITOR_NONE : monitor_no;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* This file is part of devilspie2
|
||||||
|
* Copyright (C) 2001 Havoc Pennington, 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_XUTILS_
|
||||||
|
#define __HEADER_XUTILS_
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
/* Special values for specifying which monitor.
|
||||||
|
* These values are relied upon; changing them will require changing the code where used.
|
||||||
|
* Scripts use these numbers plus 1.
|
||||||
|
* Where these are used, values ≥ 0 (> 0 in scripts) correspond to actual monitors.
|
||||||
|
*/
|
||||||
|
#define MONITOR_NONE INT_MIN
|
||||||
|
#define MONITOR_ALL -2 /* Monitor no. -1 (all monitors as one) */
|
||||||
|
#define MONITOR_WINDOW -1 /* Monitor no. 0 (current monitor) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Atom my_wnck_atom_get(const char *atom_name);
|
||||||
|
|
||||||
|
void devilspie2_change_state(Screen *screen,
|
||||||
|
Window xwindow,
|
||||||
|
gboolean add,
|
||||||
|
Atom state1,
|
||||||
|
Atom state2);
|
||||||
|
|
||||||
|
Screen* devilspie2_window_get_xscreen(Window xid);
|
||||||
|
|
||||||
|
void devilspie2_error_trap_push();
|
||||||
|
int devilspie2_error_trap_pop();
|
||||||
|
|
||||||
|
gboolean decorate_window(Window xid);
|
||||||
|
gboolean undecorate_window(Window xid);
|
||||||
|
gboolean get_decorated(Window xid);
|
||||||
|
|
||||||
|
char* my_wnck_get_string_property(Window xwindow, Atom atom, gboolean *utf8) ATTR_MALLOC;
|
||||||
|
void my_wnck_set_string_property(Window xwindow, Atom atom, const gchar *const value, gboolean utf8);
|
||||||
|
void my_wnck_set_cardinal_property (Window xwindow, Atom atom, int32_t value);
|
||||||
|
void my_wnck_delete_property (Window xwindow, Atom atom);
|
||||||
|
|
||||||
|
gboolean my_wnck_get_cardinal_list(Window xwindow,
|
||||||
|
Atom atom,
|
||||||
|
gulong **cardinals,
|
||||||
|
int *len);
|
||||||
|
|
||||||
|
int devilspie2_get_viewport_start(Window xwindow, int *x, int *y);
|
||||||
|
|
||||||
|
void my_window_set_window_type(Window xid, gchar *window_type);
|
||||||
|
void my_window_set_opacity(Window xid, double value);
|
||||||
|
|
||||||
|
void adjust_for_decoration(WnckWindow *window, int *x, int *y, int *w, int *h);
|
||||||
|
void set_window_geometry(WnckWindow *window, int x, int y, int w, int h, gboolean adjust_for_decoration);
|
||||||
|
|
||||||
|
int get_monitor_count(void);
|
||||||
|
int get_monitor_index_geometry(WnckWindow *window, const GdkRectangle *window_r, /*out*/ GdkRectangle *monitor_r);
|
||||||
|
int get_monitor_geometry(int index, /*out*/ GdkRectangle *monitor_r);
|
||||||
|
|
||||||
|
int get_window_workspace_geometry(WnckWindow *window, /*out*/ GdkRectangle *monitor_r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrapper for the above geometry-reading functions
|
||||||
|
* Selects according to monitor number (MONITOR_ALL, MONITOR_WINDOW or a monitor index no.)
|
||||||
|
* Returns the monitor index, MONITOR_ALL or, on error, MONITOR_NONE
|
||||||
|
*/
|
||||||
|
int get_monitor_or_workspace_geometry(int monitor_no, WnckWindow *window, /*out*/ GdkRectangle *monitor_or_workspace_r);
|
||||||
|
|
||||||
|
#endif /*__HEADER_XUTILS_*/
|
|
@ -0,0 +1,69 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
conn, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to connect to X server:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Start the terminal (replace with your app)
|
||||||
|
go func() {
|
||||||
|
if err := exec.Command("mate-terminal", "--title", "Workspace1-Terminal").Start(); err != nil {
|
||||||
|
fmt.Println("Error starting terminal:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the window to appear
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// Get the root window
|
||||||
|
setup := xproto.Setup(conn)
|
||||||
|
root := setup.DefaultScreen(conn).Root
|
||||||
|
|
||||||
|
// List children windows
|
||||||
|
reply, err := xproto.QueryTree(conn, root).Reply()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to query windows:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the window with the specified title
|
||||||
|
var target xproto.Window
|
||||||
|
for _, child := range reply.Children {
|
||||||
|
nameReply, err := xproto.GetProperty(conn, false, child,
|
||||||
|
xproto.AtomWmName, xproto.AtomString, 0, (1<<32)-1).Reply()
|
||||||
|
if err != nil || len(nameReply.Value) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := string(nameReply.Value)
|
||||||
|
log.Info("found name:", name)
|
||||||
|
if name == "Workspace1-Terminal" {
|
||||||
|
target = child
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == 0 {
|
||||||
|
fmt.Println("Window not found.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the window to workspace 1 and set its geometry
|
||||||
|
xproto.ConfigureWindow(conn, target, xproto.ConfigWindowX|xproto.ConfigWindowY|xproto.ConfigWindowWidth|xproto.ConfigWindowHeight,
|
||||||
|
[]uint32{100, 100, 800, 600})
|
||||||
|
fmt.Println("Window moved and resized.")
|
||||||
|
}
|
Loading…
Reference in New Issue