That totally makes sense. Thank you. I hadn't thought about the interaction of signals rudely interrupting the main loop.
The funny thing is that this trick completely works if I make gtk_window, populate it with widgets (buttons and stuff), and widget_show() everything but the window. When the SIGUSR1 comes through, it just calls widget_show() on the top level window which has GTK_WIN_POS_MOUSE set. Bang. It pops up at the mouse. Lovely. Grab some signals and ESC/F10, button-press or leave-notify makes it disappear.
Menus behave differently. A lot of their function is controlled internally, unlike a button which is mostly user-controlled. With a menu, gtk_widget_show() isn't good enough, you must call menu_popup() which then pretty much takes over control.
For now, as an experiment, I skip the SIGUSR1 bit and use a different approach. In my gtk_window/button version of this app, I have also installed a g_timeout_add() that lazily polls the pointer every 10th of a second:
Code:
#define TIMER_DELAY 100
dmenu.timer =
g_timeout_add(TIMER_DELAY, (GSourceFunc) desktop_timer, menu1);
and
Code:
gint desktop_timer(GtkWidget * win)
{
static int count = 1;
static Display *display;
static gint screen;
static Window w, thisw = 0;
Window root, child;
int rx, ry, wx, wy;
unsigned int mask;
if ( ! dmenu.win_visible ) {
if (!display) {
display = XOpenDisplay(NULL);
screen = XDefaultScreen(display);
w = XRootWindow(display, screen);
}
XQueryPointer(display, w, &root, &child, &rx, &ry, &wx, &wy, &mask);
fprintf ( stderr, "root:%p child:%p rx:%i ry:%i wx:%i wy:%i mask:%i count:%i\n",
root, child, rx, ry, wx, wy, mask, count );
menuX = rx; menuY = ry;
if (child == 0) { // we're in the root window
if (mask == Button3Mask) { // X.h: Button3Mask 1<<10, no modifiers
window_show(GTK_WIDGET(win), NULL, (gpointer) win);
}
} else {
if (!thisw) {
thisw = child;
}
}
}
if ( (count%100) == 0 ) {
window_show(GTK_WIDGET(win), NULL, (gpointer) win);
}
count++;
return TRUE;
}
window_show() is designed as a signal callback, and just sets a couple flags then calls gtk_widget_show_all() or gtk_menu_popup(), whichever is appropriate.
What is supposed to happen here is that when you right click an empty spot of the root window, my button window pops up under the mouse. No problem. Not so when it's a menu.
Now, if you'll notice, I've hacked this routine to count, and every 100(~10 seconds) it shows the menu. Strangely this works perfectly. Every 10 seconds gtk_menu_popup() does it's thing, and my gtk_menu pops up under your mouse. Awesome. But when I right click, it still calls menu_popup() but fails.
What I've discovered is that menu_popup() succeeds when it's called passively from the app, but but fails when called actively from UNIX signals or mouse clicks. It seems that all the gtk_menu's automagic gets confused by the spurious events going on.
This ties right into all the chat I've seen about gtk-menu-popup-delay and getting it set just right. I was curious if this might help. Perhaps giving a small delay to the popup would lets things clear. So I tried a trick someone said. echo "gtk-menu-popup-delay = 10000" >> ~/.gtkrc-2.0. But that doesn't seem to do anything. I tried usleep(100000) right before calling gtk_menu_popup(), and you know what? *IT WORKS*! But only for right-click. Still not for SIGUSR1.
So, I decided to try another trick. In my signal handler, instead of just abruptly calling window_show(), I did this:
Code:
g_timeout_add(100, (GSourceFunc) window_show_timer, dmenu.window);
The main loop keeps right on rolling, and a moment later there is a friendly knock on the door to menu_popup(), and you know what? It now works for SIGUSR1 also.
So, after MUCH experimentation, I believe I found the official answer to my original query:
gtk_menu and friends get confused by spurious events, so if you're going to be calling gtk_menu_popup(), do it with a leisurely g_timeout_add() and a GSourceFunc that returns FALSE, so it only calls once. The correct timer delays seem to be 200 for SIGUSR1 and 100 for right click.
Perhaps someone else can benefit from this little tidbit of gtk esoterica.
Have a very Merry Christmas.