Implemented "lxpanelctl command ..." to send message to panel plugin.
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 21 Mar 2016 10:37:31 +0000 (12:37 +0200)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 21 Mar 2016 10:37:31 +0000 (12:37 +0200)
ChangeLog
TODO
VERSIONING
man/lxpanelctl.1
src/lxpanelctl.c
src/lxpanelctl.h
src/main.c
src/panel.c
src/plugin.h
src/private.h

index 2dc4e8b..ef9c1eb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@
 * Converted space plugin into internal PanelSpace widget in liblxpanel.
 * Converted task button into widget class to avoid ambiguous code.
 * Added support for third-party plugins localized descriptions.
+* Implemented "lxpanelctl command ..." to send message to panel plugin.
 
 0.8.2
 -------------------------------------------------------------------------
diff --git a/TODO b/TODO
index 413bf75..70e2e18 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,5 @@
 * drag & drop plugins via middle button
 * "Move" option in plugin context menu
-* "lxpanelctl command ..."
 * LXPanelPluginInit::activatable flag to allow activation with hotkey
 * drag & drop launchers (LauncherButton widget as "application/x-lxpanel-launcher")
 * accessibility (keys-only; single-button-mouse; special devices)
@@ -12,7 +11,8 @@
 * hotkeys for launchers
 * support rounded corners
 * support keywords in gtk-run
-* convert Task into GtkWidget and use it to emit signals for menus and buttons
+* standalone run application with history shared with panel?
+* "lxpanelctl restart" with panel not running starts it?
 * make click-wait-release popdown popups similarly to as GtkMenuShell does
 * make lxpanel multiscreen-aware (build fb_ev_* and get_net_* into lxpanel)
 * improve startup time (move every non-instant operation into idle callback)
index d9073a2..7b2bdbc 100644 (file)
@@ -19,11 +19,11 @@ The lxpanel API consists of:
         * data/ui/netstatus.ui
 
     - The plugin API:
-        * /$(prefix)/include/lxpanel/plugin.h (Must be the same as src/plugin.h)
-        * /$(prefix)/include/lxpanel/panel.h
-        * /$(prefix)/include/lxpanel/misc.h
-        * /$(prefix)/include/lxpanel/icon-grid.h
-        * /$(prefix)/include/lxpanel/conf.h
+        * $(prefix)/include/lxpanel/plugin.h (Must be the same as src/plugin.h)
+        * $(prefix)/include/lxpanel/panel.h
+        * $(prefix)/include/lxpanel/misc.h
+        * $(prefix)/include/lxpanel/icon-grid.h
+        * $(prefix)/include/lxpanel/conf.h
 
     - The command line arguments to lxpanel and lxpanelctl.
 
index 50574d8..d3a5dee 100644 (file)
@@ -7,7 +7,7 @@
 .\"    Source: http://LXDE.org
 .\"  Language: English
 .\"
-.TH "LXPANEL" "1" "March 2, 2008" "http://LXDE\&.org" "http://LXDE.org"
+.TH "LXPANEL" "1" "March, 2016" "http://LXDE\&.org" "http://LXDE.org"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -66,6 +66,13 @@ Restart lxpanel\&.
 .RS 4
 Exit lxpanel\&.
 .RE
+.PP
+\fBcommand \fR[\fB\-\-panel=\fR[\fIN\fR:]\fIedge\fR] \fIplugin\fR \fIcommand\fR
+.RS 4
+Send a \fIcommand\fR to a \fIplugin\fR\&. Optionally monitor number \fIN\fR
+(in range 1 to 8) and \fIedge\fR (left, right, top or bottom) can be set,
+otherwise \fIcommand\fR will be send to first \fIplugin\fR found in any panel\&.
+.RE
 .SH "SEE ALSO"
 .PP
 lxpanel (1)\&.
@@ -80,6 +87,8 @@ GNU
 General Public License, Version 2 any later version published by the Free Software Foundation\&.
 .PP
 On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common\-licenses/GPL-2\&.
+.PP
+Updated by Andriy Grytsenko (LStranger)\&.
 .SH "AUTHOR"
 .PP
 \fBYing\-Chun Liu\fR
@@ -90,3 +99,5 @@ Author.
 .br
 Copyright \(co 2008 paulliu
 .br
+Copyright \(co 2016 LStranger
+.br
index fb68183..dcf502d 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 2006 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
  *               2006 Jim Huang <jserv.tw@gmail.com>
  *               2009-2010 Marty Jack <martyj19@comcast.net>
+ *               2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -29,6 +30,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdint.h>
 
 static Display* dpy;
 
@@ -36,11 +38,12 @@ static const char usage[] =
         "\nlxpanelctl - LXPanel Controller\n"
         "Usage: lxpanelctl <command>\n\n"
         "Available commands:\n"
-        "menu\tshow system menu\n"
-        "run\tshow run dialog\n"
-        "config\tshow configuration dialog\n"
-        "restart\trestart lxpanel\n"
-        "exit\texit lxpanel\n\n";
+        "menu\t\t\tshow system menu\n"
+        "run\t\t\tshow run dialog\n"
+        "config\t\t\tshow configuration dialog\n"
+        "restart\t\t\trestart lxpanel\n"
+        "exit\t\t\texit lxpanel\n"
+        "command <plugin> <cmd>\tsend a command to a plugin\n\n";
 
 static int get_cmd( const char* cmd )
 {
@@ -54,9 +57,33 @@ static int get_cmd( const char* cmd )
         return LXPANEL_CMD_RESTART;
     else if( ! strcmp( cmd, "exit") )
         return LXPANEL_CMD_EXIT;
+    else if( ! strcmp( cmd, "command") )
+        return LXPANEL_CMD_COMMAND;
     return -1;
 }
 
+/* format: either "<edge>" or "<num>:<edge>" */
+static int parse_id(const char *arg, int *mon)
+{
+    char *end;
+    long lmon = strtoul(arg, &end, 10);
+
+    if (*end == ':')
+        arg = (const char *)end + 1;
+    else
+        lmon = 0;
+    *mon = lmon;
+    if (strcmp(arg, "top") == 0)
+        return EDGE_TOP;
+    if (strcmp(arg, "bottom") == 0)
+        return EDGE_BOTTOM;
+    if (strcmp(arg, "left") == 0)
+        return EDGE_LEFT;
+    if (strcmp(arg, "right") == 0)
+        return EDGE_RIGHT;
+    return EDGE_NONE;
+}
+
 int main( int argc, char** argv )
 {
     char *display_name = (char *)getenv("DISPLAY");
@@ -65,6 +92,9 @@ int main( int argc, char** argv )
     Atom cmd_atom;
     int cmd;
     /* int restart; */
+    /* target of message, it's XClientMessageEvent::b[1]
+     * valid only if XClientMessageEvent::b[0] == LXPANEL_CMD_COMMAND */
+    uint8_t target;
 
     if( argc < 2 )
     {
@@ -80,6 +110,12 @@ int main( int argc, char** argv )
     if( ( cmd = get_cmd( argv[1] ) ) == -1 )
         return 1;
 
+    if (cmd == LXPANEL_CMD_COMMAND && argc < 4)
+    {
+        printf( usage );
+        return 1;
+    }
+
     dpy = XOpenDisplay(display_name);
     if (dpy == NULL) {
         printf("Cant connect to display: %s\n", display_name);
@@ -95,6 +131,23 @@ int main( int argc, char** argv )
 
     ev.xclient.data.b[0] = cmd;
 
+    if (cmd == LXPANEL_CMD_COMMAND)
+    {
+        int i = 2;
+        if (argc > 4 && strncmp(argv[2], "--panel=", 8))
+        {
+            int monitor;
+            int edge = parse_id(argv[2] + 8, &monitor);
+            /* edge: EDGE_NONE ..., monitor: 0 - none, 1...8 - selected */
+            target = ((edge & 0x7) << 4) + (monitor & 0xf);
+            i++;
+        }
+        else
+            target = (EDGE_NONE << 4) + 0; /* edge: none, monitor: none */
+        ev.xclient.data.b[1] = target;
+        snprintf(&ev.xclient.data.b[2], 18, "%s\t%s", argv[i], argv[i+1]);
+    }
+
     XSendEvent(dpy, root, False,
                SubstructureRedirectMask|SubstructureNotifyMask, &ev);
     XSync(dpy, False);
index 6e8a6f9..0ca1e71 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2006 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
  *               2010 Marty Jack <martyj19@comcast.net>
+ *               2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -31,7 +32,11 @@ typedef enum {
     LXPANEL_CMD_RUN,
     LXPANEL_CMD_CONFIG,
     LXPANEL_CMD_RESTART,
-    LXPANEL_CMD_EXIT
+    LXPANEL_CMD_EXIT,
+    LXPANEL_CMD_COMMAND
 } PanelControlCommand;
 
+/* this enum was in private.h but it is used by LXPANEL_CMD_COMMAND now */
+enum { EDGE_NONE=0, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_BOTTOM };
+
 #endif
index 0baeed5..db177e8 100644 (file)
@@ -13,7 +13,7 @@
  *               2012 Julien Lavergne <julien.lavergne@gmail.com>
  *               2013 Rouslan <rouslan-k@users.sourceforge.net>
  *               2013 peadaredwards <peadaredwards@users.sourceforge.net>
- *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -83,6 +83,10 @@ void restart(void)
 static void process_client_msg ( XClientMessageEvent* ev )
 {
     int cmd = ev->data.b[0];
+    int monitor;
+    int edge;
+    char *plugin_type;
+    char *command;
     switch( cmd )
     {
 #ifndef DISABLE_MENU
@@ -125,6 +129,73 @@ static void process_client_msg ( XClientMessageEvent* ev )
         case LXPANEL_CMD_EXIT:
             gtk_main_quit();
             break;
+        case LXPANEL_CMD_COMMAND:
+            monitor = (ev->data.b[1] & 0xf) - 1; /* 0 for no monitor */
+            edge = (ev->data.b[1] >> 4) & 0x7;
+            plugin_type = g_strndup(&ev->data.b[2], 18);
+            command = strchr(plugin_type, '\t');
+            if (command) do /* use do{}while(0) to enable break */
+            {
+                LXPanel *p;
+                GSList *l;
+                GList *plugins, *pl;
+                const LXPanelPluginInit *init;
+                GtkWidget *plugin = NULL;
+
+                *command++ = '\0';
+                /* find the panel by monitor and edge */
+                for (l = all_panels; l; l = l->next)
+                {
+                    p = (LXPanel*)l->data;
+                    if (p->priv->box == NULL) /* inactive panel */
+                        continue;
+                    if (monitor >= 0 && p->priv->monitor != monitor)
+                        continue;
+                    if (edge == EDGE_NONE || p->priv->edge == edge)
+                        break;
+                }
+                if (l == NULL) /* match not found */
+                    break;
+                /* find the plugin */
+                init = g_hash_table_lookup(lxpanel_get_all_types(), plugin_type);
+                if (init == NULL) /* no such plugin known */
+                    break;
+                plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
+                for (pl = plugins; pl; pl = pl->next)
+                {
+                    if (init == PLUGIN_CLASS(pl->data))
+                    {
+                        plugin = pl->data;
+                        break;
+                    }
+                }
+                g_list_free(plugins);
+                /* test for built-in commands ADD and DEL */
+                if (strcmp(command, "ADD") == 0)
+                {
+                    if (plugin == NULL)
+                    {
+                        config_setting_t *cfg;
+
+                        cfg = config_group_add_subgroup(config_root_setting(p->priv->config),
+                                                        "Plugin");
+                        config_group_set_string(cfg, "type", plugin_type);
+                        plugin = lxpanel_add_plugin(p, plugin_type, cfg, -1);
+                        if (plugin == NULL) /* failed to create */
+                            config_setting_destroy(cfg);
+                    }
+                }
+                else if (strcmp(command, "DEL") == 0)
+                {
+                    if (plugin != NULL)
+                        lxpanel_remove_plugin(p, plugin);
+                }
+                /* send the command */
+                else if (plugin && init->control)
+                    init->control(plugin, command);
+            } while(0);
+            g_free(plugin_type);
+            break;
     }
 }
 
index 2924b4c..8efa0f8 100644 (file)
@@ -13,7 +13,7 @@
  *               2012 Julien Lavergne <julien.lavergne@gmail.com>
  *               2013 Rouslan <rouslan-k@users.sourceforge.net>
  *               2013 peadaredwards <peadaredwards@users.sourceforge.net>
- *               2014-2015 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *               2015 Rafał Mużyło <galtgendo@gmail.com>
  *               2015 Hanno Zulla <hhz@users.sf.net>
  *
@@ -1089,7 +1089,12 @@ static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
 
 static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
 {
-    Panel* panel = PLUGIN_PANEL(plugin)->priv;
+    lxpanel_remove_plugin(PLUGIN_PANEL(plugin), plugin);
+}
+
+void lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin)
+{
+    Panel* panel = p->priv;
 
     /* If the configuration dialog is open, there will certainly be a crash if the
      * user manipulates the Configured Plugins list, after we remove this entry.
@@ -1103,7 +1108,7 @@ static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
     /* reset conf pointer because the widget still may be referenced by configurator */
     g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
 
-    lxpanel_config_save(PLUGIN_PANEL(plugin));
+    lxpanel_config_save(p);
     gtk_widget_destroy(plugin);
 }
 
index 21f32ee..97cdd0e 100644 (file)
@@ -92,7 +92,9 @@ G_BEGIN_DECLS
  * Callback @control is called when command was sent via the lxpanelctl.
  * The message will be sent to only one instance of plugin. Some messages
  * are handled by lxpanel: "DEL" will remove plugin from panel, "ADD"
- * will create new instance if there is no instance yet. (TODO)
+ * will create new instance if there is no instance yet. Due to design
+ * limitations of XClientMessageEvent the size of plugin type and command
+ * cannot exceed 16 characters in total.
  *
  * If @gettext_package is not %NULL then it will be used for translation
  * of @name and @description. (Since: 0.9.0)
index 08ba867..1c88036 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2015 Andriy Grytsenko <andrej@rep.kiev.ua>
+ * Copyright (C) 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -23,6 +23,7 @@
 
 #include "plugin.h"
 #include "conf.h"
+#include "lxpanelctl.h"
 
 #include <gmodule.h>
 
@@ -44,7 +45,6 @@
 
 /* Extracted from panel.h */
 enum { ALIGN_NONE, ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT  };
-enum { EDGE_NONE=0, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_BOTTOM };
 enum { WIDTH_NONE, WIDTH_REQUEST, WIDTH_PIXEL, WIDTH_PERCENT };
 enum { HEIGHT_NONE, HEIGHT_PIXEL, HEIGHT_REQUEST };
 
@@ -213,6 +213,7 @@ void lxpanel_unload_modules(void);
 
 GtkWidget *lxpanel_add_plugin(LXPanel *p, const char *name, config_setting_t *cfg, gint at);
 GHashTable *lxpanel_get_all_types(void); /* transfer none */
+void lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin);
 
 extern GQuark lxpanel_plugin_qinit; /* access to LXPanelPluginInit data */
 #define PLUGIN_CLASS(_i) ((LXPanelPluginInit*)g_object_get_qdata(G_OBJECT(_i),lxpanel_plugin_qinit))