Create GTK + applet for LXPanel

    An article on how to write your applet for LXPanel. The developers have not yet written the documentation, so they have to learn from the source, which, on the contrary, is rich in comments.
    I decided to clearly analyze one example and the easiest way to build into a ready-made * .so plugin.




    The assembly is under Ubuntu, requires minimal knowledge of C and GTK +.

    Training


    1. Get lxpanel sources:
      echo "deb-src http://mirror.yandex.ru/ubuntu maverick main restricted universe multiverse" >> /etc/apt/sources.list
      apt-get update && apt-get source lxpanel
      cd lxpanel-0.5.6

    2. Install the packages necessary for assembly:
      apt-get build-dep lxpanel

    3. Verification:
      LIBS = "- lX11" ./configure --prefix = / usr --with-plugins = cpufreq
      make
      ls src / plugins / cpufreq / .libs / cpufreq.so


    The cpufreq plugin was chosen as a victim , I'll tell you why later. And we will write a plugin for switching performance <-> ondemand processor modes.

    Spelling


    vim src / plugins / cpufreq / my_plugin.c


    Actually, all the code with comments:
    1. / **
    2.  * My_plugin
    3.  * Habrahabr
    4.  * /
    5.  
    6. #include 
    7. #include 
    8. #include 
    9. #include 
    10.  
    11. #include 
    12.  
    13. #include "panel.h"
    14. #include "misc.h"
    15. #include "plugin.h"
    16.  
    17. // For string comparison
    18. #define STREQV (a, b) (* (a) == * (b) && strcmp ((a), (b)) == 0)
    19.  
    20. // Declare the structure of our plugin
    21. typedef struct {
    22.     // Widget that will contain an icon and a menu
    23.     GtkWidget * namew;
    24. } my_plugin;
    25.  
    26. // Create an object
    27. static my_plugin * mp;
    28.  
    29. // Plugin structure is declared in src / plugin.h
    30. // * mp_p - now the Plugin class
    31. // Which at least sets the parent container mp_p-> pwid
    32. // Our widget mp-> namew is "inserted" into it
    33. // And the icon, for example, is contained in mp-> namew
    34. // Death hid for a long time
    35. static plugin * mp_p;
    36.  
    37. // The title speaks for itself
    38. static char get_governor () {
    39.     FILE * fpipe;
    40.     const char governor [16];
    41.     char line [16];
    42.  
    43.     // Problem with cpufreq-info output recognition
    44.     // The script / usr / local / bin / cpufreq-gov returns the name of the current mode
    45.     fpipe = (FILE *) popen ("/ usr / local / bin / cpufreq-gov", "r");
    46.     fgets (line, sizeof (line), fpipe);
    47.     pclose (fpipe);
    48.  
    49.     return line;
    50. }
    51.  
    52. // The title speaks for itself
    53. static char get_frequency () {
    54.     FILE * fpipe;
    55.     const char governor [16];
    56.     char line [16];
    57.  
    58.     // Problem with cpufreq-info output recognition
    59.     // The script / usr / local / bin / cpufreq-gov returns the name of the current mode
    60.     fpipe = (FILE *) popen ("cpufreq-info -f -m", "r");
    61.     fgets (line, sizeof (line), fpipe);
    62.     pclose (fpipe);
    63.     line [strlen (line) -1] = '\ 0';
    64.  
    65.     return line;
    66. }
    67.  
    68. // Make the icon update function when changing the mode
    69. static void refresh_icon () {
    70.     // Find out the current mode
    71.     const char governor [16];
    72.     char line [16] = get_governor ();
    73.     sprintf (governor, "% s", line);
    74.  
    75.     // mp_p-> pwid - parent container of type GTKWidget, which contains mp-> namew
    76.     // Take out the widget mp-> namew and change the icon
    77.     gtk_container_remove (GTK_CONTAINER (mp_p-> pwid), mp-> namew);
    78.  
    79.     // If the mode is "ondemand"
    80.     if (STREQV (governor, "ondemand"))
    81.     {
    82.         // One icon
    83.         mp-> namew = gtk_image_new_from_file ("/ usr / share / lxpanel / images / cpufreq_ond.png");
    84.     }
    85.     else
    86.     {
    87.         // Otherwise, another
    88.         mp-> namew = gtk_image_new_from_file ("/ usr / share / lxpanel / images / cpufreq_perf.png");
    89.     }
    90.     // Insert widget mp-> namew back into parent container mp_p-> pwid
    91.     gtk_container_add (GTK_CONTAINER (mp_p-> pwid), mp-> namew);
    92.     gtk_widget_show_all (mp_p-> pwid);
    93. }
    94.  
    95. // Function change mode
    96. static void set_governor (GtkWidget * widget, char * p) {
    97.     const char exec [32];
    98.     sprintf (exec, "cpufreq-set -g% s", p);
    99.     system (exec);
    100.     refresh_icon ();
    101. }
    102.  
    103. // Create a menu
    104. static GtkWidget * mp_menu () {
    105.     GtkMenu * menu = gtk_menu_new ();
    106.     GSList * group = NULL;
    107.     GtkWidget * menuitem, * radio1, * radio2;
    108.  
    109.     FILE * fpipe;
    110.     const char governor [16];
    111.     const char frequency [16];
    112.     char line [16];
    113.  
    114.     line = get_governor ();
    115.     sprintf (governor, "% s", line);
    116.  
    117.     line = get_frequency ();
    118.     sprintf (frequency, "% s", line);
    119.  
    120.     // 1) Add the current frequency to the menu, make the item inactive
    121.     menuitem = gtk_menu_item_new_with_label (frequency);
    122.     gtk_menu_append (menu, menuitem);
    123.     gtk_widget_set_sensitive (menuitem, FALSE);
    124.     gtk_widget_show (menuitem);
    125.  
    126.     // 2) Add a separator
    127.     menuitem = gtk_separator_menu_item_new ();
    128.     gtk_menu_append (menu, menuitem);
    129.     gtk_widget_show (menuitem);
    130.  
    131.     // 3) 2 radio buttons for switching between modes
    132.     radio1 = gtk_radio_menu_item_new_with_label (group, "Savings");
    133.     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (radio1));
    134.     if (STREQV (governor, "ondemand"))
    135.     {
    136.         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (radio1), TRUE);
    137.     }
    138.     gtk_menu_append (menu, radio1);
    139.     gtk_widget_show (radio1);
    140.  
    141.     // 4) Second button
    142.     radio2 = gtk_radio_menu_item_new_with_label (group, "Performance");
    143.     if (STREQV (governor, "performance"))
    144.     {
    145.         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (radio2), TRUE);
    146.     }
    147.     gtk_menu_append (menu, radio2);
    148.     gtk_widget_show (radio2);
    149.  
    150.     // When you click on the radio buttons, the mode switches
    151.     g_signal_connect (G_OBJECT (radio1), "toggled", G_CALLBACK (set_governor), "ondemand");
    152.     g_signal_connect (G_OBJECT (radio2), "toggled", G_CALLBACK (set_governor), "performance");
    153.  
    154.     return menu;
    155. }
    156.  
    157. // Handler for the click event on the plugin icon
    158. static gboolean clicked (GtkWidget * widget, GdkEventButton * evt, Plugin * plugin) {
    159.     gtk_menu_popup (mp_menu (), NULL, NULL, NULL, NULL, evt-> button, evt-> time);
    160.     return TRUE;
    161. }
    162.  
    163. // Actually the constructor of the object
    164. static int constructor (Plugin * p) {
    165.     // Create an object
    166.     mechanism_mp = g_new0 (my_plugin, 1);
    167.     p-> priv = mechanism_mp;
    168.  
    169.     // Make the parent widget sensitive (follow events)
    170.     // And remove the window decoration from it
    171.     p-> pwid = gtk_event_box_new ();
    172.     GTK_WIDGET_SET_FLAGS (p-> pwid, GTK_NO_WINDOW);
    173.     gtk_container_set_border_width (GTK_CONTAINER (p-> pwid), 0);
    174.  
    175.     // Set The Initial Icon
    176.     refresh_icon ();
    177.  
    178.     // When clicked, the clicked () handler function fires
    179.     g_signal_connect (G_OBJECT (p-> pwid), "button_press_event", G_CALLBACK (clicked), (gpointer) p);
    180.  
    181.     gtk_widget_show (mechanism_mp-> namew);
    182. }
    183.  
    184. // Destructor
    185. static void destructor (Plugin * p) {
    186.     my_plugin * mechanism_mp = (my_plugin *) p-> priv;
    187.     g_free (mechanism_mp);
    188. }
    189.  
    190. // And here we indicate information about our plugin
    191. PluginClass mp_plugin_class = {
    192.     PLUGINCLASS_VERSIONING,
    193.  
    194.     type: "my_plugin",
    195.     name: N _ ("My super plugin"),
    196.     version: "1.0",
    197.     description: N _ ("Habrahabr"),
    198.  
    199.     constructor: constructor,
    200.     destructor: destructor,
    201.     // In our plugin are not used,
    202.     // but you can assign handlers for configuration, save configuration
    203.     // And redraw the widget when changing the configuration
    204.     config: NULL,
    205.     save: NULL,
    206.     panel_configuration_changed: NULL
    207. };
    208.  


    Compilation


    You can add our plugin to build scripts, but this is not necessary and it is extremely dreary. So just take the cpufreq victim plugin and replace its source src / plugins / cpufreq / cpufreq.c with ours.
    We collect:
    LIBS = "- lX11" ./configure --prefix = / usr --with-plugins = cpufreq
    make

    The finished * .so lies in src / plugins / cpufreq / .libs / , you need to put it in the folder with the rest of the plugins, only under a different name, for example /usr/lib/lxpanel/plugins/my_plugin.so

    Content / usr / local / bin / cpufreq-gov :
    1. #! / bin / sh
    2.  
    3. echo `cpufreq-info -p | awk '{print $ 3}' '


    2 icons in / usr / share / lxpanel / images / :


    Finally


    For clarity, I advise you to read the files, they are rich in comments.
    • src / plugin.c
    • src / plugin.h
    • src / panel.c
    • src / panel.h


    Good luck Example source : my_plugin.c

    Also popular now: