[Xfce4-commits] <xfce4-embed-plugin:master> Added lots of comments.
David Schneider
noreply at xfce.org
Sun Jan 1 21:44:28 CET 2012
Updating branch refs/heads/master
to d8450df582a8ffe99729dc19ce04ada9d54f5128 (commit)
from 53889079fe15165c21d1b1ade15e9fe834ef12f4 (commit)
commit d8450df582a8ffe99729dc19ce04ada9d54f5128
Author: David Schneider <dnschneid at gmail.com>
Date: Fri Dec 30 03:32:01 2011 -0500
Added lots of comments.
panel-plugin/embed.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++----
panel-plugin/ewmh.c | 5 +-
2 files changed, 138 insertions(+), 12 deletions(-)
diff --git a/panel-plugin/embed.c b/panel-plugin/embed.c
index e598426..2c13cae 100644
--- a/panel-plugin/embed.c
+++ b/panel-plugin/embed.c
@@ -63,7 +63,7 @@ embed_add_fake_socket (EmbedPlugin *embed);
XFCE_PANEL_PLUGIN_REGISTER_EXTERNAL (embed_construct);
-
+/* Save the plugin settings. */
void
embed_save (XfcePanelPlugin *plugin, EmbedPlugin *embed)
{
@@ -104,6 +104,7 @@ embed_save (XfcePanelPlugin *plugin, EmbedPlugin *embed)
+/* Read in the plugin settings. */
static void
embed_read (EmbedPlugin *embed)
{
@@ -159,6 +160,10 @@ embed_read (EmbedPlugin *embed)
+/* Remove any old separator and add a new one, if necessary.
+ * This is used to add a separator widget, although currently as there is no
+ * separator widget, nothing happens. The function is still called in the
+ * correct places though, so it is trivial to add. */
static void
embed_update_separator (EmbedPlugin* embed, GtkOrientation orientation)
{
@@ -166,6 +171,7 @@ embed_update_separator (EmbedPlugin* embed, GtkOrientation orientation)
+/* Creates a new EmbedPlugin structure and initializes everything. */
static EmbedPlugin *
embed_new (XfcePanelPlugin *plugin)
{
@@ -226,6 +232,8 @@ embed_new (XfcePanelPlugin *plugin)
+/* Frees up the EmbedPlugin structure, popping out the embedded window if there
+ * still is one embedded. */
static void
embed_free (XfcePanelPlugin *plugin, EmbedPlugin *embed)
{
@@ -233,7 +241,8 @@ embed_free (XfcePanelPlugin *plugin, EmbedPlugin *embed)
DBG (".");
- /* Don't hold onto the embedded window */
+ /* Don't hold onto the embedded window, as if it is a normal window, it will
+ * get lost if we don't reparent it. */
embed_popout (GTK_MENU_ITEM (embed->popout_menu), embed);
/* check if the dialog is still open. if so, destroy it */
@@ -263,6 +272,7 @@ embed_free (XfcePanelPlugin *plugin, EmbedPlugin *embed)
+/* Callback when the orientation of the panel is changed. */
static void
embed_orientation_changed (XfcePanelPlugin *plugin,
GtkOrientation orientation,
@@ -275,6 +285,9 @@ embed_orientation_changed (XfcePanelPlugin *plugin,
+/* Callback when the size of the panel is changed.
+ * Updates the requested sizes of the plugin and the socket window.
+ * Also used manually to apply size changes when the plug changes. */
static gboolean
embed_size_changed (XfcePanelPlugin *plugin, gint size, EmbedPlugin *embed)
{
@@ -285,18 +298,24 @@ embed_size_changed (XfcePanelPlugin *plugin, gint size, EmbedPlugin *embed)
orientation = xfce_panel_plugin_get_orientation (plugin);
/* set the socket widget size.
- * Use the minimum size if set, otherwise if it is set to the window size and
- * we don't have a window embedded, set to square. */
+ * For the adjustable dimension, use the minimum size if set, otherwise if it
+ * is set to the window size and we don't have a window embedded, set to
+ * square. */
if (embed->min_size == EMBED_MIN_SIZE_MATCH_WINDOW)
altsize = size;
else
altsize = embed->min_size;
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
+ /* If requested, use the window size detected earlier. This will be -1 for
+ * true GtkPlugs, which will pass through the window size request. */
if (embed->min_size == EMBED_MIN_SIZE_MATCH_WINDOW && embed->plug)
altsize = embed->plug_width;
gtk_widget_set_size_request (GTK_WIDGET (embed->socket), altsize, size);
+ /* Widget altsize should just adapt, since it might include a label and/or
+ * separator. */
gtk_widget_set_size_request (GTK_WIDGET (plugin), -1, size);
} else {
+ /* Same for vertical orientation. */
if (embed->min_size == EMBED_MIN_SIZE_MATCH_WINDOW && embed->plug)
altsize = embed->plug_height;
gtk_widget_set_size_request (GTK_WIDGET (embed->socket), size, altsize);
@@ -309,6 +328,7 @@ embed_size_changed (XfcePanelPlugin *plugin, gint size, EmbedPlugin *embed)
+/* Convenience function to call embed_size_changed. */
static void
embed_size_changed_simple (EmbedPlugin *embed)
{
@@ -318,15 +338,20 @@ embed_size_changed_simple (EmbedPlugin *embed)
+/* Updates the text of the label, using the label_fmt. */
static void
embed_update_label (EmbedPlugin *embed)
{
+ /* Only show the label if the format is non-empty. */
if (embed->label_fmt && *embed->label_fmt) {
gchar *titlepos;
+ /* If we have an embedded plug and the label wants the title, process it. */
if (embed->plug &&
(titlepos = strstr (embed->label_fmt, EMBED_LABEL_FMT_TITLE))) {
gchar *title, *label;
title = get_window_title (embed->disp, embed->plug);
+ /* Construct the label, replacing the EMBED_LABEL_FMT_TITLE with the
+ * actual window title. */
label = g_strdup_printf ("%.*s%s%s",
(gint)(titlepos - embed->label_fmt), embed->label_fmt,
title, titlepos + strlen (EMBED_LABEL_FMT_TITLE));
@@ -334,6 +359,7 @@ embed_update_label (EmbedPlugin *embed)
g_free (title);
g_free (label);
} else {
+ /* Othewise just display the format string directly. */
gtk_label_set_text (GTK_LABEL (embed->label), embed->label_fmt);
}
gtk_widget_show (embed->label);
@@ -344,6 +370,11 @@ embed_update_label (EmbedPlugin *embed)
+/* Performs a single pass through the windows managed by the window manager,
+ * searching for the first window that meets all of the criteria.
+ * If one is found, it embeds it.
+ * Returns TRUE if no viable plug is found, so that if this is used as a timer
+ * callback, it will keep polling. */
static gboolean
embed_search (EmbedPlugin *embed)
{
@@ -353,12 +384,17 @@ embed_search (EmbedPlugin *embed)
DBG (".");
+ /* Grab a list of windows managed by the window manager.
+ * They will not necessarily be on the current workspace. */
if ((client_list = get_client_list (embed->disp, &client_list_size))) {
+ /* Loop through each window */
for (i = 0; i < client_list_size / sizeof (Window); i++) {
gchar *str;
gboolean match;
match = TRUE;
+ /* AND-match each specified criteria, starting with the presumably
+ * lightest-weight/most-specific ones first. */
if (match && embed->proc_name && embed->proc_name[0]) {
str = get_client_proc (embed->disp, client_list[i]);
match = !g_strcmp0 (embed->proc_name, str);
@@ -376,14 +412,19 @@ embed_search (EmbedPlugin *embed)
g_free (str);
}
+ /* If it's a match, make a fake socket and embed the window in it. */
if (match) {
+ /* Destroy the true GtkSocket, as we will not be needing it. */
gtk_widget_destroy (embed->socket);
embed->plug_is_gtkplug = FALSE;
embed->plug = client_list[i];
+ /* Store the old size of the window for both restoring and for deciding
+ * on the panel size. */
get_window_size (embed->disp, client_list[i],
&embed->plug_width, &embed->plug_height);
DBG ("found window 0x%X of geometry %dx%d",
embed->plug, embed->plug_width, embed->plug_height);
+ /* Make the fake socket and embed the window. */
embed_add_fake_socket (embed);
break;
}
@@ -398,6 +439,9 @@ embed_search (EmbedPlugin *embed)
+/* Starts the search for a viable plug. Does one initial search, and then sets
+ * up X11 monitoring and possibly polling if no plug is found right away.
+ * Does not start a search if there are no criteria, as that would be stupid. */
static void
embed_start_search (GtkWidget *socket, EmbedPlugin *embed)
{
@@ -418,13 +462,14 @@ embed_start_search (GtkWidget *socket, EmbedPlugin *embed)
/* TODO: handle the case where we want to launch an application that will
* generate a plug */
if (embed_search (embed)) {
+ /* Reset the _NET_CLIENT_LIST detector */
embed->monitor_saw_net_client_list = FALSE;
/* Watch for property changes (primarily the window list ones) on the root
* window.
* Note that gdk_x11_get_default_xdisplay () is not the same as the display
* stored in embed->disp. embed->disp will be valid through the destruction
- * of the plugin, but GDK expects its own xdisplay to access the internal
- * hashmaps */
+ * of the plugin, but GDK expects its own Display reference to access its
+ * internal hashmaps for routing events. */
XSelectInput (gdk_x11_get_default_xdisplay (),
gdk_x11_get_default_root_xwindow (), PropertyChangeMask);
if (embed->poll_delay > 0)
@@ -435,9 +480,12 @@ embed_start_search (GtkWidget *socket, EmbedPlugin *embed)
+/* Stops the search process, disabling both X11 monitoring and polling. */
static void
embed_stop_search (EmbedPlugin *embed)
{
+ /* Set the event mask to 0 to stop receiving X11 events for the root window.
+ */
XSelectInput (gdk_x11_get_default_xdisplay (),
gdk_x11_get_default_root_xwindow (), 0);
if (embed->search_timer) {
@@ -448,6 +496,7 @@ embed_stop_search (EmbedPlugin *embed)
+/* Callback for the embed menu button. (Re)starts searching. */
static void
embed_embed_menu (GtkMenuItem *embed_menu, EmbedPlugin *embed)
{
@@ -458,10 +507,16 @@ embed_embed_menu (GtkMenuItem *embed_menu, EmbedPlugin *embed)
+/* X11 event monitor for a plug. Detects window title changes, unmap, and
+ * destroy events. */
static GdkFilterReturn
embed_plug_filter (XPropertyEvent *xevent, GdkEvent *_, EmbedPlugin *embed)
{
if (xevent->type == PropertyNotify) {
+ /* To avoid double-handling window name changes, if we see a _NET_WM_NAME we
+ * will stop responding to WM_NAME events.
+ * This is reset every time a new window is embedded, in case the window
+ * manager changed. */
if (xevent->atom == XInternAtom (xevent->display, "_NET_WM_NAME", False)) {
embed->monitor_saw_net_wm_name = TRUE;
embed_update_label (embed);
@@ -470,6 +525,7 @@ embed_plug_filter (XPropertyEvent *xevent, GdkEvent *_, EmbedPlugin *embed)
embed_update_label (embed);
}
} else if (xevent->type == UnmapNotify || xevent->type == DestroyNotify) {
+ /* The plug window was destroyed, and not by us! */
DBG ("destroyed");
embed_destroyed (embed);
}
@@ -478,14 +534,20 @@ embed_plug_filter (XPropertyEvent *xevent, GdkEvent *_, EmbedPlugin *embed)
+/* Callback for when a plug is added. This is either automatically called by
+ * the GtkSocket, or manually when a fake socket embeds the plug.
+ * Does everything that should happen when a plug is added, including all UI and
+ * monitoring/polling changes. */
static void
embed_plug_added (GtkWidget *socket, EmbedPlugin *embed)
{
DBG (".");
+ /* Flip the menu items */
gtk_widget_hide (embed->embed_menu);
gtk_widget_show (embed->popout_menu);
+ /* Stop any searching that is going on */
embed_stop_search (embed);
/* If we just got plugged by a gtkplug, set the "known" width and height to -1
@@ -497,13 +559,21 @@ embed_plug_added (GtkWidget *socket, EmbedPlugin *embed)
/* Monitor the plug for destruction, along with title changes if we need it
* for the label. */
if (embed->plug) {
+ /* Construct a GdkWindow wrapper around the plug window.
+ * Throughout here we must use GDK's Display reference, not our own, since
+ * GDK uses this to determine whether to pass on events or not. */
embed->plug_window = gdk_x11_window_foreign_new_for_display (
gdk_display_get_default (), embed->plug);
if (embed->plug_window) {
+ /* Monitor for unmap/destroy events. If the label is based on the title,
+ * also monitor property change events. */
glong monitor_mask = StructureNotifyMask;
if (embed->label_fmt && strstr (embed->label_fmt, EMBED_LABEL_FMT_TITLE))
monitor_mask |= PropertyChangeMask;
+ /* Reset the _NET_WM_NAME detector. */
embed->monitor_saw_net_wm_name = FALSE;
+ /* Set up the callback function, and start monitoring for the specified X
+ * events. */
gdk_window_add_filter (embed->plug_window,
(GdkFilterFunc)embed_plug_filter, embed);
XSelectInput (gdk_x11_get_default_xdisplay (),
@@ -520,16 +590,22 @@ embed_plug_added (GtkWidget *socket, EmbedPlugin *embed)
+/* Callback wrapper function for embed_add_socket. */
static gboolean
embed_add_socket_and_resize (EmbedPlugin *embed)
{
- DBG (".");
embed_add_socket (embed, TRUE);
return FALSE;
}
+/* Callback for when a plug is removed. This is either automatically called by
+ * the GtkSocket, or manually when a plug is popped out or destroyed.
+ * Does everything that should happen when a plug is removed, including all UI
+ * and monitoring/polling changes.
+ * Assumes that the GtkSocket is about to be destroyed right after the function
+ * returns, so it adds an idle callback that creates a new one. */
static gboolean
embed_plug_removed (GtkWidget *socket, EmbedPlugin *embed)
{
@@ -537,9 +613,14 @@ embed_plug_removed (GtkWidget *socket, EmbedPlugin *embed)
g_assert (embed->socket);
+ /* Flip the menu items */
gtk_widget_hide (embed->popout_menu);
gtk_widget_show (embed->embed_menu);
+
+ /* Assume the socket will be destroyed after this returns, so get rid of our
+ * reference. */
embed->socket = NULL;
+ /* Stop monitoring the plug window for changes */
if (embed->plug_window) {
XSelectInput (gdk_x11_get_default_xdisplay (), embed->plug, 0);
gdk_window_remove_filter (embed->plug_window,
@@ -547,9 +628,11 @@ embed_plug_removed (GtkWidget *socket, EmbedPlugin *embed)
g_object_unref (embed->plug_window);
embed->plug_window = NULL;
}
+ /* Reset info */
embed->plug = 0;
embed->plug_is_gtkplug = TRUE;
embed_update_label (embed);
+ /* Create a new socket once this one is destroyed */
g_idle_add ((GSourceFunc)embed_add_socket_and_resize, embed);
/* Returning false will destroy the socket */
return FALSE;
@@ -557,19 +640,26 @@ embed_plug_removed (GtkWidget *socket, EmbedPlugin *embed)
+/* Callback for when the size of the socket is determined.
+ * Used to resize the embedded window to match the socket. */
static void
embed_size_allocate (GtkSocket *socket, GdkRectangle *allocation,
EmbedPlugin *embed)
{
if (!embed->plug || embed->plug_is_gtkplug)
return;
- DBG (".");
+ /* Only manually resize if the embedded window is just a normal window. */
resize_window (embed->disp, embed->plug,
allocation->width, allocation->height);
}
+/* Adds a GtkSocket to the plugin and hooks up the signals, optionally updating
+ * the size of the plugin to match. Generally update_size should be true unless
+ * the plugin is being initialized.
+ * Once the GtkSocket is realized, a search is started. This can be prevented by
+ * setting embed->disable_search to true. */
static void
embed_add_socket (EmbedPlugin *embed, gboolean update_size)
{
@@ -584,33 +674,57 @@ embed_add_socket (EmbedPlugin *embed, gboolean update_size)
g_signal_connect (G_OBJECT (embed->socket), "realize",
G_CALLBACK (embed_start_search), embed);
+ /* Add it to the plugin hvbox */
gtk_widget_show (embed->socket);
gtk_box_pack_start (GTK_BOX (embed->hvbox), embed->socket, TRUE, TRUE, 0);
+ /* Update the size of the plugin if needed. */
if (update_size)
embed_size_changed_simple (embed);
}
+/* Creates a simple GTK widget (GtkDrawingArea) to dumbly house the embedded
+ * plug window, and reparents the plug window. This is done because a GtkSocket
+ * will destroy a plug when it is removed, which doesn't play nice when we're
+ * embedding random (unsuspecting) windows.
+ * Assumes the true GtkSocket was already destroyed. */
static void
embed_add_fake_socket (EmbedPlugin *embed)
{
+ /* GtkDrawingArea is pretty much as simple as you can get, and it is not a
+ * container (since containers would destroy their children when destroyed) */
embed->socket = gtk_drawing_area_new ();
+ /* We use the size-allocate signal to keep the size of the plug up-to-date. */
g_signal_connect (G_OBJECT (embed->socket), "size-allocate",
G_CALLBACK (embed_size_allocate), embed);
+ /* Add it to the plugin hvbox */
gtk_widget_show (embed->socket);
gtk_box_pack_start (GTK_BOX (embed->hvbox), embed->socket, TRUE, TRUE, 0);
+
+ /* Embed the plug window.
+ * First we have to ensure that it is on the current workspace, otherwise
+ * things tend to break (the window gets separated from the decorations) */
show_window (embed->disp, embed->plug);
reparent_window (embed->disp, embed->plug,
gdk_x11_drawable_get_xid (gtk_widget_get_window (embed->socket)), 0, 0);
+
embed_plug_added (embed->socket, embed);
}
+/* Pops out any plugs that are embedded and destroys the socket.
+ * This is called automatically by the popout menu item, and can be called
+ * manually even if no plug is actually embedded at the time.
+ * If the plug is just a normal window, the window will be reparented to the top
+ * level and its size restored.
+ * Since we assume the pop out was manual, we also disable searching for a new
+ * plug to embed until the user intervenes again.
+ */
static void
embed_popout (GtkMenuItem *popout_menu, EmbedPlugin *embed)
{
@@ -633,6 +747,9 @@ embed_popout (GtkMenuItem *popout_menu, EmbedPlugin *embed)
}
+/* Callback for when the embedded plug window is ungracefully destroyed, not by
+ * our doing, and not as part of XEmbed.
+ * Calls the removed callback and destroys the socket. */
static void
embed_destroyed (EmbedPlugin *embed)
{
@@ -645,10 +762,16 @@ embed_destroyed (EmbedPlugin *embed)
+/* X11 event monitor for the root window. Detects when windows become managed by
+ * the window manager so that we can scan for matches. */
static GdkFilterReturn
embed_root_filter (XPropertyEvent *xevent, GdkEvent *_, EmbedPlugin *embed)
{
- if (xevent->type == PropertyNotify) {
+ if (!embed->plug && xevent->type == PropertyNotify) {
+ /* To avoid double-handling window list changes, if we see a
+ * _NET_CLIENT_LIST we will stop responding to _WIN_CLIENT_LIST events.
+ * This is reset every time a new search is started, in case the window
+ * manager changed. */
if (xevent->atom == XInternAtom (xevent->display,
"_NET_CLIENT_LIST", False)) {
embed->monitor_saw_net_client_list = TRUE;
@@ -664,6 +787,7 @@ embed_root_filter (XPropertyEvent *xevent, GdkEvent *_, EmbedPlugin *embed)
+/* Entry point for the panel plugin. */
static void
embed_construct (XfcePanelPlugin *plugin)
{
@@ -716,7 +840,8 @@ embed_construct (XfcePanelPlugin *plugin)
g_signal_connect (G_OBJECT (plugin), "about",
G_CALLBACK (embed_about), NULL);
- /* Register our own event filter to avoid having to poll X11 properties. */
+ /* Register our own event filter to avoid having to poll X11 properties.
+ * No events will actually trigger until we call XSelectInput elsewhere. */
gdk_window_add_filter (gdk_get_default_root_window (),
(GdkFilterFunc)embed_root_filter, embed);
}
diff --git a/panel-plugin/ewmh.c b/panel-plugin/ewmh.c
index 3b62487..e65e61a 100644
--- a/panel-plugin/ewmh.c
+++ b/panel-plugin/ewmh.c
@@ -65,7 +65,8 @@ static gchar *get_property (Display *disp, Window win, Atom xa_prop_type,
* APIs, even when long was changed to 64 bits.
*
*/
- if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False,
+ 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) {
DBG("Cannot get %s property.\n", prop_name);
@@ -121,7 +122,7 @@ Window *get_client_list (Display *disp, gulong *size)
if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
XA_WINDOW, "_NET_CLIENT_LIST", size)) == NULL) {
- if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
+ if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
XA_CARDINAL, "_WIN_CLIENT_LIST", size)) == NULL) {
DBG("Cannot get client list properties. \n"
"(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)\n");
More information about the Xfce4-commits
mailing list