#include <gtk/gtk.h>

GtkWidget *win, *popup, *entry;

static void
popup_position_func (GtkWidget *win,
                     GtkWidget *popup)
{
  gint x, y;
  gint win_x, win_y;
  gint win_width, win_height;
  GdkWindow *win_window;
  GdkScreen *screen;
  GtkRequisition requisition;
  gint monitor_num;
  GdkRectangle monitor;

  win_window = gtk_widget_get_window (win);
  screen = gdk_window_get_screen (win_window);

  monitor_num = gdk_screen_get_monitor_at_window (screen, win_window);
  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);

  gtk_widget_realize (popup);

  gdk_window_get_origin (win_window, &win_x, &win_y);
  win_width = gdk_window_get_width (win_window);
  win_height = gdk_window_get_height (win_window);

  gtk_widget_get_preferred_size (popup, &requisition, NULL);

  if (win_x + win_width > gdk_screen_get_width (screen)) {
    x = gdk_screen_get_width (screen) - requisition.width;
  } else if (win_x + win_width - requisition.width < 0) {
    x = 0;
  } else {
    x = win_x + win_width - requisition.width;
  }

  if (win_y + win_height + requisition.height > gdk_screen_get_height (screen)) {
    y = gdk_screen_get_height (screen) - requisition.height;
  } else if (win_y + win_height < 0) { /* isn't really possible ... */
    y = 0;
  } else {
    y = win_y + win_height;
  }

  /* This doesn't work */
  gtk_window_move (GTK_WINDOW (popup), x, y);

  /* The following works instead:
   * gdk_window_move (gtk_widget_get_window (popup), x, y);
   */
}

static gboolean
on_win_key_event (GtkWidget *w,
                  GdkEventKey *event)
{
  GdkEvent *new_event;
  GdkWindow *window;
  GdkScreen *screen;
  gboolean retval;

  new_event = gdk_event_copy ((GdkEvent *) event);
  window = ((GdkEventKey *) new_event)->window;
  ((GdkEventKey *) new_event)->window = gtk_widget_get_window (entry);
  gtk_widget_realize (popup);

  /* Move the entry off screen */
  screen = gtk_widget_get_screen (w);
  gtk_window_move (GTK_WINDOW (popup),
                   gdk_screen_get_width (screen) + 1,
                   gdk_screen_get_height (screen) + 1);
  gtk_widget_show (popup);

  /* Process the event off screen */
  retval = gtk_widget_event (entry, new_event);
  gtk_widget_hide (popup);

  if (retval) {
    popup_position_func (win, popup);
    gtk_widget_show (popup);
  }

  return TRUE;
}

static gboolean
on_entry_key_press_event (GtkWidget *w,
                          GdkEventKey *event)
{
  if (event->keyval == GDK_KEY_Escape) {
    gtk_widget_hide (popup);
    gtk_entry_set_text (GTK_ENTRY (entry), "");
  }

  return TRUE;
}

int
main (int argc,
      char **argv)
{
  gtk_init (&argc, &argv);

  win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_size_request (win, 400, 400);

  popup = gtk_window_new (GTK_WINDOW_POPUP);
  gtk_window_set_modal (GTK_WINDOW (popup), TRUE);
  gtk_window_set_type_hint (GTK_WINDOW (popup),
                            GDK_WINDOW_TYPE_HINT_COMBO);

  entry = gtk_entry_new ();
  gtk_widget_show (entry);
  gtk_container_add (GTK_CONTAINER (popup), entry);
  gtk_widget_realize (entry);
  g_signal_connect (entry, "key-press-event",
                    G_CALLBACK (on_entry_key_press_event), NULL);

  g_signal_connect (win, "key-press-event",
                    G_CALLBACK (on_win_key_event), NULL);
  g_signal_connect (win, "delete-event",
                    G_CALLBACK (gtk_main_quit), NULL);

  gtk_widget_show (win);
  gtk_main();

  return 0;
}
