diff options
| author | Victor Kareh <[email protected]> | 2026-01-22 06:49:06 -0500 |
|---|---|---|
| committer | Victor Kareh <[email protected]> | 2026-02-18 15:14:16 -0500 |
| commit | 5075f77b4614227437a76806a821fff965557f0f (patch) | |
| tree | 4d2c189fe6fbf34356506894cc4eb7ca2079ac23 | |
| parent | 034677b0c5bf9b37c22a6d2d52f8761c2364857d (diff) | |
| download | eom-plugin-docs.tar.bz2 eom-plugin-docs.tar.xz | |
docs: Write help about plugins and plugin developmentplugin-docs
| -rw-r--r-- | HACKING-PLUGINS.md | 402 | ||||
| -rw-r--r-- | help/C/index.docbook | 67 |
2 files changed, 469 insertions, 0 deletions
diff --git a/HACKING-PLUGINS.md b/HACKING-PLUGINS.md new file mode 100644 index 0000000..5150074 --- /dev/null +++ b/HACKING-PLUGINS.md @@ -0,0 +1,402 @@ +# EOM Plugin Development Guide + +Quick guide for creating plugins for Eye of MATE (EOM). + +## Quick Start + +EOM uses **libpeas** for plugins. A plugin extends EOM by implementing the `EomWindowActivatable` interface. + +**What you can do:** +- Add menu items and toolbar buttons +- React to image changes +- Access image data and metadata +- Add custom UI widgets + +**Languages:** C or Python 3 + +**Install location:** `~/.local/share/eom/plugins/your-plugin-name/` + +## Plugin Structure + +Every plugin needs these files: + +1. **Plugin header** - `my-plugin.h` +2. **Plugin code** - `my-plugin.c` +3. **Plugin descriptor** - `my-plugin.plugin` +4. **Build file** - `Makefile` + +### Basic Plugin Template + +**Note:** This is a simplified template. For a complete working example with all necessary boilerplate, see `plugins/reload/` in the EOM source tree. + +```c +// my-plugin.h +#ifndef __MY_PLUGIN_H__ +#define __MY_PLUGIN_H__ + +#include <glib-object.h> +#include <libpeas/peas-extension-base.h> +#include <libpeas/peas-object-module.h> +#include <eom-window.h> + +G_BEGIN_DECLS + +#define MY_TYPE_PLUGIN (my_plugin_get_type ()) +G_DECLARE_FINAL_TYPE (MyPlugin, my_plugin, MY, PLUGIN, PeasExtensionBase) + +struct _MyPlugin { + PeasExtensionBase parent; + EomWindow *window; + GtkActionGroup *action_group; + guint ui_id; +}; + +GType my_plugin_get_type (void) G_GNUC_CONST; +G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module); + +G_END_DECLS + +#endif /* __MY_PLUGIN_H__ */ +``` + +```c +// my-plugin.c +#include "my-plugin.h" +#include <eom-window-activatable.h> + +static void eom_window_activatable_iface_init (EomWindowActivatableInterface *iface); + +// Register the plugin type +G_DEFINE_DYNAMIC_TYPE_EXTENDED (MyPlugin, my_plugin, + PEAS_TYPE_EXTENSION_BASE, 0, + G_IMPLEMENT_INTERFACE_DYNAMIC (EOM_TYPE_WINDOW_ACTIVATABLE, + eom_window_activatable_iface_init)) + +enum { + PROP_0, + PROP_WINDOW +}; + +// Initialize plugin instance +static void +my_plugin_init (MyPlugin *plugin) +{ + // Initialize your plugin data here +} + +// Clean up plugin resources +static void +my_plugin_dispose (GObject *object) +{ + MyPlugin *plugin = MY_PLUGIN (object); + + if (plugin->window != NULL) { + g_object_unref (plugin->window); + plugin->window = NULL; + } + + G_OBJECT_CLASS (my_plugin_parent_class)->dispose (object); +} + +// Set plugin properties +static void +my_plugin_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MyPlugin *plugin = MY_PLUGIN (object); + + switch (prop_id) { + case PROP_WINDOW: + plugin->window = EOM_WINDOW (g_value_dup_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +// Get plugin properties +static void +my_plugin_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MyPlugin *plugin = MY_PLUGIN (object); + + switch (prop_id) { + case PROP_WINDOW: + g_value_set_object (value, plugin->window); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +// Called when plugin is enabled +static void +my_plugin_activate (EomWindowActivatable *activatable) +{ + MyPlugin *plugin = MY_PLUGIN (activatable); + + // Add menu items, connect signals, etc. +} + +// Called when plugin is disabled +static void +my_plugin_deactivate (EomWindowActivatable *activatable) +{ + MyPlugin *plugin = MY_PLUGIN (activatable); + + // Remove menu items, disconnect signals, etc. +} + +// Initialize plugin class +static void +my_plugin_class_init (MyPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = my_plugin_dispose; + object_class->set_property = my_plugin_set_property; + object_class->get_property = my_plugin_get_property; + + g_object_class_override_property (object_class, PROP_WINDOW, "window"); +} + +// Required by G_DEFINE_DYNAMIC_TYPE_EXTENDED +static void +my_plugin_class_finalize (MyPluginClass *klass) +{ + // Dummy function for dynamic type registration +} + +// Implement the activatable interface +static void +eom_window_activatable_iface_init (EomWindowActivatableInterface *iface) +{ + iface->activate = my_plugin_activate; + iface->deactivate = my_plugin_deactivate; +} + +// Register with libpeas +G_MODULE_EXPORT void +peas_register_types (PeasObjectModule *module) +{ + my_plugin_register_type (G_TYPE_MODULE (module)); + peas_object_module_register_extension_type (module, + EOM_TYPE_WINDOW_ACTIVATABLE, MY_TYPE_PLUGIN); +} +``` + +### Plugin Descriptor + +```ini +# my-plugin.plugin +[Plugin] +Module=my-plugin +IAge=2 +Name=My Plugin Name +Description=What this plugin does +Authors=Your Name <[email protected]> +Copyright=Copyright © 2025 Your Name +Website=https://mate-desktop.org +``` + +### Build File + +```makefile +# Makefile +PLUGIN_NAME = my-plugin +SOURCES = my-plugin.c +HEADERS = my-plugin.h + +USER_DIR = $(HOME)/.local/share/eom/plugins/$(PLUGIN_NAME) +CFLAGS = `pkg-config --cflags gtk+-3.0 eom libpeas-1.0` -fPIC +LDFLAGS = `pkg-config --libs gtk+-3.0 eom libpeas-1.0` -shared + +all: lib$(PLUGIN_NAME).so + +lib$(PLUGIN_NAME).so: $(SOURCES) $(HEADERS) + $(CC) $(CFLAGS) $(SOURCES) -o $@ $(LDFLAGS) + +install: all + mkdir -p $(USER_DIR) + cp lib$(PLUGIN_NAME).so $(USER_DIR)/ + cp $(PLUGIN_NAME).plugin $(USER_DIR)/ + +clean: + rm -f lib$(PLUGIN_NAME).so +``` + +## Common Tasks + +### Adding a Menu Item + +```c +static void +my_action_callback (GtkAction *action, EomWindow *window) +{ + // Do something when menu item is clicked +} + +static const GtkActionEntry actions[] = { + { "MyAction", NULL, "Menu Label", "<Ctrl>M", + "Tooltip text", G_CALLBACK (my_action_callback) } +}; + +static const gchar* ui_xml = + "<ui><menubar name='MainMenu'>" + "<menu name='ToolsMenu' action='Tools'>" + "<menuitem action='MyAction'/>" + "</menu></menubar></ui>"; + +static void +my_plugin_activate (EomWindowActivatable *activatable) +{ + MyPlugin *plugin = MY_PLUGIN (activatable); + GtkUIManager *manager = eom_window_get_ui_manager (plugin->window); + + plugin->action_group = gtk_action_group_new ("MyActions"); + gtk_action_group_add_actions (plugin->action_group, actions, + G_N_ELEMENTS (actions), plugin->window); + gtk_ui_manager_insert_action_group (manager, plugin->action_group, -1); + plugin->ui_id = gtk_ui_manager_add_ui_from_string (manager, ui_xml, -1, NULL); +} +``` + +### Working with Images + +```c +// Get current image +EomImage *image = eom_window_get_image (window); + +// Get image data +GdkPixbuf *pixbuf = eom_image_get_pixbuf (image); +gint width, height; +eom_image_get_size (image, &width, &height); + +// Check image type +if (eom_image_is_jpeg (image)) { /* ... */ } + +// Transform image +EomTransform *transform = eom_transform_rotate_new (90); +eom_image_transform (image, transform, NULL); +g_object_unref (transform); + +// Mark as modified +eom_image_modified (image); +``` + +### Responding to Events + +```c +static void +on_image_changed (EomThumbView *view, MyPlugin *plugin) +{ + EomImage *image = eom_window_get_image (plugin->window); + // React to image change +} + +static void +my_plugin_activate (EomWindowActivatable *activatable) +{ + MyPlugin *plugin = MY_PLUGIN (activatable); + GtkWidget *thumbview = eom_window_get_thumb_view (plugin->window); + + plugin->signal_id = g_signal_connect (thumbview, "selection_changed", + G_CALLBACK (on_image_changed), plugin); +} + +static void +my_plugin_deactivate (EomWindowActivatable *activatable) +{ + MyPlugin *plugin = MY_PLUGIN (activatable); + GtkWidget *thumbview = eom_window_get_thumb_view (plugin->window); + + g_signal_handler_disconnect (thumbview, plugin->signal_id); +} +``` + +## Key APIs + +### EomWindow + +```c +EomImage * eom_window_get_image (EomWindow *window); +GtkUIManager * eom_window_get_ui_manager (EomWindow *window); +GtkWidget * eom_window_get_view (EomWindow *window); +GtkWidget * eom_window_get_sidebar (EomWindow *window); +GtkWidget * eom_window_get_statusbar (EomWindow *window); +void eom_window_reload_image (EomWindow *window); +``` + +### EomImage + +```c +GdkPixbuf * eom_image_get_pixbuf (EomImage *img); +void eom_image_get_size (EomImage *img, gint *width, gint *height); +void eom_image_transform (EomImage *img, EomTransform *trans, EomJob *job); +void eom_image_modified (EomImage *img); +gboolean eom_image_is_jpeg (EomImage *img); +``` + +See `src/eom-window.h` and `src/eom-image.h` for complete API. + +## Building and Testing + +```bash +# Build the plugin +make + +# Install to user directory +make install + +# Test +eom +# Go to Edit → Preferences → Plugins +# Enable your plugin +``` + +## Built-in Examples + +Study these plugins in the `plugins/` directory: + +1. **fullscreen** - Simple event handling (double-click to toggle fullscreen) +2. **reload** - Adding menu items (reload image from disk) +3. **statusbar-date** - Widget manipulation and EXIF reading + +## Tips + +- **Memory management**: Always `g_object_unref()` objects and `g_free()` allocated memory +- **Cleanup**: Remove all UI elements and disconnect all signals in `deactivate()` +- **Debugging**: Run with `EOM_DEBUG_PLUGINS=1 eom` or `EOM_DEBUG=1 eom` to see plugin messages +- **Headers**: All EOM headers are in `/usr/include/eom/` (install `libeom-dev`) + +## Complete Example + +See `plugins/reload/` in the EOM source for a complete, simple example showing: +- Plugin structure with proper GObject boilerplate +- Adding a menu item with keyboard shortcut +- Calling EOM window functions +- Proper activation and deactivation + +## Resources + +- **libpeas source/examples**: https://gitlab.gnome.org/GNOME/libpeas/-/tree/1.36?ref_type=heads +- **EOM headers**: `/usr/include/eom/` or `src/` in source tree (install `libeom-dev`) +- **GTK+ 3 docs**: https://docs.gtk.org/gtk3/ +- **MATE wiki**: https://wiki.mate-desktop.org/ +- **GObject Tutorial**: https://docs.gtk.org/gobject/ + +## Next Steps + +1. Copy one of the built-in plugins as a template +2. Modify it for your needs +3. Build and test iteratively +4. Share your plugin with the community! + +For advanced topics (GSettings integration, complex UI, Python plugins), refer to the built-in plugin code and GTK+/libpeas documentation. diff --git a/help/C/index.docbook b/help/C/index.docbook index 88f9955..29edff6 100644 --- a/help/C/index.docbook +++ b/help/C/index.docbook @@ -783,5 +783,72 @@ </varlistentry> </variablelist> </section> + + <!--============ Plugins ==================--> + <section xml:id="eom-prefs-plugins"><info><title>Plugins</title></info> + + <para><application>Image Viewer</application> supports plugins that can extend its functionality with additional features. Plugins can add menu items, modify image display behavior, or integrate with external applications.</para> + + <section xml:id="eom-plugins-enable"><info><title>Enabling and Disabling Plugins</title></info> + <para>To manage plugins:</para> + <orderedlist inheritnum="ignore" continuation="restarts"> + <listitem> + <para>Choose <menuchoice><guimenu>Edit</guimenu><guimenuitem>Preferences</guimenuitem></menuchoice>.</para> + </listitem> + <listitem> + <para>Click on the <guilabel>Plugins</guilabel> tab.</para> + </listitem> + <listitem> + <para>Check the box next to a plugin name to enable it, or uncheck it to disable it.</para> + </listitem> + <listitem> + <para>Click <guibutton>Close</guibutton> to save your changes.</para> + </listitem> + </orderedlist> + <para>Changes to plugin activation take effect immediately.</para> + </section> + + <section xml:id="eom-plugins-builtin"><info><title>Built-in Plugins</title></info> + <para><application>Image Viewer</application> includes the following built-in plugins:</para> + <variablelist> + <varlistentry> + <term><guilabel>Fullscreen with double-click</guilabel></term> + <listitem> + <para>Allows you to toggle fullscreen mode by double-clicking on an image. This provides a quick way to enter and exit fullscreen without using the menu or keyboard shortcuts.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><guilabel>Reload Image</guilabel></term> + <listitem> + <para>Adds a menu item and shortcut to reload the current image from disk. This is useful when an image file has been modified by an external application and you want to see the updated version without closing and reopening the file.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><guilabel>Statusbar Date</guilabel></term> + <listitem> + <para>Displays the date when the photo was taken in the statusbar, extracted from the image's EXIF metadata. This is particularly useful for digital photos taken with a camera that records the capture date.</para> + </listitem> + </varlistentry> + </variablelist> + </section> + + <section xml:id="eom-plugins-install"><info><title>Installing Additional Plugins</title></info> + <para>Additional plugins can be installed by placing them in one of the following locations:</para> + <itemizedlist> + <listitem> + <para><filename>~/.local/share/eom/plugins/</filename> - for personal use</para> + </listitem> + <listitem> + <para><filename>/usr/lib/eom/plugins/</filename> - system-wide (requires administrator privileges)</para> + </listitem> + </itemizedlist> + <para>After installing a plugin, restart <application>Image Viewer</application> or reload the plugin list in the Preferences dialog to see the new plugin.</para> + </section> + + <section xml:id="eom-plugins-develop"><info><title>Developing Plugins</title></info> + <para>If you are a developer interested in creating plugins for <application>Image Viewer</application>, please refer to <filename>HACKING-PLUGINS.md</filename> in the source code repository, or visit the MATE Desktop development documentation website.</para> + </section> + + </section> </section> </article> |
