Rename LXKeys into LXHotkey.
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Thu, 10 Mar 2016 19:09:46 +0000 (21:09 +0200)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Thu, 10 Mar 2016 19:19:58 +0000 (21:19 +0200)
15 files changed:
.gitignore
Makefile.am
configure.ac
lxhotkey.pc.in [new file with mode: 0644]
lxkeys.pc.in [deleted file]
plugins/Makefile.am
plugins/openbox.c
po/POTFILES.in
po/lxhotkey.pot [new file with mode: 0644]
po/lxkeys.pot [deleted file]
src/Makefile.am
src/lxhotkey.c [new file with mode: 0644]
src/lxhotkey.h [new file with mode: 0644]
src/lxkeys.c [deleted file]
src/lxkeys.h [deleted file]

index 77caac1..de47094 100644 (file)
@@ -25,6 +25,6 @@ po/POTFILES
 *.o
 *.lo
 *.la
-lxkeys
+lxhotkey
 *.pc
 *.tar.xz
index 0c04fce..b9bfa68 100644 (file)
@@ -5,6 +5,6 @@ ACLOCAL_AMFLAGS= -I m4
 SUBDIRS = src plugins po
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = lxkeys.pc
+pkgconfig_DATA = lxhotkey.pc
 
-EXTRA_DIST = autogen.sh lxkeys.pc.in
+EXTRA_DIST = autogen.sh lxhotkey.pc.in
index 1885400..77d829f 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.53)
-AC_INIT(lxkeys, 0.1.0, http://lxde.org/)
+AC_INIT(lxhotkey, 0.1.0, http://lxde.org/)
 AM_INIT_AUTOMAKE([-Wall foreign subdir-objects no-dist-gzip dist-xz])
 AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([m4])
@@ -24,7 +24,7 @@ LT_PREREQ([2.2])
 LT_INIT
 
 # International support
-GETTEXT_PACKAGE=lxkeys
+GETTEXT_PACKAGE=lxhotkey
 AC_SUBST(GETTEXT_PACKAGE)
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package.])
 
@@ -72,7 +72,7 @@ fi
 dnl Finish all
 AC_CONFIG_FILES([
     Makefile
-    lxkeys.pc
+    lxhotkey.pc
     src/Makefile
     plugins/Makefile
     po/Makefile.in
diff --git a/lxhotkey.pc.in b/lxhotkey.pc.in
new file mode 100644 (file)
index 0000000..ba2933d
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+pkglibdir=@libdir@/lxhotkey
+includedir=@includedir@
+pluginsdir=${pkglibdir}
+
+Name: lxhotkey
+Description: A lightweight global keyboard shortcuts configurator.
+Requires: libfm >= 1.2.0
+Version: @VERSION@
+Libs: 
+Cflags: -I${includedir}
diff --git a/lxkeys.pc.in b/lxkeys.pc.in
deleted file mode 100644 (file)
index 4d7b4f4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-pkglibdir=@libdir@/lxkeys
-includedir=@includedir@
-pluginsdir=${pkglibdir}
-
-Name: lxkeys
-Description: A lightweight global keyboard shortcuts configurator.
-Requires: libfm >= 1.2.0
-Version: @VERSION@
-Libs: 
-Cflags: -I${includedir}
index 927fd87..6e466e7 100644 (file)
@@ -8,10 +8,10 @@ AM_CPPFLAGS = \
 
 AM_LDFLAGS = \
        -module -avoid-version -shared -export-dynamic -no-undefined \
-       -rpath $(libdir)/lxkeys $(PACKAGE_LIBS)
+       -rpath $(libdir)/lxhotkey $(PACKAGE_LIBS)
 
 ## modules list
-pkglibdir = $(libdir)/lxkeys
+pkglibdir = $(libdir)/lxhotkey
 pkglib_LTLIBRARIES = ob.la
 
 ## Openbox module
index 680bdbc..4bed169 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
- * This file is a part of LXKeys project.
+ * This file is a part of LXHotkey project.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
 # include "config.h"
 #endif
 
-#include "lxkeys.h"
+#include "lxhotkey.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
 #include <fnmatch.h>
 
-#define LXKEYS_OB_ERROR lxkeys_ob_error_quark()
-static GQuark lxkeys_ob_error_quark(void)
+#define LXKEYS_OB_ERROR lxhotkey_ob_error_quark()
+static GQuark lxhotkey_ob_error_quark(void)
 {
     static GQuark q = 0;
 
     if G_UNLIKELY(q == 0)
-        q = g_quark_from_static_string("lxkeys-ob-error");
+        q = g_quark_from_static_string("lxhotkey-ob-error");
 
     return q;
 }
-enum LXKeysObError {
+enum LXHotkeyObError {
     LXKEYS_FILE_ERROR,
     LXKEYS_PARSE_ERROR
 };
 
 
-/* simple functions to manage LXKeysAttr data type */
-static inline LXKeysAttr *lxkeys_attr_new(void)
+/* simple functions to manage LXHotkeyAttr data type */
+static inline LXHotkeyAttr *lxhotkey_attr_new(void)
 {
-    return g_slice_new0(LXKeysAttr);
+    return g_slice_new0(LXHotkeyAttr);
 }
 
 #define free_options(acts) g_list_free_full(acts, (GDestroyNotify)lkxeys_attr_free)
 
-static void lkxeys_attr_free(LXKeysAttr *data)
+static void lkxeys_attr_free(LXHotkeyAttr *data)
 {
     g_free(data->name);
     g_list_free_full(data->values, g_free);
     free_options(data->subopts);
-    g_slice_free(LXKeysAttr, data);
+    g_slice_free(LXHotkeyAttr, data);
 }
 
-static void lkxeys_action_free(LXKeysGlobal *data)
+static void lkxeys_action_free(LXHotkeyGlobal *data)
 {
     free_options(data->actions);
     g_free(data->accel1);
@@ -72,7 +72,7 @@ static void lkxeys_action_free(LXKeysGlobal *data)
     g_free(data);
 }
 
-static void lkxeys_app_free(LXKeysApp *data)
+static void lkxeys_app_free(LXHotkeyApp *data)
 {
     g_free(data->exec);
     free_options(data->options);
@@ -81,11 +81,11 @@ static void lkxeys_app_free(LXKeysApp *data)
     g_free(data);
 }
 
-/* recursively compare two lists of LXKeysAttr */
+/* recursively compare two lists of LXHotkeyAttr */
 static gboolean options_equal(GList *opts1, GList *opts2)
 {
     while (opts1 && opts2) {
-        LXKeysAttr *attr1 = opts1->data, *attr2 = opts2->data;
+        LXHotkeyAttr *attr1 = opts1->data, *attr2 = opts2->data;
 
         if (g_strcmp0(attr1->name, attr2->name) != 0)
             return FALSE;
@@ -160,7 +160,7 @@ static gboolean restart_openbox(GError **error)
 
 static char * values_enabled[] = { N_("yes"), N_("no"), NULL };
 
-static LXKeysAttr options_startupnotify[] = {
+static LXHotkeyAttr options_startupnotify[] = {
     { N_("enabled"), BOOLEAN_VALUES, NULL, FALSE },
     { N_("wmclass"), NULL, NULL, FALSE },
     { N_("name"), NULL, NULL, FALSE },
@@ -168,7 +168,7 @@ static LXKeysAttr options_startupnotify[] = {
     { NULL }
 };
 
-static LXKeysAttr options_Execute[] = {
+static LXHotkeyAttr options_Execute[] = {
     { N_("command"), NULL, NULL, FALSE },
     { N_("prompt"), NULL, NULL, FALSE },
     { N_("startupnotify"), NULL, TO_BE_CONVERTED(options_startupnotify), FALSE },
@@ -179,14 +179,14 @@ static char * values_x[] = { "#", "%", N_("center"), NULL };
 static char * values_monitor[] = { N_("default"), N_("primary"), N_("mouse"),
                                    N_("active"), N_("all"), "#", NULL };
 
-static LXKeysAttr options_position[] = {
+static LXHotkeyAttr options_position[] = {
     { "x", TO_BE_CONVERTED(values_x), NULL, FALSE },
     { "y", TO_BE_PREVIOUS, NULL, FALSE },
     { N_("monitor"), TO_BE_CONVERTED(values_monitor), NULL, FALSE },
     { NULL }
 };
 
-static LXKeysAttr options_ShowMenu[] = {
+static LXHotkeyAttr options_ShowMenu[] = {
     { N_("menu"), NULL, NULL, FALSE },
     { N_("position"), NULL, TO_BE_CONVERTED(options_position), FALSE },
     { NULL }
@@ -194,7 +194,7 @@ static LXKeysAttr options_ShowMenu[] = {
 
 static char * values_dialog[] = { N_("list"), N_("icons"), N_("none"), NULL };
 
-static LXKeysAttr options_NextWindow[] = {
+static LXHotkeyAttr options_NextWindow[] = {
     { N_("dialog"), TO_BE_CONVERTED(values_dialog), NULL, FALSE },
     { N_("bar"), BOOLEAN_VALUES, NULL, FALSE },
     { N_("raise"), BOOLEAN_VALUES, NULL, FALSE },
@@ -211,7 +211,7 @@ static char * values_direction[] = { N_("north"), N_("northeast"), N_("east"),
                                      N_("southeast"), N_("south"), N_("southwest"),
                                      N_("west"), N_("northwest"), NULL };
 
-static LXKeysAttr options_DirectionalCycleWindows[] = {
+static LXHotkeyAttr options_DirectionalCycleWindows[] = {
     { N_("direction"), TO_BE_CONVERTED(values_direction), NULL, FALSE },
     { N_("dialog"), BOOLEAN_VALUES, NULL, FALSE },
     { N_("bar"), BOOLEAN_VALUES, NULL, FALSE },
@@ -227,7 +227,7 @@ static char * values_to[] = { "#",  N_("current"), N_("next"), N_("previous"),
                               N_("down"), N_("west"), N_("left"), N_("east"),
                               N_("right"), NULL };
 
-static LXKeysAttr options_GoToDesktop[] = {
+static LXHotkeyAttr options_GoToDesktop[] = {
     { N_("to"), TO_BE_CONVERTED(values_to), NULL, FALSE },
     { N_("wrap"), BOOLEAN_VALUES, NULL, FALSE },
     { NULL }
@@ -235,24 +235,24 @@ static LXKeysAttr options_GoToDesktop[] = {
 
 static char * values_where[] = { N_("current"), N_("last"), NULL };
 
-static LXKeysAttr options_AddDesktop[] = {
+static LXHotkeyAttr options_AddDesktop[] = {
     { N_("where"), TO_BE_CONVERTED(values_where), NULL, FALSE },
     { NULL }
 };
 
-static LXKeysAttr options_Restart[] = {
+static LXHotkeyAttr options_Restart[] = {
     { N_("command"), NULL, NULL, FALSE },
     { NULL }
 };
 
-static LXKeysAttr options_Exit[] = {
+static LXHotkeyAttr options_Exit[] = {
     { N_("prompt"), BOOLEAN_VALUES, NULL, FALSE },
     { NULL }
 };
 
 static char * values_directionM[] = { N_("both"), N_("horizontal"), N_("vertical"), NULL };
 
-static LXKeysAttr options_ToggleMaximize[] = {
+static LXHotkeyAttr options_ToggleMaximize[] = {
     { N_("direction"), TO_BE_CONVERTED(values_directionM), NULL, FALSE },
     { NULL }
 };
@@ -262,7 +262,7 @@ static char * values_toS[] = { "#", N_("current"), N_("next"), N_("previous"),
                                N_("down"), N_("west"), N_("left"), N_("east"),
                                N_("right"), NULL };
 
-static LXKeysAttr options_SendToDesktop[] = {
+static LXHotkeyAttr options_SendToDesktop[] = {
     { N_("to"), TO_BE_CONVERTED(values_toS), NULL, FALSE },
     { N_("wrap"), BOOLEAN_VALUES, NULL, FALSE },
     { N_("follow"), BOOLEAN_VALUES, NULL, FALSE },
@@ -273,7 +273,7 @@ static char * values_edge[] = { N_("top"), N_("left"), N_("right"), N_("bottom")
                                 N_("topleft"), N_("topright"), N_("bottomleft"),
                                 N_("bottomright"), NULL };
 
-static LXKeysAttr options_Resize[] = {
+static LXHotkeyAttr options_Resize[] = {
     { N_("edge"), TO_BE_CONVERTED(values_edge), NULL, FALSE },
     { NULL }
 };
@@ -282,7 +282,7 @@ static char * values_xM[] = { "#", N_("current"), N_("center"), NULL };
 static char * values_width[] = { "#", "%", N_("current"), NULL };
 static char * values_monitorM[] = { "#", N_("current"), N_("all"), N_("next"), N_("prev"), NULL };
 
-static LXKeysAttr options_MoveResizeTo[] = {
+static LXHotkeyAttr options_MoveResizeTo[] = {
     { "x", TO_BE_CONVERTED(values_xM), NULL, FALSE },
     { "y", TO_BE_PREVIOUS, NULL, FALSE },
     { N_("width"), TO_BE_CONVERTED(values_width), NULL, FALSE },
@@ -293,7 +293,7 @@ static LXKeysAttr options_MoveResizeTo[] = {
 
 static char * values_xR[] = { "#", NULL };
 
-static LXKeysAttr options_MoveRelative[] = {
+static LXHotkeyAttr options_MoveRelative[] = {
     { "x", TO_BE_CONVERTED(values_xR), NULL, FALSE },
     { "y", TO_BE_PREVIOUS, NULL, FALSE },
     { NULL }
@@ -301,7 +301,7 @@ static LXKeysAttr options_MoveRelative[] = {
 
 static char * values_xRR[] = { "#", NULL };
 
-static LXKeysAttr options_ResizeRelative[] = {
+static LXHotkeyAttr options_ResizeRelative[] = {
     { N_("left"), TO_BE_CONVERTED(values_xRR), NULL, FALSE },
     { N_("right"), TO_BE_PREVIOUS, NULL, FALSE },
     { N_("top"), TO_BE_PREVIOUS, NULL, FALSE },
@@ -311,19 +311,19 @@ static LXKeysAttr options_ResizeRelative[] = {
 
 static char * values_directionE[] = { N_("north"), N_("south"), N_("west"), N_("east"), NULL };
 
-static LXKeysAttr options_MoveToEdge[] = {
+static LXHotkeyAttr options_MoveToEdge[] = {
     { N_("direction"), TO_BE_CONVERTED(values_directionE), NULL, FALSE },
     { NULL }
 };
 
 static char * values_layer[] = { N_("top"), N_("normal"), N_("bottom"), NULL };
 
-static LXKeysAttr options_SendToLayer[] = {
+static LXHotkeyAttr options_SendToLayer[] = {
     { N_("layer"), TO_BE_CONVERTED(values_layer), NULL, FALSE },
     { NULL }
 };
 
-static LXKeysAttr list_actions[] = {
+static LXHotkeyAttr list_actions[] = {
     /* global actions */
     { N_("Execute"), NULL, TO_BE_CONVERTED(options_Execute), FALSE },
     { N_("ShowMenu"), NULL, TO_BE_CONVERTED(options_ShowMenu), FALSE },
@@ -393,7 +393,7 @@ static GList *convert_values(gpointer data)
 
 static GList *convert_options(gpointer data)
 {
-    LXKeysAttr *array, *last = NULL;
+    LXHotkeyAttr *array, *last = NULL;
     GList *list = NULL;
 
     for (array = data; array->name != NULL; array++) {
@@ -422,7 +422,7 @@ static GList *convert_options(gpointer data)
 
 typedef struct {
     FmXmlFileItem *parent; /* R/O */
-    GList *list; /* contains LXKeysAttr items for finished actions */
+    GList *list; /* contains LXHotkeyAttr items for finished actions */
 } ObActionsList;
 
 typedef struct {
@@ -470,9 +470,9 @@ static gboolean tag_handler_keybind(FmXmlFileItem *item, GList *children,
     GList *actions, *l;
     gchar *key;
     const char *exec_line = NULL;
-    LXKeysAttr *action;
-    LXKeysApp *app = NULL;
-    LXKeysGlobal *act;
+    LXHotkeyAttr *action;
+    LXHotkeyApp *app = NULL;
+    LXHotkeyGlobal *act;
     guint i;
 
     if (!cfg->stack || cfg->stack->next) { /* corruption! */
@@ -507,14 +507,14 @@ static gboolean tag_handler_keybind(FmXmlFileItem *item, GList *children,
                 exec_line = fm_xml_file_item_get_data(fm_xml_file_item_find_child(exec, FM_XML_FILE_TEXT), NULL);
                 for (l = cfg->execs; l; l = l->next)
                     /* if exec line is equal to one gathered already */
-                    if (strcmp(((LXKeysApp *)l->data)->exec, exec_line) == 0 &&
+                    if (strcmp(((LXHotkeyApp *)l->data)->exec, exec_line) == 0 &&
                         /* and it has no secondary keybinding */
-                        ((LXKeysApp *)l->data)->accel2 == NULL &&
+                        ((LXHotkeyApp *)l->data)->accel2 == NULL &&
                         /* and options are also equal */
-                        options_equal(((LXKeysApp *)l->data)->options, action->subopts))
+                        options_equal(((LXHotkeyApp *)l->data)->options, action->subopts))
                     {
                         /* then just add this keybinding to found one */
-                        app = (LXKeysApp *)l->data;
+                        app = (LXHotkeyApp *)l->data;
                         break;
                     }
             }
@@ -535,16 +535,16 @@ static gboolean tag_handler_keybind(FmXmlFileItem *item, GList *children,
     if (app) {
         app->accel2 = key;
         app->data2 = item;
-    /* otherwise create LXKeysApp or LXKeysGlobal and add it to the list */
+    /* otherwise create LXHotkeyApp or LXHotkeyGlobal and add it to the list */
     } else if (exec_line) {
-        app = g_new0(LXKeysApp, 1);
+        app = g_new0(LXHotkeyApp, 1);
         app->accel1 = key;
         app->exec = g_strdup(exec_line);
         app->data1 = item;
         app->options = action->subopts;
-        /* remove exec line from options, it should be in XML but not in LXKeysApp */
+        /* remove exec line from options, it should be in XML but not in LXHotkeyApp */
         for (l = app->options; l; ) {
-            LXKeysAttr *opt = l->data;
+            LXHotkeyAttr *opt = l->data;
             l = l->next;
             if (strcmp(opt->name, "command") == 0 || strcmp(opt->name, "execute") == 0) {
                 app->options = g_list_remove(app->options, opt);
@@ -556,12 +556,12 @@ static gboolean tag_handler_keybind(FmXmlFileItem *item, GList *children,
     } else {
         for (l = cfg->actions; l; l = l->next)
             /* if the same actions list was gathered already */
-            if (options_equal(((LXKeysGlobal *)l->data)->actions, actions)
+            if (options_equal(((LXHotkeyGlobal *)l->data)->actions, actions)
                 /* and it has no secondary keybinding */
-                && ((LXKeysGlobal *)l->data)->accel2 == NULL)
+                && ((LXHotkeyGlobal *)l->data)->accel2 == NULL)
                 break;
         if (l == NULL) {
-            act = g_new0(LXKeysGlobal, 1);
+            act = g_new0(LXHotkeyGlobal, 1);
             act->accel1 = key;
             act->data1 = item;
             act->actions = actions;
@@ -576,7 +576,7 @@ static gboolean tag_handler_keybind(FmXmlFileItem *item, GList *children,
     return TRUE;
 }
 
-/* collect all children of FmXmlFileItem into LXKeysAttr list
+/* collect all children of FmXmlFileItem into LXHotkeyAttr list
    removing from stack if found there
    since actions cannot be mixed with options, just
    ignore anything not collected into any ObActionsList */
@@ -586,7 +586,7 @@ static GList *resolve_item(GList **stack, GList *children, GList **value,
     GList *child, *l, *items = NULL;
     ObActionsList *act; /* stack item */
     FmXmlFileItem *item; /* child item */
-    LXKeysAttr *data;
+    LXHotkeyAttr *data;
 
     for (child = children; child; child = child->next) {
         item = child->data;
@@ -601,7 +601,7 @@ static GList *resolve_item(GList **stack, GList *children, GList **value,
             free_options(items);
             return NULL;
         }
-        data = lxkeys_attr_new();
+        data = lxhotkey_attr_new();
         data->name = g_strdup(fm_xml_file_item_get_tag_name(item));
         /* find if item is in stack, then remove from there and use ready list */
         for (l = *stack; l; l = l->next) {
@@ -685,14 +685,14 @@ static gboolean tag_handler_action(FmXmlFileItem *item, GList *children,
     /* if parent doesn't exist on stack neither any child is then it got
        deeper so just add it on stack */
     ObXmlFile *cfg = user_data;
-    LXKeysAttr *data;
+    LXHotkeyAttr *data;
     ObActionsList *oblist;
     FmXmlFileItem *parent;
     GError *err = NULL;
     guint i;
 
-    /* create a LXKeysAttr */
-    data = lxkeys_attr_new();
+    /* create a LXHotkeyAttr */
+    data = lxhotkey_attr_new();
     //data->has_actions = FALSE; /* action can have only options, not sub-actions! */
     /* resolve all children of this action, clearing from stack */
     data->subopts = resolve_item(&cfg->stack, children, &data->values, &err);
@@ -868,7 +868,7 @@ static GList *obcfg_get_wm_keys(gpointer config, const char *mask, GError **erro
 {
     ObXmlFile *cfg = (ObXmlFile *)config;
     GList *list = NULL, *l;
-    LXKeysGlobal *data;
+    LXHotkeyGlobal *data;
 
     if (cfg == NULL)
         g_set_error_literal(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
@@ -891,13 +891,13 @@ static gboolean tag_null_handler(FmXmlFileItem *item, GList *children,
     return TRUE;
 }
 
-/* if opts==NULL then don't copy any LXKeysAttr below it */
-static FmXmlFileItem *make_new_xml_item(ObXmlFile *cfg, LXKeysAttr *opt,
+/* if opts==NULL then don't copy any LXHotkeyAttr below it */
+static FmXmlFileItem *make_new_xml_item(ObXmlFile *cfg, LXHotkeyAttr *opt,
                                         GList **opts, gboolean is_action)
 {
     FmXmlFileItem *item, *sub;
     GList *l;
-    LXKeysAttr *act = NULL;
+    LXHotkeyAttr *act = NULL;
 
     if (is_action) {
         item = fm_xml_file_item_new(ObXmlFile_action);
@@ -922,7 +922,7 @@ static FmXmlFileItem *make_new_xml_item(ObXmlFile *cfg, LXKeysAttr *opt,
     }
     if (opts != NULL) {
         /* make a copy if requested */
-        act = g_new0(LXKeysAttr, 1);
+        act = g_new0(LXHotkeyAttr, 1);
         act->name = g_strdup(opt->name);
         if (opt->values)
             act->values = g_list_prepend(NULL, g_strdup(opt->values->data));
@@ -937,7 +937,7 @@ static FmXmlFileItem *make_new_xml_item(ObXmlFile *cfg, LXKeysAttr *opt,
     return item;
 }
 
-/* if opts==NULL then don't make any LXKeysAttr below it */
+/* if opts==NULL then don't make any LXHotkeyAttr below it */
 static FmXmlFileItem *make_new_xml_binding(ObXmlFile *cfg, GList *actions,
                                            const gchar *accel, GList **opts,
                                            const gchar *exec)
@@ -977,11 +977,11 @@ static inline void replace_key(FmXmlFileItem *item, const char *key, char **kptr
     *kptr = g_strdup(key);
 }
 
-static gboolean obcfg_set_wm_key(gpointer config, LXKeysGlobal *data, GError **error)
+static gboolean obcfg_set_wm_key(gpointer config, LXHotkeyGlobal *data, GError **error)
 {
     ObXmlFile *cfg = (ObXmlFile *)config;
     GList *l;
-    LXKeysGlobal *act;
+    LXHotkeyGlobal *act;
 
     if (cfg == NULL) {
         g_set_error_literal(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
@@ -995,20 +995,20 @@ static gboolean obcfg_set_wm_key(gpointer config, LXKeysGlobal *data, GError **e
     /* find if those keys are already bound elsewhere */
     for (l = cfg->actions; l; l = l->next) {
         if (data->accel1) {
-            if (strcmp(data->accel1, ((LXKeysGlobal *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel1, ((LXKeysGlobal *)l->data)->accel2) == 0)
+            if (strcmp(data->accel1, ((LXHotkeyGlobal *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel1, ((LXHotkeyGlobal *)l->data)->accel2) == 0)
                 goto _accel1_bound;
         }
         if (data->accel2) {
-            if (g_strcmp0(data->accel2, ((LXKeysGlobal *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel2, ((LXKeysGlobal *)l->data)->accel2) == 0)
+            if (g_strcmp0(data->accel2, ((LXHotkeyGlobal *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel2, ((LXHotkeyGlobal *)l->data)->accel2) == 0)
                 goto _accel2_bound;
         }
     }
     for (l = cfg->execs; l; l = l->next) {
         if (data->accel1) {
-            if (strcmp(data->accel1, ((LXKeysApp *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel1, ((LXKeysApp *)l->data)->accel2) == 0) {
+            if (strcmp(data->accel1, ((LXHotkeyApp *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel1, ((LXHotkeyApp *)l->data)->accel2) == 0) {
 _accel1_bound:
                 g_set_error(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
                             _("Hotkey '%s' is already bound to an action."),
@@ -1017,8 +1017,8 @@ _accel1_bound:
             }
         }
         if (data->accel2) {
-            if (g_strcmp0(data->accel2, ((LXKeysApp *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel2, ((LXKeysApp *)l->data)->accel2) == 0) {
+            if (g_strcmp0(data->accel2, ((LXHotkeyApp *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel2, ((LXHotkeyApp *)l->data)->accel2) == 0) {
 _accel2_bound:
                 g_set_error(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
                             _("Hotkey '%s' is already bound to an action."),
@@ -1108,7 +1108,7 @@ _accel2_bound:
         }
     /* if not found then add a new keybinding */
     } else if (data->accel1) {
-        act = g_new0(LXKeysGlobal, 1);
+        act = g_new0(LXHotkeyGlobal, 1);
         act->data1 = make_new_xml_binding(cfg, data->actions, data->accel1, &act->actions, NULL);
         act->accel1 = g_strdup(data->accel1);
         /* do the same for accel2 if requested */
@@ -1125,7 +1125,7 @@ static GList *obcfg_get_app_keys(gpointer config, const char *mask, GError **err
 {
     ObXmlFile *cfg = (ObXmlFile *)config;
     GList *list = NULL, *l;
-    LXKeysApp *data;
+    LXHotkeyApp *data;
 
     if (cfg == NULL)
         g_set_error_literal(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
@@ -1139,11 +1139,11 @@ static GList *obcfg_get_app_keys(gpointer config, const char *mask, GError **err
     return list;
 }
 
-static gboolean obcfg_set_app_key(gpointer config, LXKeysApp *data, GError **error)
+static gboolean obcfg_set_app_key(gpointer config, LXHotkeyApp *data, GError **error)
 {
     ObXmlFile *cfg = (ObXmlFile *)config;
     GList *l;
-    LXKeysApp *app;
+    LXHotkeyApp *app;
 
     if (cfg == NULL) {
         g_set_error_literal(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
@@ -1157,20 +1157,20 @@ static gboolean obcfg_set_app_key(gpointer config, LXKeysApp *data, GError **err
     /* find if those keys are already bound elsewhere */
     for (l = cfg->actions; l; l = l->next) {
         if (data->accel1) {
-            if (strcmp(data->accel1, ((LXKeysGlobal *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel1, ((LXKeysGlobal *)l->data)->accel2) == 0)
+            if (strcmp(data->accel1, ((LXHotkeyGlobal *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel1, ((LXHotkeyGlobal *)l->data)->accel2) == 0)
                 goto _accel1_bound;
         }
         if (data->accel2) {
-            if (g_strcmp0(data->accel2, ((LXKeysGlobal *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel2, ((LXKeysGlobal *)l->data)->accel2) == 0)
+            if (g_strcmp0(data->accel2, ((LXHotkeyGlobal *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel2, ((LXHotkeyGlobal *)l->data)->accel2) == 0)
                 goto _accel2_bound;
         }
     }
     for (l = cfg->execs; l; l = l->next) {
         if (data->accel1) {
-            if (strcmp(data->accel1, ((LXKeysApp *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel1, ((LXKeysApp *)l->data)->accel2) == 0) {
+            if (strcmp(data->accel1, ((LXHotkeyApp *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel1, ((LXHotkeyApp *)l->data)->accel2) == 0) {
 _accel1_bound:
                 g_set_error(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
                             _("Hotkey '%s' is already bound to an action."),
@@ -1179,8 +1179,8 @@ _accel1_bound:
             }
         }
         if (data->accel2) {
-            if (g_strcmp0(data->accel2, ((LXKeysApp *)l->data)->accel1) == 0 ||
-                g_strcmp0(data->accel2, ((LXKeysApp *)l->data)->accel2) == 0) {
+            if (g_strcmp0(data->accel2, ((LXHotkeyApp *)l->data)->accel1) == 0 ||
+                g_strcmp0(data->accel2, ((LXHotkeyApp *)l->data)->accel2) == 0) {
 _accel2_bound:
                 g_set_error(error, LXKEYS_OB_ERROR, LXKEYS_FILE_ERROR,
                             _("Hotkey '%s' is already bound to an action."),
@@ -1271,7 +1271,7 @@ _accel2_bound:
         }
     /* if not found then add a new keybinding */
     } else if (data->accel1) {
-        app = g_new0(LXKeysApp, 1);
+        app = g_new0(LXHotkeyApp, 1);
         app->exec = g_strdup(data->exec);
         app->data1 = make_new_xml_binding(cfg, data->options, data->accel1, &app->options, data->exec);
         app->accel1 = g_strdup(data->accel1);
@@ -1301,9 +1301,9 @@ static GList *obcfg_get_app_options(gpointer config, GError **error)
 }
 
 
-FM_DEFINE_MODULE(lxkeys, Openbox)
+FM_DEFINE_MODULE(lxhotkey, Openbox)
 
-LXKeysPluginInit fm_module_init_lxkeys = {
+LXHotkeyPluginInit fm_module_init_lxhotkey = {
     .load = obcfg_load,
     .save = obcfg_save,
     .free = obcfg_free,
index 964e903..888dfe1 100644 (file)
@@ -1,2 +1,2 @@
-src/lxkeys.c
+src/lxhotkey.c
 plugins/openbox.c
diff --git a/po/lxhotkey.pot b/po/lxhotkey.pot
new file mode 100644 (file)
index 0000000..d00e798
--- /dev/null
@@ -0,0 +1,641 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-03-10 21:19+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../src/lxhotkey.c:228
+#, c-format
+msgid "Usage: %s global [<action>]      - show keys bound to action(s)\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:229
+#, c-format
+msgid "       %s global <action> <key>  - bind a key to the action\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:230
+#, c-format
+msgid "       %s app [<exec>]           - show keys bound to exec line\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:231
+#, c-format
+msgid "       %s app <exec> <key>       - bind a key to some exec line\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:232
+#, c-format
+msgid "       %s app <exec> --          - unbind all keys from exec line\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:233
+#, c-format
+msgid "       %s show <key>             - show the action bound to a key\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:234
+#, c-format
+msgid "       %s --gui=<type>           - start with GUI\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:329
+msgid "empty option name."
+msgstr ""
+
+#: ../src/lxhotkey.c:332
+msgid "empty action name."
+msgstr ""
+
+#: ../src/lxhotkey.c:364
+#, c-format
+msgid "no matching option '%s' found for action '%s'."
+msgstr ""
+
+#: ../src/lxhotkey.c:368
+#, c-format
+msgid "action '%s' isn't supported by WM %s."
+msgstr ""
+
+#: ../src/lxhotkey.c:385
+#, c-format
+msgid "value '%s' is not supported for option '%s'."
+msgstr ""
+
+#: ../src/lxhotkey.c:389
+#, c-format
+msgid "value '%s' is not supported for action '%s'."
+msgstr ""
+
+#: ../src/lxhotkey.c:402
+#, c-format
+msgid "action '%s' does not support options."
+msgstr ""
+
+#: ../src/lxhotkey.c:466
+#, c-format
+msgid "LXHotkey: sorry, cannot configure keys remotely.\n"
+msgstr ""
+
+#: ../src/lxhotkey.c:493
+#, c-format
+msgid "Window manager %s isn't supported now, sorry."
+msgstr ""
+
+#: ../src/lxhotkey.c:500
+msgid "Problems loading configuration: "
+msgstr ""
+
+#: ../src/lxhotkey.c:509
+#, c-format
+msgid "GUI type %s currently isn't supported."
+msgstr ""
+
+#. invalid request
+#: ../src/lxhotkey.c:526 ../src/lxhotkey.c:602
+msgid "Invalid request: "
+msgstr ""
+
+#: ../src/lxhotkey.c:536 ../src/lxhotkey.c:614
+msgid "Problems saving configuration: "
+msgstr ""
+
+#: ../src/lxhotkey.c:553
+msgid "ACTION(s)"
+msgstr ""
+
+#: ../src/lxhotkey.c:553 ../src/lxhotkey.c:631
+msgid "KEY(s)"
+msgstr ""
+
+#: ../src/lxhotkey.c:631
+msgid "EXEC"
+msgstr ""
+
+#: ../src/lxhotkey.c:652
+msgid "Requested operation isn't supported."
+msgstr ""
+
+#: ../plugins/openbox.c:139
+msgid "Failed to reconfigure Openbox."
+msgstr ""
+
+#. reuse GList
+#. reuse GList
+#: ../plugins/openbox.c:161
+msgid "yes"
+msgstr ""
+
+#: ../plugins/openbox.c:161
+msgid "no"
+msgstr ""
+
+#: ../plugins/openbox.c:164
+msgid "enabled"
+msgstr ""
+
+#: ../plugins/openbox.c:165
+msgid "wmclass"
+msgstr ""
+
+#: ../plugins/openbox.c:166
+msgid "name"
+msgstr ""
+
+#: ../plugins/openbox.c:167
+msgid "icon"
+msgstr ""
+
+#: ../plugins/openbox.c:172 ../plugins/openbox.c:244
+msgid "command"
+msgstr ""
+
+#: ../plugins/openbox.c:173 ../plugins/openbox.c:249
+msgid "prompt"
+msgstr ""
+
+#: ../plugins/openbox.c:174
+msgid "startupnotify"
+msgstr ""
+
+#: ../plugins/openbox.c:178 ../plugins/openbox.c:281
+msgid "center"
+msgstr ""
+
+#: ../plugins/openbox.c:179
+msgid "default"
+msgstr ""
+
+#: ../plugins/openbox.c:179
+msgid "primary"
+msgstr ""
+
+#: ../plugins/openbox.c:179
+msgid "mouse"
+msgstr ""
+
+#: ../plugins/openbox.c:180
+msgid "active"
+msgstr ""
+
+#: ../plugins/openbox.c:180 ../plugins/openbox.c:283
+msgid "all"
+msgstr ""
+
+#: ../plugins/openbox.c:185 ../plugins/openbox.c:290
+msgid "monitor"
+msgstr ""
+
+#: ../plugins/openbox.c:190
+msgid "menu"
+msgstr ""
+
+#: ../plugins/openbox.c:191
+msgid "position"
+msgstr ""
+
+#: ../plugins/openbox.c:195
+msgid "list"
+msgstr ""
+
+#: ../plugins/openbox.c:195
+msgid "icons"
+msgstr ""
+
+#: ../plugins/openbox.c:195
+msgid "none"
+msgstr ""
+
+#: ../plugins/openbox.c:198 ../plugins/openbox.c:216
+msgid "dialog"
+msgstr ""
+
+#: ../plugins/openbox.c:199 ../plugins/openbox.c:217
+msgid "bar"
+msgstr ""
+
+#: ../plugins/openbox.c:200 ../plugins/openbox.c:218
+msgid "raise"
+msgstr ""
+
+#: ../plugins/openbox.c:201
+msgid "allDesktops"
+msgstr ""
+
+#: ../plugins/openbox.c:202 ../plugins/openbox.c:219
+msgid "panels"
+msgstr ""
+
+#: ../plugins/openbox.c:203
+msgid "desktop"
+msgstr ""
+
+#: ../plugins/openbox.c:204
+msgid "linear"
+msgstr ""
+
+#: ../plugins/openbox.c:205
+msgid "interactive"
+msgstr ""
+
+#: ../plugins/openbox.c:206 ../plugins/openbox.c:221
+msgid "finalactions"
+msgstr ""
+
+#: ../plugins/openbox.c:210 ../plugins/openbox.c:226 ../plugins/openbox.c:261
+#: ../plugins/openbox.c:312
+msgid "north"
+msgstr ""
+
+#: ../plugins/openbox.c:210
+msgid "northeast"
+msgstr ""
+
+#: ../plugins/openbox.c:210 ../plugins/openbox.c:227 ../plugins/openbox.c:262
+#: ../plugins/openbox.c:312
+msgid "east"
+msgstr ""
+
+#: ../plugins/openbox.c:211
+msgid "southeast"
+msgstr ""
+
+#: ../plugins/openbox.c:211 ../plugins/openbox.c:226 ../plugins/openbox.c:261
+#: ../plugins/openbox.c:312
+msgid "south"
+msgstr ""
+
+#: ../plugins/openbox.c:211
+msgid "southwest"
+msgstr ""
+
+#: ../plugins/openbox.c:212 ../plugins/openbox.c:227 ../plugins/openbox.c:262
+#: ../plugins/openbox.c:312
+msgid "west"
+msgstr ""
+
+#: ../plugins/openbox.c:212
+msgid "northwest"
+msgstr ""
+
+#: ../plugins/openbox.c:215 ../plugins/openbox.c:256 ../plugins/openbox.c:315
+msgid "direction"
+msgstr ""
+
+#: ../plugins/openbox.c:220
+msgid "desktops"
+msgstr ""
+
+#: ../plugins/openbox.c:225 ../plugins/openbox.c:236 ../plugins/openbox.c:260
+#: ../plugins/openbox.c:281 ../plugins/openbox.c:282 ../plugins/openbox.c:283
+msgid "current"
+msgstr ""
+
+#: ../plugins/openbox.c:225 ../plugins/openbox.c:260 ../plugins/openbox.c:283
+msgid "next"
+msgstr ""
+
+#: ../plugins/openbox.c:225 ../plugins/openbox.c:260
+msgid "previous"
+msgstr ""
+
+#: ../plugins/openbox.c:226 ../plugins/openbox.c:236 ../plugins/openbox.c:261
+msgid "last"
+msgstr ""
+
+#: ../plugins/openbox.c:226 ../plugins/openbox.c:261
+msgid "up"
+msgstr ""
+
+#: ../plugins/openbox.c:227 ../plugins/openbox.c:262
+msgid "down"
+msgstr ""
+
+#: ../plugins/openbox.c:227 ../plugins/openbox.c:262 ../plugins/openbox.c:272
+#: ../plugins/openbox.c:305
+msgid "left"
+msgstr ""
+
+#: ../plugins/openbox.c:228 ../plugins/openbox.c:263 ../plugins/openbox.c:272
+#: ../plugins/openbox.c:306
+msgid "right"
+msgstr ""
+
+#: ../plugins/openbox.c:231 ../plugins/openbox.c:266
+msgid "to"
+msgstr ""
+
+#: ../plugins/openbox.c:232 ../plugins/openbox.c:267
+msgid "wrap"
+msgstr ""
+
+#: ../plugins/openbox.c:239
+msgid "where"
+msgstr ""
+
+#: ../plugins/openbox.c:253
+msgid "both"
+msgstr ""
+
+#: ../plugins/openbox.c:253
+msgid "horizontal"
+msgstr ""
+
+#: ../plugins/openbox.c:253
+msgid "vertical"
+msgstr ""
+
+#: ../plugins/openbox.c:268
+msgid "follow"
+msgstr ""
+
+#: ../plugins/openbox.c:272 ../plugins/openbox.c:307 ../plugins/openbox.c:319
+msgid "top"
+msgstr ""
+
+#: ../plugins/openbox.c:272 ../plugins/openbox.c:308 ../plugins/openbox.c:319
+msgid "bottom"
+msgstr ""
+
+#: ../plugins/openbox.c:273
+msgid "topleft"
+msgstr ""
+
+#: ../plugins/openbox.c:273
+msgid "topright"
+msgstr ""
+
+#: ../plugins/openbox.c:273
+msgid "bottomleft"
+msgstr ""
+
+#: ../plugins/openbox.c:274
+msgid "bottomright"
+msgstr ""
+
+#: ../plugins/openbox.c:277
+msgid "edge"
+msgstr ""
+
+#: ../plugins/openbox.c:283
+msgid "prev"
+msgstr ""
+
+#: ../plugins/openbox.c:288
+msgid "width"
+msgstr ""
+
+#: ../plugins/openbox.c:289
+msgid "height"
+msgstr ""
+
+#: ../plugins/openbox.c:319
+msgid "normal"
+msgstr ""
+
+#: ../plugins/openbox.c:322
+msgid "layer"
+msgstr ""
+
+#. global actions
+#: ../plugins/openbox.c:328
+msgid "Execute"
+msgstr ""
+
+#: ../plugins/openbox.c:329
+msgid "ShowMenu"
+msgstr ""
+
+#: ../plugins/openbox.c:330
+msgid "NextWindow"
+msgstr ""
+
+#: ../plugins/openbox.c:331
+msgid "PreviousWindow"
+msgstr ""
+
+#: ../plugins/openbox.c:332
+msgid "DirectionalCycleWindows"
+msgstr ""
+
+#: ../plugins/openbox.c:333
+msgid "DirectionalTargetWindow"
+msgstr ""
+
+#: ../plugins/openbox.c:334
+msgid "GoToDesktop"
+msgstr ""
+
+#: ../plugins/openbox.c:335
+msgid "AddDesktop"
+msgstr ""
+
+#: ../plugins/openbox.c:336
+msgid "RemoveDesktop"
+msgstr ""
+
+#: ../plugins/openbox.c:337
+msgid "ToggleDockAutohide"
+msgstr ""
+
+#: ../plugins/openbox.c:338
+msgid "Reconfigure"
+msgstr ""
+
+#: ../plugins/openbox.c:339
+msgid "Restart"
+msgstr ""
+
+#: ../plugins/openbox.c:340
+msgid "Exit"
+msgstr ""
+
+#. windows actions
+#: ../plugins/openbox.c:342
+msgid "Focus"
+msgstr ""
+
+#: ../plugins/openbox.c:343
+msgid "Raise"
+msgstr ""
+
+#: ../plugins/openbox.c:344
+msgid "Lower"
+msgstr ""
+
+#: ../plugins/openbox.c:345
+msgid "RaiseLower"
+msgstr ""
+
+#: ../plugins/openbox.c:346
+msgid "Unfocus"
+msgstr ""
+
+#: ../plugins/openbox.c:347
+msgid "FocusToBottom"
+msgstr ""
+
+#: ../plugins/openbox.c:348
+msgid "Iconify"
+msgstr ""
+
+#: ../plugins/openbox.c:349
+msgid "Close"
+msgstr ""
+
+#: ../plugins/openbox.c:350
+msgid "ToggleShade"
+msgstr ""
+
+#: ../plugins/openbox.c:351
+msgid "Shade"
+msgstr ""
+
+#: ../plugins/openbox.c:352
+msgid "Unshade"
+msgstr ""
+
+#: ../plugins/openbox.c:353
+msgid "ToggleOmnipresent"
+msgstr ""
+
+#: ../plugins/openbox.c:354
+msgid "ToggleMaximize"
+msgstr ""
+
+#: ../plugins/openbox.c:355
+msgid "Maximize"
+msgstr ""
+
+#: ../plugins/openbox.c:356
+msgid "Unmaximize"
+msgstr ""
+
+#: ../plugins/openbox.c:357
+msgid "ToggleFullscreen"
+msgstr ""
+
+#: ../plugins/openbox.c:358
+msgid "ToggleDecorations"
+msgstr ""
+
+#: ../plugins/openbox.c:359
+msgid "Decorate"
+msgstr ""
+
+#: ../plugins/openbox.c:360
+msgid "Undecorate"
+msgstr ""
+
+#: ../plugins/openbox.c:361
+msgid "SendToDesktop"
+msgstr ""
+
+#: ../plugins/openbox.c:362
+msgid "Move"
+msgstr ""
+
+#: ../plugins/openbox.c:363
+msgid "Resize"
+msgstr ""
+
+#: ../plugins/openbox.c:364
+msgid "MoveResizeTo"
+msgstr ""
+
+#: ../plugins/openbox.c:365
+msgid "MoveRelative"
+msgstr ""
+
+#: ../plugins/openbox.c:366
+msgid "ResizeRelative"
+msgstr ""
+
+#: ../plugins/openbox.c:367
+msgid "MoveToEdge"
+msgstr ""
+
+#: ../plugins/openbox.c:368
+msgid "GrowToEdge"
+msgstr ""
+
+#: ../plugins/openbox.c:369
+msgid "ShrinkToEdge"
+msgstr ""
+
+#: ../plugins/openbox.c:370
+msgid "GrowToFill"
+msgstr ""
+
+#: ../plugins/openbox.c:371
+msgid "ToggleAlwaysOnTop"
+msgstr ""
+
+#: ../plugins/openbox.c:372
+msgid "ToggleAlwaysOnBottom"
+msgstr ""
+
+#: ../plugins/openbox.c:373
+msgid "SendToLayer"
+msgstr ""
+
+#: ../plugins/openbox.c:455
+msgid "Duplicate <keyboard> section in the rc.xml file."
+msgstr ""
+
+#: ../plugins/openbox.c:480 ../plugins/openbox.c:486 ../plugins/openbox.c:721
+msgid "Internal error."
+msgstr ""
+
+#: ../plugins/openbox.c:529
+msgid "rc.xml error: no key is set for a keybind."
+msgstr ""
+
+#: ../plugins/openbox.c:600
+msgid "Invalid rc.xml: action with a sub-action."
+msgstr ""
+
+#: ../plugins/openbox.c:710
+msgid "rc.xml error: no name is set for action."
+msgstr ""
+
+#: ../plugins/openbox.c:740
+#, c-format
+msgid "rc.xml error: empty tag <%s> is prohibited."
+msgstr ""
+
+#: ../plugins/openbox.c:829
+msgid "Could not find the rc.xml file anywhere."
+msgstr ""
+
+#: ../plugins/openbox.c:875 ../plugins/openbox.c:988 ../plugins/openbox.c:1132
+#: ../plugins/openbox.c:1150
+msgid "No WM configuration is available."
+msgstr ""
+
+#: ../plugins/openbox.c:992
+msgid "Keybinding should activate at least one action."
+msgstr ""
+
+#: ../plugins/openbox.c:1014 ../plugins/openbox.c:1024
+#: ../plugins/openbox.c:1176 ../plugins/openbox.c:1186
+#, c-format
+msgid "Hotkey '%s' is already bound to an action."
+msgstr ""
+
+#: ../plugins/openbox.c:1154
+msgid "The exec line cannot be empty."
+msgstr ""
diff --git a/po/lxkeys.pot b/po/lxkeys.pot
deleted file mode 100644 (file)
index e8ef407..0000000
+++ /dev/null
@@ -1,641 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-03-10 20:52+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: ../src/lxkeys.c:228
-#, c-format
-msgid "Usage: %s global [<action>]      - show keys bound to action(s)\n"
-msgstr ""
-
-#: ../src/lxkeys.c:229
-#, c-format
-msgid "       %s global <action> <key>  - bind a key to the action\n"
-msgstr ""
-
-#: ../src/lxkeys.c:230
-#, c-format
-msgid "       %s app [<exec>]           - show keys bound to exec line\n"
-msgstr ""
-
-#: ../src/lxkeys.c:231
-#, c-format
-msgid "       %s app <exec> <key>       - bind a key to some exec line\n"
-msgstr ""
-
-#: ../src/lxkeys.c:232
-#, c-format
-msgid "       %s app <exec> --          - unbind all keys from exec line\n"
-msgstr ""
-
-#: ../src/lxkeys.c:233
-#, c-format
-msgid "       %s show <key>             - show the action bound to a key\n"
-msgstr ""
-
-#: ../src/lxkeys.c:234
-#, c-format
-msgid "       %s --gui=<type>           - start with GUI\n"
-msgstr ""
-
-#: ../src/lxkeys.c:329
-msgid "empty option name."
-msgstr ""
-
-#: ../src/lxkeys.c:332
-msgid "empty action name."
-msgstr ""
-
-#: ../src/lxkeys.c:364
-#, c-format
-msgid "no matching option '%s' found for action '%s'."
-msgstr ""
-
-#: ../src/lxkeys.c:368
-#, c-format
-msgid "action '%s' isn't supported by WM %s."
-msgstr ""
-
-#: ../src/lxkeys.c:385
-#, c-format
-msgid "value '%s' is not supported for option '%s'."
-msgstr ""
-
-#: ../src/lxkeys.c:389
-#, c-format
-msgid "value '%s' is not supported for action '%s'."
-msgstr ""
-
-#: ../src/lxkeys.c:402
-#, c-format
-msgid "action '%s' does not support options."
-msgstr ""
-
-#: ../src/lxkeys.c:466
-#, c-format
-msgid "LXKeys: sorry, cannot configure keys remotely.\n"
-msgstr ""
-
-#: ../src/lxkeys.c:493
-#, c-format
-msgid "Window manager %s isn't supported now, sorry."
-msgstr ""
-
-#: ../src/lxkeys.c:500
-msgid "Problems loading configuration: "
-msgstr ""
-
-#: ../src/lxkeys.c:509
-#, c-format
-msgid "GUI type %s currently isn't supported."
-msgstr ""
-
-#. invalid request
-#: ../src/lxkeys.c:526 ../src/lxkeys.c:602
-msgid "Invalid request: "
-msgstr ""
-
-#: ../src/lxkeys.c:536 ../src/lxkeys.c:614
-msgid "Problems saving configuration: "
-msgstr ""
-
-#: ../src/lxkeys.c:553
-msgid "ACTION(s)"
-msgstr ""
-
-#: ../src/lxkeys.c:553 ../src/lxkeys.c:631
-msgid "KEY(s)"
-msgstr ""
-
-#: ../src/lxkeys.c:631
-msgid "EXEC"
-msgstr ""
-
-#: ../src/lxkeys.c:652
-msgid "Requested operation isn't supported."
-msgstr ""
-
-#: ../plugins/openbox.c:139
-msgid "Failed to reconfigure Openbox."
-msgstr ""
-
-#. reuse GList
-#. reuse GList
-#: ../plugins/openbox.c:161
-msgid "yes"
-msgstr ""
-
-#: ../plugins/openbox.c:161
-msgid "no"
-msgstr ""
-
-#: ../plugins/openbox.c:164
-msgid "enabled"
-msgstr ""
-
-#: ../plugins/openbox.c:165
-msgid "wmclass"
-msgstr ""
-
-#: ../plugins/openbox.c:166
-msgid "name"
-msgstr ""
-
-#: ../plugins/openbox.c:167
-msgid "icon"
-msgstr ""
-
-#: ../plugins/openbox.c:172 ../plugins/openbox.c:244
-msgid "command"
-msgstr ""
-
-#: ../plugins/openbox.c:173 ../plugins/openbox.c:249
-msgid "prompt"
-msgstr ""
-
-#: ../plugins/openbox.c:174
-msgid "startupnotify"
-msgstr ""
-
-#: ../plugins/openbox.c:178 ../plugins/openbox.c:281
-msgid "center"
-msgstr ""
-
-#: ../plugins/openbox.c:179
-msgid "default"
-msgstr ""
-
-#: ../plugins/openbox.c:179
-msgid "primary"
-msgstr ""
-
-#: ../plugins/openbox.c:179
-msgid "mouse"
-msgstr ""
-
-#: ../plugins/openbox.c:180
-msgid "active"
-msgstr ""
-
-#: ../plugins/openbox.c:180 ../plugins/openbox.c:283
-msgid "all"
-msgstr ""
-
-#: ../plugins/openbox.c:185 ../plugins/openbox.c:290
-msgid "monitor"
-msgstr ""
-
-#: ../plugins/openbox.c:190
-msgid "menu"
-msgstr ""
-
-#: ../plugins/openbox.c:191
-msgid "position"
-msgstr ""
-
-#: ../plugins/openbox.c:195
-msgid "list"
-msgstr ""
-
-#: ../plugins/openbox.c:195
-msgid "icons"
-msgstr ""
-
-#: ../plugins/openbox.c:195
-msgid "none"
-msgstr ""
-
-#: ../plugins/openbox.c:198 ../plugins/openbox.c:216
-msgid "dialog"
-msgstr ""
-
-#: ../plugins/openbox.c:199 ../plugins/openbox.c:217
-msgid "bar"
-msgstr ""
-
-#: ../plugins/openbox.c:200 ../plugins/openbox.c:218
-msgid "raise"
-msgstr ""
-
-#: ../plugins/openbox.c:201
-msgid "allDesktops"
-msgstr ""
-
-#: ../plugins/openbox.c:202 ../plugins/openbox.c:219
-msgid "panels"
-msgstr ""
-
-#: ../plugins/openbox.c:203
-msgid "desktop"
-msgstr ""
-
-#: ../plugins/openbox.c:204
-msgid "linear"
-msgstr ""
-
-#: ../plugins/openbox.c:205
-msgid "interactive"
-msgstr ""
-
-#: ../plugins/openbox.c:206 ../plugins/openbox.c:221
-msgid "finalactions"
-msgstr ""
-
-#: ../plugins/openbox.c:210 ../plugins/openbox.c:226 ../plugins/openbox.c:261
-#: ../plugins/openbox.c:312
-msgid "north"
-msgstr ""
-
-#: ../plugins/openbox.c:210
-msgid "northeast"
-msgstr ""
-
-#: ../plugins/openbox.c:210 ../plugins/openbox.c:227 ../plugins/openbox.c:262
-#: ../plugins/openbox.c:312
-msgid "east"
-msgstr ""
-
-#: ../plugins/openbox.c:211
-msgid "southeast"
-msgstr ""
-
-#: ../plugins/openbox.c:211 ../plugins/openbox.c:226 ../plugins/openbox.c:261
-#: ../plugins/openbox.c:312
-msgid "south"
-msgstr ""
-
-#: ../plugins/openbox.c:211
-msgid "southwest"
-msgstr ""
-
-#: ../plugins/openbox.c:212 ../plugins/openbox.c:227 ../plugins/openbox.c:262
-#: ../plugins/openbox.c:312
-msgid "west"
-msgstr ""
-
-#: ../plugins/openbox.c:212
-msgid "northwest"
-msgstr ""
-
-#: ../plugins/openbox.c:215 ../plugins/openbox.c:256 ../plugins/openbox.c:315
-msgid "direction"
-msgstr ""
-
-#: ../plugins/openbox.c:220
-msgid "desktops"
-msgstr ""
-
-#: ../plugins/openbox.c:225 ../plugins/openbox.c:236 ../plugins/openbox.c:260
-#: ../plugins/openbox.c:281 ../plugins/openbox.c:282 ../plugins/openbox.c:283
-msgid "current"
-msgstr ""
-
-#: ../plugins/openbox.c:225 ../plugins/openbox.c:260 ../plugins/openbox.c:283
-msgid "next"
-msgstr ""
-
-#: ../plugins/openbox.c:225 ../plugins/openbox.c:260
-msgid "previous"
-msgstr ""
-
-#: ../plugins/openbox.c:226 ../plugins/openbox.c:236 ../plugins/openbox.c:261
-msgid "last"
-msgstr ""
-
-#: ../plugins/openbox.c:226 ../plugins/openbox.c:261
-msgid "up"
-msgstr ""
-
-#: ../plugins/openbox.c:227 ../plugins/openbox.c:262
-msgid "down"
-msgstr ""
-
-#: ../plugins/openbox.c:227 ../plugins/openbox.c:262 ../plugins/openbox.c:272
-#: ../plugins/openbox.c:305
-msgid "left"
-msgstr ""
-
-#: ../plugins/openbox.c:228 ../plugins/openbox.c:263 ../plugins/openbox.c:272
-#: ../plugins/openbox.c:306
-msgid "right"
-msgstr ""
-
-#: ../plugins/openbox.c:231 ../plugins/openbox.c:266
-msgid "to"
-msgstr ""
-
-#: ../plugins/openbox.c:232 ../plugins/openbox.c:267
-msgid "wrap"
-msgstr ""
-
-#: ../plugins/openbox.c:239
-msgid "where"
-msgstr ""
-
-#: ../plugins/openbox.c:253
-msgid "both"
-msgstr ""
-
-#: ../plugins/openbox.c:253
-msgid "horizontal"
-msgstr ""
-
-#: ../plugins/openbox.c:253
-msgid "vertical"
-msgstr ""
-
-#: ../plugins/openbox.c:268
-msgid "follow"
-msgstr ""
-
-#: ../plugins/openbox.c:272 ../plugins/openbox.c:307 ../plugins/openbox.c:319
-msgid "top"
-msgstr ""
-
-#: ../plugins/openbox.c:272 ../plugins/openbox.c:308 ../plugins/openbox.c:319
-msgid "bottom"
-msgstr ""
-
-#: ../plugins/openbox.c:273
-msgid "topleft"
-msgstr ""
-
-#: ../plugins/openbox.c:273
-msgid "topright"
-msgstr ""
-
-#: ../plugins/openbox.c:273
-msgid "bottomleft"
-msgstr ""
-
-#: ../plugins/openbox.c:274
-msgid "bottomright"
-msgstr ""
-
-#: ../plugins/openbox.c:277
-msgid "edge"
-msgstr ""
-
-#: ../plugins/openbox.c:283
-msgid "prev"
-msgstr ""
-
-#: ../plugins/openbox.c:288
-msgid "width"
-msgstr ""
-
-#: ../plugins/openbox.c:289
-msgid "height"
-msgstr ""
-
-#: ../plugins/openbox.c:319
-msgid "normal"
-msgstr ""
-
-#: ../plugins/openbox.c:322
-msgid "layer"
-msgstr ""
-
-#. global actions
-#: ../plugins/openbox.c:328
-msgid "Execute"
-msgstr ""
-
-#: ../plugins/openbox.c:329
-msgid "ShowMenu"
-msgstr ""
-
-#: ../plugins/openbox.c:330
-msgid "NextWindow"
-msgstr ""
-
-#: ../plugins/openbox.c:331
-msgid "PreviousWindow"
-msgstr ""
-
-#: ../plugins/openbox.c:332
-msgid "DirectionalCycleWindows"
-msgstr ""
-
-#: ../plugins/openbox.c:333
-msgid "DirectionalTargetWindow"
-msgstr ""
-
-#: ../plugins/openbox.c:334
-msgid "GoToDesktop"
-msgstr ""
-
-#: ../plugins/openbox.c:335
-msgid "AddDesktop"
-msgstr ""
-
-#: ../plugins/openbox.c:336
-msgid "RemoveDesktop"
-msgstr ""
-
-#: ../plugins/openbox.c:337
-msgid "ToggleDockAutohide"
-msgstr ""
-
-#: ../plugins/openbox.c:338
-msgid "Reconfigure"
-msgstr ""
-
-#: ../plugins/openbox.c:339
-msgid "Restart"
-msgstr ""
-
-#: ../plugins/openbox.c:340
-msgid "Exit"
-msgstr ""
-
-#. windows actions
-#: ../plugins/openbox.c:342
-msgid "Focus"
-msgstr ""
-
-#: ../plugins/openbox.c:343
-msgid "Raise"
-msgstr ""
-
-#: ../plugins/openbox.c:344
-msgid "Lower"
-msgstr ""
-
-#: ../plugins/openbox.c:345
-msgid "RaiseLower"
-msgstr ""
-
-#: ../plugins/openbox.c:346
-msgid "Unfocus"
-msgstr ""
-
-#: ../plugins/openbox.c:347
-msgid "FocusToBottom"
-msgstr ""
-
-#: ../plugins/openbox.c:348
-msgid "Iconify"
-msgstr ""
-
-#: ../plugins/openbox.c:349
-msgid "Close"
-msgstr ""
-
-#: ../plugins/openbox.c:350
-msgid "ToggleShade"
-msgstr ""
-
-#: ../plugins/openbox.c:351
-msgid "Shade"
-msgstr ""
-
-#: ../plugins/openbox.c:352
-msgid "Unshade"
-msgstr ""
-
-#: ../plugins/openbox.c:353
-msgid "ToggleOmnipresent"
-msgstr ""
-
-#: ../plugins/openbox.c:354
-msgid "ToggleMaximize"
-msgstr ""
-
-#: ../plugins/openbox.c:355
-msgid "Maximize"
-msgstr ""
-
-#: ../plugins/openbox.c:356
-msgid "Unmaximize"
-msgstr ""
-
-#: ../plugins/openbox.c:357
-msgid "ToggleFullscreen"
-msgstr ""
-
-#: ../plugins/openbox.c:358
-msgid "ToggleDecorations"
-msgstr ""
-
-#: ../plugins/openbox.c:359
-msgid "Decorate"
-msgstr ""
-
-#: ../plugins/openbox.c:360
-msgid "Undecorate"
-msgstr ""
-
-#: ../plugins/openbox.c:361
-msgid "SendToDesktop"
-msgstr ""
-
-#: ../plugins/openbox.c:362
-msgid "Move"
-msgstr ""
-
-#: ../plugins/openbox.c:363
-msgid "Resize"
-msgstr ""
-
-#: ../plugins/openbox.c:364
-msgid "MoveResizeTo"
-msgstr ""
-
-#: ../plugins/openbox.c:365
-msgid "MoveRelative"
-msgstr ""
-
-#: ../plugins/openbox.c:366
-msgid "ResizeRelative"
-msgstr ""
-
-#: ../plugins/openbox.c:367
-msgid "MoveToEdge"
-msgstr ""
-
-#: ../plugins/openbox.c:368
-msgid "GrowToEdge"
-msgstr ""
-
-#: ../plugins/openbox.c:369
-msgid "ShrinkToEdge"
-msgstr ""
-
-#: ../plugins/openbox.c:370
-msgid "GrowToFill"
-msgstr ""
-
-#: ../plugins/openbox.c:371
-msgid "ToggleAlwaysOnTop"
-msgstr ""
-
-#: ../plugins/openbox.c:372
-msgid "ToggleAlwaysOnBottom"
-msgstr ""
-
-#: ../plugins/openbox.c:373
-msgid "SendToLayer"
-msgstr ""
-
-#: ../plugins/openbox.c:455
-msgid "Duplicate <keyboard> section in the rc.xml file."
-msgstr ""
-
-#: ../plugins/openbox.c:480 ../plugins/openbox.c:486 ../plugins/openbox.c:721
-msgid "Internal error."
-msgstr ""
-
-#: ../plugins/openbox.c:529
-msgid "rc.xml error: no key is set for a keybind."
-msgstr ""
-
-#: ../plugins/openbox.c:600
-msgid "Invalid rc.xml: action with a sub-action."
-msgstr ""
-
-#: ../plugins/openbox.c:710
-msgid "rc.xml error: no name is set for action."
-msgstr ""
-
-#: ../plugins/openbox.c:740
-#, c-format
-msgid "rc.xml error: empty tag <%s> is prohibited."
-msgstr ""
-
-#: ../plugins/openbox.c:829
-msgid "Could not find the rc.xml file anywhere."
-msgstr ""
-
-#: ../plugins/openbox.c:875 ../plugins/openbox.c:988 ../plugins/openbox.c:1132
-#: ../plugins/openbox.c:1150
-msgid "No WM configuration is available."
-msgstr ""
-
-#: ../plugins/openbox.c:992
-msgid "Keybinding should activate at least one action."
-msgstr ""
-
-#: ../plugins/openbox.c:1014 ../plugins/openbox.c:1024
-#: ../plugins/openbox.c:1176 ../plugins/openbox.c:1186
-#, c-format
-msgid "Hotkey '%s' is already bound to an action."
-msgstr ""
-
-#: ../plugins/openbox.c:1154
-msgid "The exec line cannot be empty."
-msgstr ""
index 20381df..6900326 100644 (file)
@@ -1,16 +1,16 @@
 ## Process this file with automake to produce Makefile.in
 
-bin_PROGRAMS = lxkeys
+bin_PROGRAMS = lxhotkey
 
-lxkeys_CPPFLAGS = \
+lxhotkey_CPPFLAGS = \
        -I$(top_srcdir) \
-       -DPACKAGE_PLUGINS_DIR=\""$(libdir)/lxkeys"\" \
+       -DPACKAGE_PLUGINS_DIR=\""$(libdir)/lxhotkey"\" \
        -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
        $(PACKAGE_CFLAGS)
 
-lxkeys_SOURCES = lxkeys.c
+lxhotkey_SOURCES = lxhotkey.c
 
-lxkeys_LDADD = $(PACKAGE_LIBS)
+lxhotkey_LDADD = $(PACKAGE_LIBS)
 
-lxkeys_includedir = $(includedir)/lxkeys
-lxkeys_include_HEADERS = lxkeys.h
+lxhotkey_includedir = $(includedir)/lxhotkey
+lxhotkey_include_HEADERS = lxhotkey.h
diff --git a/src/lxhotkey.c b/src/lxhotkey.c
new file mode 100644 (file)
index 0000000..732fb64
--- /dev/null
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * This file is a part of LXHotkey project.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "lxhotkey.h"
+
+#include <glib/gi18n.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_LIBUNISTRING
+# include <unistdio.h>
+# define ulc_printf(...) ulc_fprintf(stdout,__VA_ARGS__)
+#else
+# define ulc_printf printf
+#endif
+
+/* for errors */
+static GQuark LXKEYS_ERROR;
+typedef enum {
+    LXKEYS_BAD_ARGS, /* invalid commandline arguments */
+    LXKEYS_NOT_SUPPORTED /* operation not supported */
+} LXHotkeyError;
+
+/* simple functions to manage LXHotkeyAttr data type */
+static inline LXHotkeyAttr *lxhotkey_attr_new(void)
+{
+    return g_slice_new0(LXHotkeyAttr);
+}
+
+#define free_actions(acts) g_list_free_full(acts, (GDestroyNotify)lkxeys_attr_free)
+
+static void lkxeys_attr_free(LXHotkeyAttr *data)
+{
+    g_free(data->name);
+    g_list_free_full(data->values, g_free);
+    free_actions(data->subopts);
+    g_slice_free(LXHotkeyAttr, data);
+}
+
+#define NONULL(a) (a) ? ((char *)a) : ""
+
+/* this function is taken from wmctrl utility */
+#define MAX_PROPERTY_VALUE_LEN 4096
+
+static gchar *get_property (Display *disp, Window win, /*{{{*/
+        Atom xa_prop_type, gchar *prop_name, unsigned long *size)
+{
+    Atom xa_prop_name;
+    Atom xa_ret_type;
+    int ret_format;
+    unsigned long ret_nitems;
+    unsigned long ret_bytes_after;
+    unsigned long tmp_size;
+    unsigned char *ret_prop;
+    gchar *ret;
+
+    xa_prop_name = XInternAtom(disp, prop_name, False);
+
+    /* MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
+     *
+     * long_length = Specifies the length in 32-bit multiples of the
+     *               data to be retrieved.
+     *
+     * NOTE:  see
+     * http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
+     * In particular:
+     *
+     * When the X window system was ported to 64-bit architectures, a
+     * rather peculiar design decision was made. 32-bit quantities such
+     * as Window IDs, atoms, etc, were kept as longs in the client side
+     * APIs, even when long was changed to 64 bits.
+     *
+     */
+    if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False,
+            xa_prop_type, &xa_ret_type, &ret_format,
+            &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
+        return NULL;
+    }
+
+    if (xa_ret_type != xa_prop_type) {
+        XFree(ret_prop);
+        return NULL;
+    }
+
+    /* null terminate the result to make string handling easier */
+    tmp_size = (ret_format / 8) * ret_nitems;
+    /* Correct 64 Architecture implementation of 32 bit data */
+    if(ret_format==32) tmp_size *= sizeof(long)/4;
+    ret = g_malloc(tmp_size + 1);
+    memcpy(ret, ret_prop, tmp_size);
+    ret[tmp_size] = '\0';
+
+    if (size) {
+        *size = tmp_size;
+    }
+
+    XFree(ret_prop);
+    return ret;
+} /*}}}*/
+
+static gchar *get_wm_info(void)
+{
+    /* this code is taken from wmctrl utility, adapted
+       Copyright (C) 2003, Tomas Styblo <tripie@cpan.org> */
+    Display *disp;
+    Window *sup_window = NULL;
+    gchar *wm_name = NULL;
+
+    if (!(disp = XOpenDisplay(NULL))) {
+        fputs("Cannot open display.\n", stderr);
+        return NULL;
+    }
+
+    if (!(sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
+                    XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL))) {
+        if (!(sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
+                        XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) {
+            fputs("Cannot get window manager info properties.\n"
+                  "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr);
+            return NULL;
+        }
+    }
+
+    /* WM_NAME */
+    if (!(wm_name = get_property(disp, *sup_window,
+            XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) {
+        if (!(wm_name = get_property(disp, *sup_window,
+                XA_STRING, "_NET_WM_NAME", NULL))) {
+            fputs("Cannot get name of the window manager (_NET_WM_NAME).\n", stderr);
+        } else {
+            gchar *_wm_name = wm_name;
+
+            wm_name = g_locale_to_utf8(_wm_name, -1, NULL, NULL, NULL);
+            if (wm_name)
+                g_free(_wm_name);
+            else
+                /* Cannot convert string from locale charset to UTF-8. */
+                wm_name = _wm_name;
+        }
+    }
+
+    return wm_name;
+}
+
+/* test if we are called from X which is local */
+static gboolean test_X_is_local(void)
+{
+    return TRUE; // TODO!
+}
+
+
+/* WM plugins */
+typedef struct LXHotkeyPlugin {
+    struct LXHotkeyPlugin *next;
+    gchar *name;
+    LXHotkeyPluginInit *t;
+} LXHotkeyPlugin;
+
+static LXHotkeyPlugin *plugins = NULL;
+
+FM_MODULE_DEFINE_TYPE(lxhotkey, LXHotkeyPluginInit, 1)
+static gboolean fm_module_callback_lxhotkey(const char *name, gpointer init, int ver)
+{
+    LXHotkeyPlugin *plugin;
+
+    /* ignore ver for now, only 1 exists */
+    /* FIXME: need to check for duplicates? */
+    plugin = g_new(LXHotkeyPlugin, 1);
+    plugin->next = plugins;
+    plugin->name = g_strdup(name);
+    plugin->t = init;
+    plugins = plugin;
+    return TRUE;
+}
+
+
+/* GUI plugins */
+typedef struct LXHotkeyGUIPlugin {
+    struct LXHotkeyGUIPlugin *next;
+    gchar *name;
+    LXHotkeyGUIPluginInit *t;
+} LXHotkeyGUIPlugin;
+
+static LXHotkeyGUIPlugin *gui_plugins = NULL;
+
+FM_MODULE_DEFINE_TYPE(lxhotkey_gui, LXHotkeyGUIPluginInit, 1)
+static gboolean fm_module_callback_lxhotkey_gui(const char *name, gpointer init, int ver)
+{
+    LXHotkeyGUIPlugin *plugin;
+
+    /* ignore ver for now, only 1 exists */
+    /* FIXME: need to check for duplicates? */
+    plugin = g_new(LXHotkeyGUIPlugin, 1);
+    plugin->next = gui_plugins;
+    plugin->name = g_strdup(name);
+    plugin->t = init;
+    gui_plugins = plugin;
+    return TRUE;
+}
+
+
+static int _print_help(const char *cmd)
+{
+    printf(_("Usage: %s global [<action>]      - show keys bound to action(s)\n"), cmd);
+    printf(_("       %s global <action> <key>  - bind a key to the action\n"), cmd);
+    printf(_("       %s app [<exec>]           - show keys bound to exec line\n"), cmd);
+    printf(_("       %s app <exec> <key>       - bind a key to some exec line\n"), cmd);
+    printf(_("       %s app <exec> --          - unbind all keys from exec line\n"), cmd);
+    printf(_("       %s show <key>             - show the action bound to a key\n"), cmd);
+    printf(_("       %s --gui=<type>           - start with GUI\n"), cmd);
+    return 0;
+}
+
+/* convert text line to LXHotkeyAttr list
+   text may be formatted like this:
+   startupnotify=yes:attr1=val1:attr2=val2&action=val
+   any ':','&','\' in any value should be escaped with '\' */
+static GList *actions_from_str(const char *line, GError **error)
+{
+    GString *str = g_string_sized_new(16);
+    LXHotkeyAttr *data = NULL, *attr = NULL;
+    GList *list;
+
+    data = g_new0(LXHotkeyAttr, 1);
+    list = g_list_prepend(NULL, data);
+    for (; *line; line++) {
+        switch (*line) {
+        case '=':
+            if (!data->name) { /* this is new data value */
+                if (str->len == 0)
+                    goto _empty_name;
+                data->name = g_strdup(str->str);
+                g_string_truncate(str, 0);
+            } else if (attr && !attr->name) { /* this is an option */
+                if (str->len == 0)
+                    goto _empty_opt;
+                attr->name = g_strdup(str->str);
+                g_string_truncate(str, 0);
+            } else /* '=' in value, continue processing */
+                g_string_append_c(str, *line);
+            break;
+        case ':':
+            if (!data->name) {
+                if (str->len == 0) /* empty action name */
+                    goto _empty_name;
+                data->name = g_strdup(str->str);
+            } else if (attr) { /* finish previous attr */
+                if (!attr->name) {
+                    if (str->len == 0)
+                        goto _empty_opt;
+                    attr->name = g_strdup(str->str);
+                } else
+                    attr->values = g_list_prepend(NULL, g_strdup(str->str));
+            } else /* got value for the action */
+                data->values = g_list_prepend(NULL, g_strdup(str->str));
+            g_string_truncate(str, 0);
+            attr = g_new0(LXHotkeyAttr, 1);
+            data->subopts = g_list_prepend(data->subopts, attr);
+            break;
+        case '&':
+            if (!data->name) {
+                if (str->len == 0) /* empty action name */
+                    goto _empty_name;
+                data->name = g_strdup(str->str);
+            } else if (attr) { /* finish last attr */
+                if (!attr->name) {
+                    if (str->len == 0)
+                        goto _empty_opt;
+                    attr->name = g_strdup(str->str);
+                } else
+                    attr->values = g_list_prepend(NULL, g_strdup(str->str));
+            } else /* got value for the action */
+                data->values = g_list_prepend(NULL, g_strdup(str->str));
+            g_string_truncate(str, 0);
+            attr = NULL; /* previous action just finished */
+            data->subopts = g_list_reverse(data->subopts);
+            data = g_new0(LXHotkeyAttr, 1);
+            list = g_list_prepend(list, data);
+            break;
+        case '\\':
+            /* do nothing, this was an escape char */
+            break;
+        default:
+            g_string_append_c(str, *line);
+        }
+    }
+    if (!data->name) {
+        if (str->len == 0) /* empty action name */
+            goto _empty_name;
+        data->name = g_strdup(str->str);
+    } else if (attr) { /* finish last attr */
+        if (!attr->name) {
+            if (str->len == 0)
+                goto _empty_opt;
+            attr->name = g_strdup(str->str);
+        } else
+            attr->values = g_list_prepend(NULL, g_strdup(str->str));
+    } else /* got value for the action */
+        data->values = g_list_prepend(NULL, g_strdup(str->str));
+    list = g_list_reverse(list);
+    g_string_free(str, TRUE);
+    return list;
+
+_empty_opt:
+    g_set_error_literal(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS, _("empty option name."));
+    goto _failed;
+_empty_name:
+    g_set_error_literal(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS, _("empty action name."));
+_failed:
+    free_actions(list);
+    g_string_free(str, TRUE);
+    return NULL;
+}
+
+/* check if action list matches origin
+   if origin==NULL then error is possibly set already */
+static gboolean validate_actions(const GList *act, const GList *origin,
+                                 const LXHotkeyAttr *action, gchar *wm_name,
+                                 GError **error)
+{
+    const LXHotkeyAttr *data, *ordata;
+    const GList *l, *olist;
+    char *endptr;
+
+    if (!origin)
+        return FALSE;
+    if (action)
+        olist = action->subopts; /* action is ordata on recursion actually */
+    else
+        olist = origin;
+    while (act) {
+        data = act->data;
+        /* find corresponding descriptor in the origin list */
+        for (l = olist; l; l = l->next)
+            if (g_strcmp0(data->name, (ordata = l->data)->name) == 0)
+                break;
+        if (l == NULL) {
+            if (action)
+                g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
+                            _("no matching option '%s' found for action '%s'."),
+                            data->name, action->name);
+            else
+                g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
+                            _("action '%s' isn't supported by WM %s."),
+                            data->name, wm_name);
+            return FALSE;
+        }
+        /* if ordata->values isn't NULL and data->values isn't NULL
+           then it must match, ordata->values==NULL means anything matches */
+        if (data->values != NULL && ordata->values != NULL) {
+            for (l = ordata->values; l; l = l->next)
+                if (g_strcmp0(data->values->data, l->data) == 0 ||
+                    /* check for numeric value too */
+                    (strtol(data->values->data, &endptr, 10) < LONG_MAX &&
+                     ((g_strcmp0(l->data, "#") == 0 && endptr[0] == '\0') ||
+                      (g_strcmp0(l->data, "%") == 0 && endptr[0] == '%'))))
+                    break;
+            if (l == NULL) {
+                if (action)
+                    g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
+                                _("value '%s' is not supported for option '%s'."),
+                                (char *)data->values->data, data->name);
+                else
+                    g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
+                                _("value '%s' is not supported for action '%s'."),
+                                (char *)data->values->data, data->name);
+                return FALSE;
+            }
+        }
+        /* for each data->subopts do recursion against ordata->subopts */
+        if (!data->subopts) ;
+        else if (ordata->has_actions) {
+            /* test against origin actions list, not suboptions */
+            if (!validate_actions(data->subopts, origin, NULL, wm_name, error))
+                return FALSE;
+        } else if (!ordata->subopts) {
+            g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
+                        _("action '%s' does not support options."), data->name);
+            return FALSE;
+        } else if (!validate_actions(data->subopts, origin, ordata, wm_name, error))
+            return FALSE;
+        act = act->next;
+    }
+    return TRUE;
+}
+
+/* convert list to a text line */
+//static char *actions_to_str(const GList *act)
+//{
+//}
+
+static void print_suboptions(GList *sub, int indent)
+{
+    indent += 3;
+    while (sub) {
+        LXHotkeyAttr *action = sub->data;
+        if (action->values && action->values->data)
+            printf("%*s%s=%s\n", indent, "", _(action->name),
+                                  _(action->values->data));
+        else
+            printf("%*s%s\n", indent, "", _(action->name));
+        print_suboptions(action->subopts, indent);
+        sub = sub->next;
+    }
+}
+
+
+int main(int argc, char *argv[])
+{
+    const char *cmd;
+    gchar *wm_name;
+    LXHotkeyPlugin *plugin;
+    LXHotkeyGUIPlugin *gui_plugin = NULL;
+    int ret = 1; /* failure */
+    gpointer config = NULL;
+    GError *error = NULL;
+    gboolean do_gui = FALSE;
+
+    /* init localizations */
+    setlocale(LC_ALL, "");
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+    textdomain(GETTEXT_PACKAGE);
+#endif
+
+    /* parse args first, show help if "help" "-h" or "--help" */
+    if (argc < 2)
+        cmd = "--gui=gtk";
+    else
+        cmd = argv[1];
+    if (cmd[0] == '-' && cmd[1] == '-') /* skip leading "--" if given */
+        cmd += 2;
+    if (strcmp(cmd, "help") == 0 || (cmd[0] == '-' && cmd[1] == 'h'))
+        return _print_help(argv[0]);
+    if (memcmp(cmd, "gui=", 4) == 0) {
+        do_gui = TRUE;
+        cmd += 4;
+    }
+
+    if (!test_X_is_local()) {
+        fprintf(stderr, _("LXHotkey: sorry, cannot configure keys remotely.\n"));
+        return 1;
+    }
+
+    /* init LibFM and FmModule */
+    fm_init(NULL);
+    fm_modules_add_directory(PACKAGE_PLUGINS_DIR);
+    fm_module_register_lxhotkey();
+    if (do_gui)
+        fm_module_register_lxhotkey_gui();
+
+    LXKEYS_ERROR = g_quark_from_static_string("lxhotkey-error");
+
+    /* detect current WM and find a module for it */
+    wm_name = get_wm_info();
+    if (!wm_name)
+        goto _exit;
+    CHECK_MODULES();
+    for (plugin = plugins; plugin; plugin = plugin->next)
+        if (g_ascii_strcasecmp(plugin->name, wm_name) == 0)
+            break;
+    if (do_gui) /* load GUI plugin if requested */
+        for (gui_plugin = gui_plugins; gui_plugin; gui_plugin = gui_plugin->next)
+            if (g_ascii_strcasecmp(gui_plugin->name, cmd) == 0)
+                break;
+    if (!plugin) {
+        g_set_error(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
+                    _("Window manager %s isn't supported now, sorry."), wm_name);
+        goto _exit;
+    }
+
+    /* load the found module */
+    config = plugin->t->load(NULL, &error);
+    if (!config) {
+        g_prefix_error(&error, _("Problems loading configuration: "));
+        goto _exit;
+    }
+
+    if (do_gui) {
+        if (gui_plugin && gui_plugin->t->run)
+            gui_plugin->t->run(wm_name, plugin->t, config, &error);
+        else
+            g_set_error(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
+                        _("GUI type %s currently isn't supported."), cmd);
+        goto _exit;
+    }
+
+    /* doing commandline: call module function depending on args */
+    if (strcmp(argv[1], "global") == 0) { /* lxhotkey global ... */
+        if (argc > 3) { /* set */
+            LXHotkeyGlobal data;
+
+            if (plugin->t->set_wm_key == NULL)
+                goto _not_supported;
+            /* parse and validate actions */
+            data.actions = actions_from_str(argv[2], &error);
+            if (error ||
+                (plugin->t->get_wm_actions != NULL &&
+                 !validate_actions(data.actions, plugin->t->get_wm_actions(config, &error),
+                                   NULL, wm_name, &error))) { /* invalid request */
+                g_prefix_error(&error, _("Invalid request: "));
+                goto _exit;
+            }
+            // FIXME: validate key
+            data.accel1 = argv[3];
+            data.accel2 = NULL;
+            if (argc > 4)
+                data.accel2 = argv[4];
+            if (!plugin->t->set_wm_key(config, &data, &error) ||
+                !plugin->t->save(config, &error)) {
+                g_prefix_error(&error, _("Problems saving configuration: "));
+                free_actions(data.actions);
+                goto _exit;
+            }
+            free_actions(data.actions);
+        } else { /* show by mask */
+            const char *mask = NULL;
+            GList *keys, *key;
+            LXHotkeyGlobal *data;
+            GList *act;
+            LXHotkeyAttr *action;
+
+            if (plugin->t->get_wm_keys == NULL)
+                goto _not_supported;
+            if (argc > 2)
+                mask = argv[2]; /* mask given */
+            keys = plugin->t->get_wm_keys(config, mask, NULL);
+            ulc_printf(" %-24s %s\n", _("ACTION(s)"), _("KEY(s)"));
+            for (key = keys; key; key = key->next) {
+                data = key->data;
+                for (act = data->actions; act; act = act->next)
+                {
+                    action = act->data;
+                    if (act != data->actions)
+                        printf("+ %s\n", _(action->name));
+                    else if (data->accel2)
+                        ulc_printf("%-24s %s %s\n", _(action->name), data->accel1,
+                                                    data->accel2);
+                    else
+                        ulc_printf("%-24s %s\n", _(action->name), data->accel1);
+                    print_suboptions(action->subopts, 0);
+                }
+            }
+            g_list_free(keys);
+        }
+    } else if (strcmp(argv[1], "app") == 0) { /* lxhotkey app ... */
+        if (argc > 3) { /* set */
+            GList *keys = NULL;
+            LXHotkeyApp data;
+
+            if (plugin->t->set_app_key == NULL)
+                goto _not_supported;
+            /* check if exec already has a key */
+            if (plugin->t->get_app_keys != NULL)
+                keys = plugin->t->get_app_keys(config, argv[2], NULL);
+            if (keys && keys->next) /* mask in exec line isn't supported */
+                goto _not_supported;
+            // FIXME: validate key
+            data.accel2 = NULL;
+            if (strcmp(argv[3], "--") == 0) { /* remove all bindings */
+                data.accel1 = NULL;
+            } else if (keys && ((LXHotkeyApp *)keys->data)->accel1) {
+                data.accel1 = ((LXHotkeyApp *)keys->data)->accel1;
+                data.accel2 = argv[3];
+            } else {
+                data.accel1 = argv[3];
+            }
+            g_list_free(keys);
+            cmd = strchr(argv[2], '&');
+            if (cmd) {
+                data.options = actions_from_str(&cmd[1], &error);
+                if (error ||
+                    (plugin->t->get_app_options != NULL &&
+                     !validate_actions(data.options,
+                                       plugin->t->get_app_options(config, &error),
+                                       NULL, wm_name, &error))) { /* invalid request */
+                    g_prefix_error(&error, _("Invalid request: "));
+                    free_actions(data.options);
+                    goto _exit;
+                }
+                data.exec = g_strndup(argv[2], cmd - argv[2]);
+            } else {
+                data.options = NULL;
+                data.exec = g_strdup(argv[2]);
+            }
+            // FIXME: validate exec
+            if (!plugin->t->set_app_key(config, &data, &error) ||
+                !plugin->t->save(config, &error)) {
+                g_prefix_error(&error, _("Problems saving configuration: "));
+                free_actions(data.options);
+                g_free(data.exec);
+                goto _exit;
+            }
+            free_actions(data.options);
+            g_free(data.exec);
+        } else { /* show by mask */
+            const char *mask = NULL;
+            GList *keys, *key;
+            LXHotkeyApp *data;
+
+            if (plugin->t->get_app_keys == NULL)
+                goto _not_supported;
+            if (argc > 2)
+                mask = argv[2]; /* mask given */
+            keys = plugin->t->get_app_keys(config, mask, NULL);
+            ulc_printf(" %-48s %s\n", _("EXEC"), _("KEY(s)"));
+            for (key = keys; key; key = key->next) {
+                data = key->data;
+                if (data->accel2)
+                    ulc_printf("%-48s %s %s\n", data->exec, data->accel1,
+                                                data->accel2);
+                else
+                    ulc_printf("%-48s %s\n", data->exec, data->accel1);
+                print_suboptions(data->options, 0);
+            }
+            g_list_free(keys);
+        }
+    } else if (strcmp(argv[1], "show") == 0) { /* lxhotkey show ... */
+        // TODO!
+    } else
+        goto _exit;
+    ret = 0; /* success */
+    goto _exit;
+
+_not_supported:
+    g_set_error_literal(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
+                        _("Requested operation isn't supported."));
+
+    /* release resources */
+_exit:
+    if (config)
+        plugin->t->free(config);
+    if (error) {
+        /* if do_gui then show an alert window instead of stderr */
+        if (gui_plugin && gui_plugin->t->alert)
+            gui_plugin->t->alert(error);
+        else
+            fprintf(stderr, "LXHotkey: %s\n", error->message);
+        g_error_free(error);
+    }
+    fm_module_unregister_type("lxhotkey");
+    if (do_gui)
+        fm_module_unregister_type("lxhotkey_gui");
+    while (plugins) {
+        plugin = plugins;
+        plugins = plugin->next;
+        g_free(plugin->name);
+        g_free(plugin);
+    }
+    while (gui_plugins) {
+        gui_plugin = gui_plugins;
+        gui_plugins = gui_plugin->next;
+        g_free(gui_plugin->name);
+        g_free(gui_plugin);
+    }
+    g_free(wm_name);
+
+    return ret;
+}
diff --git a/src/lxhotkey.h b/src/lxhotkey.h
new file mode 100644 (file)
index 0000000..9ea6ece
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * This file is a part of LXHotkey project.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LXKEYS_H_
+#define _LXKEYS_H_ 1
+
+#include <libfm/fm.h>
+
+G_BEGIN_DECLS
+
+/**
+ * LXHotkeyAttr:
+ * @name: action or option name
+ * @values: (element-type char *): option value
+ * @subopts: (element-type LXHotkeyAttr): (allow-none): list of suboptions
+ * @has_actions: %TRUE if @subopts contains actions, %FALSE if @subopts contains options
+ *
+ * Data descriptor for actions and options. Actions are ativated by keybinding.
+ * Each action may contain arbitrary number of options that alter its execution.
+ *
+ * This data is also used in a result on LXHotkeyPluginInit:get_wm_actions() or
+ * LXHotkeyPluginInit:get_app_options() call. In that case for each option the
+ * @values list should be either %NULL if option accepts any value, or list of
+ * acceptable values for the option ("#" value has a special meaning: integer
+ * value matches it, the same as "%" for percent value). For such purpose it is
+ * advisable to make @name and @values translateable constants because GUI might
+ * represent them in target locale which might be convenient for users.
+ */
+typedef struct {
+    gchar *name;
+    GList *values;
+    GList *subopts;
+    gboolean has_actions;
+} LXHotkeyAttr;
+
+/**
+ * LXHotkeyGlobal:
+ * @actions: (element-type LXHotkeyAttr): list of actions
+ * @accel1: a keybinding to activate @actions, in GDK accelerator format
+ * @accel2: optional alternate keybinding to activate @actions
+ * @data1: a pointer for using by WM plugin
+ * @data2: a pointer for using by WM plugin
+ *
+ * Descriptor of a keybinding which isn't a single command line action.
+ * The keybinding string is in format that looks like "<Control>a" or
+ * "<Shift><Alt>F1" or "<Release>z" (note that not each WM supports last one
+ * variant).
+ */
+typedef struct {
+    GList *actions;
+    gchar *accel1;
+    gchar *accel2;
+    gpointer data1;
+    gpointer data2;
+} LXHotkeyGlobal;
+
+/**
+ * LXHotkeyApp:
+ * @exec: a command line to execute
+ * @actions: (element-type LXHotkeyAttr): (allow-none): list of options
+ * @accel1: a keybinding to activate @exec, in GDK accelerator format
+ * @accel2: optional alternate keybinding to activate @exec
+ * @data1: a pointer for using by WM plugin
+ * @data2: a pointer for using by WM plugin
+ *
+ * Descriptor of a keybinding for a single command line action. The command
+ * execution may be altered by some @options. See also #LXHotkeyGlobal.
+ */
+typedef struct {
+    gchar *exec;
+    GList *options;
+    gchar *accel1;
+    gchar *accel2;
+    gpointer data1;
+    gpointer data2;
+} LXHotkeyApp;
+
+
+/* WM support plugins */
+
+#define FM_MODULE_lxhotkey_VERSION 1 /* version of this API */
+
+/**
+ * LXHotkeyPluginInit:
+ * @load: callback to (re)load bindings from WM configuration
+ * @save: callback to save bindings to WM configuration
+ * @free: callback to release allocated resources
+ * @get_wm_keys: (allow-none): callback to get global keys by provided mask
+ * @set_wm_key: (allow-none): callback to set a global key by provided data
+ * @get_wm_actions: (allow-none): callback to get global actions list provided by WM
+ * @get_app_keys: (allow-none): callback to get keys bound to commands
+ * @set_app_key: (allow-none): callback to set a key for a command
+ * @get_app_options: (allow-none): callback to get possible actions for commands
+ *
+ * Callbacks @get_wm_keys and @get_app_keys return list which should be freed
+ * by caller (transfer container).
+ * Callbacks @get_wm_actions and @get_app_actions return list that should be
+ * not modified nor freed by caller (transfer none).
+ * Callback @get_wm_keys returns list of keybindings by @mask which is a shell
+ * style pattern for keys.
+ * Callback @set_wm_key changes a keybinding for list of actions provided. If
+ * @data::accel1 is %NULL then all keybindings for @data::actions will be
+ * cleared, otherwise keybindings will be changed accordingly.
+ */
+typedef struct {
+    /*< public >*/
+    gpointer (*load)(gpointer config, GError **error);
+    gboolean (*save)(gpointer config, GError **error);
+    void (*free)(gpointer config);
+    GList *(*get_wm_keys)(gpointer config, const char *mask, GError **error);
+    gboolean (*set_wm_key)(gpointer config, LXHotkeyGlobal *data, GError **error);
+    GList *(*get_wm_actions)(gpointer config, GError **error);
+    GList *(*get_app_keys)(gpointer config, const char *mask, GError **error);
+    gboolean (*set_app_key)(gpointer config, LXHotkeyApp *data, GError **error);
+    GList *(*get_app_options)(gpointer config, GError **error);
+    /*< private >*/
+    gpointer _reserved1;
+    gpointer _reserved2;
+    gpointer _reserved3;
+} LXHotkeyPluginInit;
+
+/**
+ * This descriptor instance should be defined in each plugin code as main
+ * entry point for plugin creation. Primitive plugin example follows:
+ *
+ * #include <lxhotkey/lxhotkey.h>
+ *
+ * gpointer test_load(gpointer config, GError **error)
+ * {
+ *      if (config == NULL)
+ *          config = g_strdup("");
+ *      return config;
+ * }
+ *
+ * gboolean test_save(gpointer config, GError **error)
+ * {
+ *      return FALSE;
+ * }
+ *
+ * FM_DEFINE_MODULE(lxhotkey, NoWM)
+ *
+ * LXHotkeyPluginInit fm_module_init_lxhotkey = {
+ *      .load = test_load,
+ *      .save = test_save,
+ *      .free = g_free
+ * }
+ */
+extern LXHotkeyPluginInit fm_module_init_lxhotkey;
+
+
+/* GUI plugins */
+
+#define FM_MODULE_lxhotkey_gui_VERSION 1 /* version of this API */
+
+/**
+ * LXHotkeyGUIPluginInit:
+ * @run: callback to run GUI
+ * @alert: callback to show an error message
+ *
+ * The @run callback receives name of WM, pointer to callbacks, and pointer to
+ * the config data which is already succesfully loaded and ready to use.
+ */
+typedef struct {
+    /*< public >*/
+    void (*run)(const gchar *wm, const LXHotkeyPluginInit *cb, gpointer config, GError **error);
+    void (*alert)(GError *error);
+    /*< private >*/
+    gpointer _reserved1;
+    gpointer _reserved2;
+    gpointer _reserved3;
+} LXHotkeyGUIPluginInit;
+
+/**
+ * This descriptor instance should be defined in each plugin code as main
+ * entry point for a GUI plugin creation.
+ */
+extern LXHotkeyGUIPluginInit fm_module_init_lxhotkey_gui;
+
+G_END_DECLS
+
+#endif /* _LXKEYS_H_ */
diff --git a/src/lxkeys.c b/src/lxkeys.c
deleted file mode 100644 (file)
index 17c3226..0000000
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
- *
- * This file is a part of LXKeys project.
- *
- * This program 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 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "lxkeys.h"
-
-#include <glib/gi18n.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-
-#include <stdlib.h>
-
-#ifdef HAVE_LIBUNISTRING
-# include <unistdio.h>
-# define ulc_printf(...) ulc_fprintf(stdout,__VA_ARGS__)
-#else
-# define ulc_printf printf
-#endif
-
-/* for errors */
-static GQuark LXKEYS_ERROR;
-typedef enum {
-    LXKEYS_BAD_ARGS, /* invalid commandline arguments */
-    LXKEYS_NOT_SUPPORTED /* operation not supported */
-} LXKeysError;
-
-/* simple functions to manage LXKeysAttr data type */
-static inline LXKeysAttr *lxkeys_attr_new(void)
-{
-    return g_slice_new0(LXKeysAttr);
-}
-
-#define free_actions(acts) g_list_free_full(acts, (GDestroyNotify)lkxeys_attr_free)
-
-static void lkxeys_attr_free(LXKeysAttr *data)
-{
-    g_free(data->name);
-    g_list_free_full(data->values, g_free);
-    free_actions(data->subopts);
-    g_slice_free(LXKeysAttr, data);
-}
-
-#define NONULL(a) (a) ? ((char *)a) : ""
-
-/* this function is taken from wmctrl utility */
-#define MAX_PROPERTY_VALUE_LEN 4096
-
-static gchar *get_property (Display *disp, Window win, /*{{{*/
-        Atom xa_prop_type, gchar *prop_name, unsigned long *size)
-{
-    Atom xa_prop_name;
-    Atom xa_ret_type;
-    int ret_format;
-    unsigned long ret_nitems;
-    unsigned long ret_bytes_after;
-    unsigned long tmp_size;
-    unsigned char *ret_prop;
-    gchar *ret;
-
-    xa_prop_name = XInternAtom(disp, prop_name, False);
-
-    /* MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
-     *
-     * long_length = Specifies the length in 32-bit multiples of the
-     *               data to be retrieved.
-     *
-     * NOTE:  see
-     * http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
-     * In particular:
-     *
-     * When the X window system was ported to 64-bit architectures, a
-     * rather peculiar design decision was made. 32-bit quantities such
-     * as Window IDs, atoms, etc, were kept as longs in the client side
-     * APIs, even when long was changed to 64 bits.
-     *
-     */
-    if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False,
-            xa_prop_type, &xa_ret_type, &ret_format,
-            &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
-        return NULL;
-    }
-
-    if (xa_ret_type != xa_prop_type) {
-        XFree(ret_prop);
-        return NULL;
-    }
-
-    /* null terminate the result to make string handling easier */
-    tmp_size = (ret_format / 8) * ret_nitems;
-    /* Correct 64 Architecture implementation of 32 bit data */
-    if(ret_format==32) tmp_size *= sizeof(long)/4;
-    ret = g_malloc(tmp_size + 1);
-    memcpy(ret, ret_prop, tmp_size);
-    ret[tmp_size] = '\0';
-
-    if (size) {
-        *size = tmp_size;
-    }
-
-    XFree(ret_prop);
-    return ret;
-} /*}}}*/
-
-static gchar *get_wm_info(void)
-{
-    /* this code is taken from wmctrl utility, adapted
-       Copyright (C) 2003, Tomas Styblo <tripie@cpan.org> */
-    Display *disp;
-    Window *sup_window = NULL;
-    gchar *wm_name = NULL;
-
-    if (!(disp = XOpenDisplay(NULL))) {
-        fputs("Cannot open display.\n", stderr);
-        return NULL;
-    }
-
-    if (!(sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
-                    XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL))) {
-        if (!(sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
-                        XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) {
-            fputs("Cannot get window manager info properties.\n"
-                  "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr);
-            return NULL;
-        }
-    }
-
-    /* WM_NAME */
-    if (!(wm_name = get_property(disp, *sup_window,
-            XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) {
-        if (!(wm_name = get_property(disp, *sup_window,
-                XA_STRING, "_NET_WM_NAME", NULL))) {
-            fputs("Cannot get name of the window manager (_NET_WM_NAME).\n", stderr);
-        } else {
-            gchar *_wm_name = wm_name;
-
-            wm_name = g_locale_to_utf8(_wm_name, -1, NULL, NULL, NULL);
-            if (wm_name)
-                g_free(_wm_name);
-            else
-                /* Cannot convert string from locale charset to UTF-8. */
-                wm_name = _wm_name;
-        }
-    }
-
-    return wm_name;
-}
-
-/* test if we are called from X which is local */
-static gboolean test_X_is_local(void)
-{
-    return TRUE; // TODO!
-}
-
-
-/* WM plugins */
-typedef struct LXKeysPlugin {
-    struct LXKeysPlugin *next;
-    gchar *name;
-    LXKeysPluginInit *t;
-} LXKeysPlugin;
-
-static LXKeysPlugin *plugins = NULL;
-
-FM_MODULE_DEFINE_TYPE(lxkeys, LXKeysPluginInit, 1)
-static gboolean fm_module_callback_lxkeys(const char *name, gpointer init, int ver)
-{
-    LXKeysPlugin *plugin;
-
-    /* ignore ver for now, only 1 exists */
-    /* FIXME: need to check for duplicates? */
-    plugin = g_new(LXKeysPlugin, 1);
-    plugin->next = plugins;
-    plugin->name = g_strdup(name);
-    plugin->t = init;
-    plugins = plugin;
-    return TRUE;
-}
-
-
-/* GUI plugins */
-typedef struct LXKeysGUIPlugin {
-    struct LXKeysGUIPlugin *next;
-    gchar *name;
-    LXKeysGUIPluginInit *t;
-} LXKeysGUIPlugin;
-
-static LXKeysGUIPlugin *gui_plugins = NULL;
-
-FM_MODULE_DEFINE_TYPE(lxkeys_gui, LXKeysGUIPluginInit, 1)
-static gboolean fm_module_callback_lxkeys_gui(const char *name, gpointer init, int ver)
-{
-    LXKeysGUIPlugin *plugin;
-
-    /* ignore ver for now, only 1 exists */
-    /* FIXME: need to check for duplicates? */
-    plugin = g_new(LXKeysGUIPlugin, 1);
-    plugin->next = gui_plugins;
-    plugin->name = g_strdup(name);
-    plugin->t = init;
-    gui_plugins = plugin;
-    return TRUE;
-}
-
-
-static int _print_help(const char *cmd)
-{
-    printf(_("Usage: %s global [<action>]      - show keys bound to action(s)\n"), cmd);
-    printf(_("       %s global <action> <key>  - bind a key to the action\n"), cmd);
-    printf(_("       %s app [<exec>]           - show keys bound to exec line\n"), cmd);
-    printf(_("       %s app <exec> <key>       - bind a key to some exec line\n"), cmd);
-    printf(_("       %s app <exec> --          - unbind all keys from exec line\n"), cmd);
-    printf(_("       %s show <key>             - show the action bound to a key\n"), cmd);
-    printf(_("       %s --gui=<type>           - start with GUI\n"), cmd);
-    return 0;
-}
-
-/* convert text line to LXKeysAttr list
-   text may be formatted like this:
-   startupnotify=yes:attr1=val1:attr2=val2&action=val
-   any ':','&','\' in any value should be escaped with '\' */
-static GList *actions_from_str(const char *line, GError **error)
-{
-    GString *str = g_string_sized_new(16);
-    LXKeysAttr *data = NULL, *attr = NULL;
-    GList *list;
-
-    data = g_new0(LXKeysAttr, 1);
-    list = g_list_prepend(NULL, data);
-    for (; *line; line++) {
-        switch (*line) {
-        case '=':
-            if (!data->name) { /* this is new data value */
-                if (str->len == 0)
-                    goto _empty_name;
-                data->name = g_strdup(str->str);
-                g_string_truncate(str, 0);
-            } else if (attr && !attr->name) { /* this is an option */
-                if (str->len == 0)
-                    goto _empty_opt;
-                attr->name = g_strdup(str->str);
-                g_string_truncate(str, 0);
-            } else /* '=' in value, continue processing */
-                g_string_append_c(str, *line);
-            break;
-        case ':':
-            if (!data->name) {
-                if (str->len == 0) /* empty action name */
-                    goto _empty_name;
-                data->name = g_strdup(str->str);
-            } else if (attr) { /* finish previous attr */
-                if (!attr->name) {
-                    if (str->len == 0)
-                        goto _empty_opt;
-                    attr->name = g_strdup(str->str);
-                } else
-                    attr->values = g_list_prepend(NULL, g_strdup(str->str));
-            } else /* got value for the action */
-                data->values = g_list_prepend(NULL, g_strdup(str->str));
-            g_string_truncate(str, 0);
-            attr = g_new0(LXKeysAttr, 1);
-            data->subopts = g_list_prepend(data->subopts, attr);
-            break;
-        case '&':
-            if (!data->name) {
-                if (str->len == 0) /* empty action name */
-                    goto _empty_name;
-                data->name = g_strdup(str->str);
-            } else if (attr) { /* finish last attr */
-                if (!attr->name) {
-                    if (str->len == 0)
-                        goto _empty_opt;
-                    attr->name = g_strdup(str->str);
-                } else
-                    attr->values = g_list_prepend(NULL, g_strdup(str->str));
-            } else /* got value for the action */
-                data->values = g_list_prepend(NULL, g_strdup(str->str));
-            g_string_truncate(str, 0);
-            attr = NULL; /* previous action just finished */
-            data->subopts = g_list_reverse(data->subopts);
-            data = g_new0(LXKeysAttr, 1);
-            list = g_list_prepend(list, data);
-            break;
-        case '\\':
-            /* do nothing, this was an escape char */
-            break;
-        default:
-            g_string_append_c(str, *line);
-        }
-    }
-    if (!data->name) {
-        if (str->len == 0) /* empty action name */
-            goto _empty_name;
-        data->name = g_strdup(str->str);
-    } else if (attr) { /* finish last attr */
-        if (!attr->name) {
-            if (str->len == 0)
-                goto _empty_opt;
-            attr->name = g_strdup(str->str);
-        } else
-            attr->values = g_list_prepend(NULL, g_strdup(str->str));
-    } else /* got value for the action */
-        data->values = g_list_prepend(NULL, g_strdup(str->str));
-    list = g_list_reverse(list);
-    g_string_free(str, TRUE);
-    return list;
-
-_empty_opt:
-    g_set_error_literal(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS, _("empty option name."));
-    goto _failed;
-_empty_name:
-    g_set_error_literal(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS, _("empty action name."));
-_failed:
-    free_actions(list);
-    g_string_free(str, TRUE);
-    return NULL;
-}
-
-/* check if action list matches origin
-   if origin==NULL then error is possibly set already */
-static gboolean validate_actions(const GList *act, const GList *origin,
-                                 const LXKeysAttr *action, gchar *wm_name,
-                                 GError **error)
-{
-    const LXKeysAttr *data, *ordata;
-    const GList *l, *olist;
-    char *endptr;
-
-    if (!origin)
-        return FALSE;
-    if (action)
-        olist = action->subopts; /* action is ordata on recursion actually */
-    else
-        olist = origin;
-    while (act) {
-        data = act->data;
-        /* find corresponding descriptor in the origin list */
-        for (l = olist; l; l = l->next)
-            if (g_strcmp0(data->name, (ordata = l->data)->name) == 0)
-                break;
-        if (l == NULL) {
-            if (action)
-                g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
-                            _("no matching option '%s' found for action '%s'."),
-                            data->name, action->name);
-            else
-                g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
-                            _("action '%s' isn't supported by WM %s."),
-                            data->name, wm_name);
-            return FALSE;
-        }
-        /* if ordata->values isn't NULL and data->values isn't NULL
-           then it must match, ordata->values==NULL means anything matches */
-        if (data->values != NULL && ordata->values != NULL) {
-            for (l = ordata->values; l; l = l->next)
-                if (g_strcmp0(data->values->data, l->data) == 0 ||
-                    /* check for numeric value too */
-                    (strtol(data->values->data, &endptr, 10) < LONG_MAX &&
-                     ((g_strcmp0(l->data, "#") == 0 && endptr[0] == '\0') ||
-                      (g_strcmp0(l->data, "%") == 0 && endptr[0] == '%'))))
-                    break;
-            if (l == NULL) {
-                if (action)
-                    g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
-                                _("value '%s' is not supported for option '%s'."),
-                                (char *)data->values->data, data->name);
-                else
-                    g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
-                                _("value '%s' is not supported for action '%s'."),
-                                (char *)data->values->data, data->name);
-                return FALSE;
-            }
-        }
-        /* for each data->subopts do recursion against ordata->subopts */
-        if (!data->subopts) ;
-        else if (ordata->has_actions) {
-            /* test against origin actions list, not suboptions */
-            if (!validate_actions(data->subopts, origin, NULL, wm_name, error))
-                return FALSE;
-        } else if (!ordata->subopts) {
-            g_set_error(error, LXKEYS_ERROR, LXKEYS_BAD_ARGS,
-                        _("action '%s' does not support options."), data->name);
-            return FALSE;
-        } else if (!validate_actions(data->subopts, origin, ordata, wm_name, error))
-            return FALSE;
-        act = act->next;
-    }
-    return TRUE;
-}
-
-/* convert list to a text line */
-//static char *actions_to_str(const GList *act)
-//{
-//}
-
-static void print_suboptions(GList *sub, int indent)
-{
-    indent += 3;
-    while (sub) {
-        LXKeysAttr *action = sub->data;
-        if (action->values && action->values->data)
-            printf("%*s%s=%s\n", indent, "", _(action->name),
-                                  _(action->values->data));
-        else
-            printf("%*s%s\n", indent, "", _(action->name));
-        print_suboptions(action->subopts, indent);
-        sub = sub->next;
-    }
-}
-
-
-int main(int argc, char *argv[])
-{
-    const char *cmd;
-    gchar *wm_name;
-    LXKeysPlugin *plugin;
-    LXKeysGUIPlugin *gui_plugin = NULL;
-    int ret = 1; /* failure */
-    gpointer config = NULL;
-    GError *error = NULL;
-    gboolean do_gui = FALSE;
-
-    /* init localizations */
-    setlocale(LC_ALL, "");
-#ifdef ENABLE_NLS
-    bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
-    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
-    textdomain(GETTEXT_PACKAGE);
-#endif
-
-    /* parse args first, show help if "help" "-h" or "--help" */
-    if (argc < 2)
-        cmd = "--gui=gtk";
-    else
-        cmd = argv[1];
-    if (cmd[0] == '-' && cmd[1] == '-') /* skip leading "--" if given */
-        cmd += 2;
-    if (strcmp(cmd, "help") == 0 || (cmd[0] == '-' && cmd[1] == 'h'))
-        return _print_help(argv[0]);
-    if (memcmp(cmd, "gui=", 4) == 0) {
-        do_gui = TRUE;
-        cmd += 4;
-    }
-
-    if (!test_X_is_local()) {
-        fprintf(stderr, _("LXKeys: sorry, cannot configure keys remotely.\n"));
-        return 1;
-    }
-
-    /* init LibFM and FmModule */
-    fm_init(NULL);
-    fm_modules_add_directory(PACKAGE_PLUGINS_DIR);
-    fm_module_register_lxkeys();
-    if (do_gui)
-        fm_module_register_lxkeys_gui();
-
-    LXKEYS_ERROR = g_quark_from_static_string("lxkeys-error");
-
-    /* detect current WM and find a module for it */
-    wm_name = get_wm_info();
-    if (!wm_name)
-        goto _exit;
-    CHECK_MODULES();
-    for (plugin = plugins; plugin; plugin = plugin->next)
-        if (g_ascii_strcasecmp(plugin->name, wm_name) == 0)
-            break;
-    if (do_gui) /* load GUI plugin if requested */
-        for (gui_plugin = gui_plugins; gui_plugin; gui_plugin = gui_plugin->next)
-            if (g_ascii_strcasecmp(gui_plugin->name, cmd) == 0)
-                break;
-    if (!plugin) {
-        g_set_error(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
-                    _("Window manager %s isn't supported now, sorry."), wm_name);
-        goto _exit;
-    }
-
-    /* load the found module */
-    config = plugin->t->load(NULL, &error);
-    if (!config) {
-        g_prefix_error(&error, _("Problems loading configuration: "));
-        goto _exit;
-    }
-
-    if (do_gui) {
-        if (gui_plugin && gui_plugin->t->run)
-            gui_plugin->t->run(wm_name, plugin->t, config, &error);
-        else
-            g_set_error(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
-                        _("GUI type %s currently isn't supported."), cmd);
-        goto _exit;
-    }
-
-    /* doing commandline: call module function depending on args */
-    if (strcmp(argv[1], "global") == 0) { /* lxkeys global ... */
-        if (argc > 3) { /* set */
-            LXKeysGlobal data;
-
-            if (plugin->t->set_wm_key == NULL)
-                goto _not_supported;
-            /* parse and validate actions */
-            data.actions = actions_from_str(argv[2], &error);
-            if (error ||
-                (plugin->t->get_wm_actions != NULL &&
-                 !validate_actions(data.actions, plugin->t->get_wm_actions(config, &error),
-                                   NULL, wm_name, &error))) { /* invalid request */
-                g_prefix_error(&error, _("Invalid request: "));
-                goto _exit;
-            }
-            // FIXME: validate key
-            data.accel1 = argv[3];
-            data.accel2 = NULL;
-            if (argc > 4)
-                data.accel2 = argv[4];
-            if (!plugin->t->set_wm_key(config, &data, &error) ||
-                !plugin->t->save(config, &error)) {
-                g_prefix_error(&error, _("Problems saving configuration: "));
-                free_actions(data.actions);
-                goto _exit;
-            }
-            free_actions(data.actions);
-        } else { /* show by mask */
-            const char *mask = NULL;
-            GList *keys, *key;
-            LXKeysGlobal *data;
-            GList *act;
-            LXKeysAttr *action;
-
-            if (plugin->t->get_wm_keys == NULL)
-                goto _not_supported;
-            if (argc > 2)
-                mask = argv[2]; /* mask given */
-            keys = plugin->t->get_wm_keys(config, mask, NULL);
-            ulc_printf(" %-24s %s\n", _("ACTION(s)"), _("KEY(s)"));
-            for (key = keys; key; key = key->next) {
-                data = key->data;
-                for (act = data->actions; act; act = act->next)
-                {
-                    action = act->data;
-                    if (act != data->actions)
-                        printf("+ %s\n", _(action->name));
-                    else if (data->accel2)
-                        ulc_printf("%-24s %s %s\n", _(action->name), data->accel1,
-                                                    data->accel2);
-                    else
-                        ulc_printf("%-24s %s\n", _(action->name), data->accel1);
-                    print_suboptions(action->subopts, 0);
-                }
-            }
-            g_list_free(keys);
-        }
-    } else if (strcmp(argv[1], "app") == 0) { /* lxkeys app ... */
-        if (argc > 3) { /* set */
-            GList *keys = NULL;
-            LXKeysApp data;
-
-            if (plugin->t->set_app_key == NULL)
-                goto _not_supported;
-            /* check if exec already has a key */
-            if (plugin->t->get_app_keys != NULL)
-                keys = plugin->t->get_app_keys(config, argv[2], NULL);
-            if (keys && keys->next) /* mask in exec line isn't supported */
-                goto _not_supported;
-            // FIXME: validate key
-            data.accel2 = NULL;
-            if (strcmp(argv[3], "--") == 0) { /* remove all bindings */
-                data.accel1 = NULL;
-            } else if (keys && ((LXKeysApp *)keys->data)->accel1) {
-                data.accel1 = ((LXKeysApp *)keys->data)->accel1;
-                data.accel2 = argv[3];
-            } else {
-                data.accel1 = argv[3];
-            }
-            g_list_free(keys);
-            cmd = strchr(argv[2], '&');
-            if (cmd) {
-                data.options = actions_from_str(&cmd[1], &error);
-                if (error ||
-                    (plugin->t->get_app_options != NULL &&
-                     !validate_actions(data.options,
-                                       plugin->t->get_app_options(config, &error),
-                                       NULL, wm_name, &error))) { /* invalid request */
-                    g_prefix_error(&error, _("Invalid request: "));
-                    free_actions(data.options);
-                    goto _exit;
-                }
-                data.exec = g_strndup(argv[2], cmd - argv[2]);
-            } else {
-                data.options = NULL;
-                data.exec = g_strdup(argv[2]);
-            }
-            // FIXME: validate exec
-            if (!plugin->t->set_app_key(config, &data, &error) ||
-                !plugin->t->save(config, &error)) {
-                g_prefix_error(&error, _("Problems saving configuration: "));
-                free_actions(data.options);
-                g_free(data.exec);
-                goto _exit;
-            }
-            free_actions(data.options);
-            g_free(data.exec);
-        } else { /* show by mask */
-            const char *mask = NULL;
-            GList *keys, *key;
-            LXKeysApp *data;
-
-            if (plugin->t->get_app_keys == NULL)
-                goto _not_supported;
-            if (argc > 2)
-                mask = argv[2]; /* mask given */
-            keys = plugin->t->get_app_keys(config, mask, NULL);
-            ulc_printf(" %-48s %s\n", _("EXEC"), _("KEY(s)"));
-            for (key = keys; key; key = key->next) {
-                data = key->data;
-                if (data->accel2)
-                    ulc_printf("%-48s %s %s\n", data->exec, data->accel1,
-                                                data->accel2);
-                else
-                    ulc_printf("%-48s %s\n", data->exec, data->accel1);
-                print_suboptions(data->options, 0);
-            }
-            g_list_free(keys);
-        }
-    } else if (strcmp(argv[1], "show") == 0) { /* lxkeys show ... */
-        // TODO!
-    } else
-        goto _exit;
-    ret = 0; /* success */
-    goto _exit;
-
-_not_supported:
-    g_set_error_literal(&error, LXKEYS_ERROR, LXKEYS_NOT_SUPPORTED,
-                        _("Requested operation isn't supported."));
-
-    /* release resources */
-_exit:
-    if (config)
-        plugin->t->free(config);
-    if (error) {
-        /* if do_gui then show an alert window instead of stderr */
-        if (gui_plugin && gui_plugin->t->alert)
-            gui_plugin->t->alert(error);
-        else
-            fprintf(stderr, "LXKeys: %s\n", error->message);
-        g_error_free(error);
-    }
-    fm_module_unregister_type("lxkeys");
-    if (do_gui)
-        fm_module_unregister_type("lxkeys_gui");
-    while (plugins) {
-        plugin = plugins;
-        plugins = plugin->next;
-        g_free(plugin->name);
-        g_free(plugin);
-    }
-    while (gui_plugins) {
-        gui_plugin = gui_plugins;
-        gui_plugins = gui_plugin->next;
-        g_free(gui_plugin->name);
-        g_free(gui_plugin);
-    }
-    g_free(wm_name);
-
-    return ret;
-}
diff --git a/src/lxkeys.h b/src/lxkeys.h
deleted file mode 100644 (file)
index 68fcfb2..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
- *
- * This file is a part of LXKeys project.
- *
- * This program 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 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef _LXKEYS_H_
-#define _LXKEYS_H_ 1
-
-#include <libfm/fm.h>
-
-G_BEGIN_DECLS
-
-/**
- * LXKeysAttr:
- * @name: action or option name
- * @values: (element-type char *): option value
- * @subopts: (element-type LXKeysAttr): (allow-none): list of suboptions
- * @has_actions: %TRUE if @subopts contains actions, %FALSE if @subopts contains options
- *
- * Data descriptor for actions and options. Actions are ativated by keybinding.
- * Each action may contain arbitrary number of options that alter its execution.
- *
- * This data is also used in a result on LXKeysPluginInit:get_wm_actions() or
- * LXKeysPluginInit:get_app_options() call. In that case for each option the
- * @values list should be either %NULL if option accepts any value, or list of
- * acceptable values for the option ("#" value has a special meaning: integer
- * value matches it, the same as "%" for percent value). For such purpose it is
- * advisable to make @name and @values translateable constants because GUI might
- * represent them in target locale which might be convenient for users.
- */
-typedef struct {
-    gchar *name;
-    GList *values;
-    GList *subopts;
-    gboolean has_actions;
-} LXKeysAttr;
-
-/**
- * LXKeysGlobal:
- * @actions: (element-type LXKeysAttr): list of actions
- * @accel1: a keybinding to activate @actions, in GDK accelerator format
- * @accel2: optional alternate keybinding to activate @actions
- * @data1: a pointer for using by WM plugin
- * @data2: a pointer for using by WM plugin
- *
- * Descriptor of a keybinding which isn't a single command line action.
- * The keybinding string is in format that looks like "<Control>a" or
- * "<Shift><Alt>F1" or "<Release>z" (note that not each WM supports last one
- * variant).
- */
-typedef struct {
-    GList *actions;
-    gchar *accel1;
-    gchar *accel2;
-    gpointer data1;
-    gpointer data2;
-} LXKeysGlobal;
-
-/**
- * LXKeysApp:
- * @exec: a command line to execute
- * @actions: (element-type LXKeysAttr): (allow-none): list of options
- * @accel1: a keybinding to activate @exec, in GDK accelerator format
- * @accel2: optional alternate keybinding to activate @exec
- * @data1: a pointer for using by WM plugin
- * @data2: a pointer for using by WM plugin
- *
- * Descriptor of a keybinding for a single command line action. The command
- * execution may be altered by some @options. See also #LXKeysGlobal.
- */
-typedef struct {
-    gchar *exec;
-    GList *options;
-    gchar *accel1;
-    gchar *accel2;
-    gpointer data1;
-    gpointer data2;
-} LXKeysApp;
-
-
-/* WM support plugins */
-
-#define FM_MODULE_lxkeys_VERSION 1 /* version of this API */
-
-/**
- * LXKeysPluginInit:
- * @load: callback to (re)load bindings from WM configuration
- * @save: callback to save bindings to WM configuration
- * @free: callback to release allocated resources
- * @get_wm_keys: (allow-none): callback to get global keys by provided mask
- * @set_wm_key: (allow-none): callback to set a global key by provided data
- * @get_wm_actions: (allow-none): callback to get global actions list provided by WM
- * @get_app_keys: (allow-none): callback to get keys bound to commands
- * @set_app_key: (allow-none): callback to set a key for a command
- * @get_app_options: (allow-none): callback to get possible actions for commands
- *
- * Callbacks @get_wm_keys and @get_app_keys return list which should be freed
- * by caller (transfer container).
- * Callbacks @get_wm_actions and @get_app_actions return list that should be
- * not modified nor freed by caller (transfer none).
- * Callback @get_wm_keys returns list of keybindings by @mask which is a shell
- * style pattern for keys.
- * Callback @set_wm_key changes a keybinding for list of actions provided. If
- * @data::accel1 is %NULL then all keybindings for @data::actions will be
- * cleared, otherwise keybindings will be changed accordingly.
- */
-typedef struct {
-    /*< public >*/
-    gpointer (*load)(gpointer config, GError **error);
-    gboolean (*save)(gpointer config, GError **error);
-    void (*free)(gpointer config);
-    GList *(*get_wm_keys)(gpointer config, const char *mask, GError **error);
-    gboolean (*set_wm_key)(gpointer config, LXKeysGlobal *data, GError **error);
-    GList *(*get_wm_actions)(gpointer config, GError **error);
-    GList *(*get_app_keys)(gpointer config, const char *mask, GError **error);
-    gboolean (*set_app_key)(gpointer config, LXKeysApp *data, GError **error);
-    GList *(*get_app_options)(gpointer config, GError **error);
-    /*< private >*/
-    gpointer _reserved1;
-    gpointer _reserved2;
-    gpointer _reserved3;
-} LXKeysPluginInit;
-
-/**
- * This descriptor instance should be defined in each plugin code as main
- * entry point for plugin creation. Primitive plugin example follows:
- *
- * #include <lxkeys/lxkeys.h>
- *
- * gpointer test_load(gpointer config, GError **error)
- * {
- *      if (config == NULL)
- *          config = g_strdup("");
- *      return config;
- * }
- *
- * gboolean test_save(gpointer config, GError **error)
- * {
- *      return FALSE;
- * }
- *
- * FM_DEFINE_MODULE(lxkeys, NoWM)
- *
- * LXKeysPluginInit fm_module_init_lxkeys = {
- *      .load = test_load,
- *      .save = test_save,
- *      .free = g_free
- * }
- */
-extern LXKeysPluginInit fm_module_init_lxkeys;
-
-
-/* GUI plugins */
-
-#define FM_MODULE_lxkeys_gui_VERSION 1 /* version of this API */
-
-/**
- * LXKeysGUIPluginInit:
- * @run: callback to run GUI
- * @alert: callback to show an error message
- *
- * The @run callback receives name of WM, pointer to callbacks, and pointer to
- * the config data which is already succesfully loaded and ready to use.
- */
-typedef struct {
-    /*< public >*/
-    void (*run)(const gchar *wm, const LXKeysPluginInit *cb, gpointer config, GError **error);
-    void (*alert)(GError *error);
-    /*< private >*/
-    gpointer _reserved1;
-    gpointer _reserved2;
-    gpointer _reserved3;
-} LXKeysGUIPluginInit;
-
-/**
- * This descriptor instance should be defined in each plugin code as main
- * entry point for a GUI plugin creation.
- */
-extern LXKeysGUIPluginInit fm_module_init_lxkeys_gui;
-
-G_END_DECLS
-
-#endif /* _LXKEYS_H_ */