Example #1
0
void
scrolling_1 (struct frame *frame, int window_size, int unchanged_at_top,
	     int unchanged_at_bottom, int *draw_cost, int *old_draw_cost,
	     unsigned *old_hash, unsigned *new_hash, int free_at_end)
{
  USE_SAFE_ALLOCA;
  struct matrix_elt *matrix;
  SAFE_NALLOCA (matrix, window_size + 1, window_size + 1);

  if (FRAME_SCROLL_REGION_OK (frame))
    {
      calculate_direct_scrolling (frame, matrix, window_size,
				  unchanged_at_bottom,
				  draw_cost, old_draw_cost,
				  old_hash, new_hash, free_at_end);
      do_direct_scrolling (frame, frame->current_matrix,
			   matrix, window_size, unchanged_at_top);
    }
  else
    {
      calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
			   draw_cost, old_hash, new_hash,
			   free_at_end);
      do_scrolling (frame,
                    frame->current_matrix, matrix, window_size,
		    unchanged_at_top);
    }

  SAFE_FREE ();
}
Example #2
0
File: menu.c Project: ueno/emacs
void
find_and_call_menu_selection (struct frame *f, int menu_bar_items_used,
			      Lisp_Object vector, void *client_data)
{
  Lisp_Object prefix, entry;
  Lisp_Object *subprefix_stack;
  int submenu_depth = 0;
  int i;
  USE_SAFE_ALLOCA;

  entry = Qnil;
  SAFE_NALLOCA (subprefix_stack, 1, menu_bar_items_used);
  prefix = Qnil;
  i = 0;

  while (i < menu_bar_items_used)
    {
      if (EQ (AREF (vector, i), Qnil))
	{
	  subprefix_stack[submenu_depth++] = prefix;
	  prefix = entry;
	  i++;
	}
      else if (EQ (AREF (vector, i), Qlambda))
	{
	  prefix = subprefix_stack[--submenu_depth];
	  i++;
	}
      else if (EQ (AREF (vector, i), Qt))
	{
	  prefix = AREF (vector, i + MENU_ITEMS_PANE_PREFIX);
	  i += MENU_ITEMS_PANE_LENGTH;
	}
      else
	{
	  entry = AREF (vector, i + MENU_ITEMS_ITEM_VALUE);
	  /* Treat the pointer as an integer.  There's no problem
	     as long as pointers have enough bits to hold small integers.  */
	  if ((intptr_t) client_data == i)
	    {
	      int j;
	      struct input_event buf;
	      Lisp_Object frame;
	      EVENT_INIT (buf);

	      XSETFRAME (frame, f);
	      buf.kind = MENU_BAR_EVENT;
	      buf.frame_or_window = frame;
	      buf.arg = frame;
	      kbd_buffer_store_event (&buf);

	      for (j = 0; j < submenu_depth; j++)
		if (!NILP (subprefix_stack[j]))
		  {
		    buf.kind = MENU_BAR_EVENT;
		    buf.frame_or_window = frame;
		    buf.arg = subprefix_stack[j];
		    kbd_buffer_store_event (&buf);
		  }

	      if (!NILP (prefix))
		{
		  buf.kind = MENU_BAR_EVENT;
		  buf.frame_or_window = frame;
		  buf.arg = prefix;
		  kbd_buffer_store_event (&buf);
		}

	      buf.kind = MENU_BAR_EVENT;
	      buf.frame_or_window = frame;
	      buf.arg = entry;
	      kbd_buffer_store_event (&buf);

	      break;
	    }
	  i += MENU_ITEMS_ITEM_LENGTH;
	}
    }

  SAFE_FREE ();
}
Example #3
0
File: menu.c Project: ueno/emacs
widget_value *
digest_single_submenu (int start, int end, bool top_level_items)
{
  widget_value *wv, *prev_wv, *save_wv, *first_wv;
  int i;
  int submenu_depth = 0;
  widget_value **submenu_stack;
  bool panes_seen = 0;
  struct frame *f = XFRAME (Vmenu_updating_frame);
  USE_SAFE_ALLOCA;

  SAFE_NALLOCA (submenu_stack, 1, menu_items_used);
  wv = make_widget_value ("menu", NULL, true, Qnil);
  wv->button_type = BUTTON_TYPE_NONE;
  first_wv = wv;
  save_wv = 0;
  prev_wv = 0;

  /* Loop over all panes and items made by the preceding call
     to parse_single_submenu and construct a tree of widget_value objects.
     Ignore the panes and items used by previous calls to
     digest_single_submenu, even though those are also in menu_items.  */
  i = start;
  while (i < end)
    {
      if (EQ (AREF (menu_items, i), Qnil))
	{
	  submenu_stack[submenu_depth++] = save_wv;
	  save_wv = prev_wv;
	  prev_wv = 0;
	  i++;
	}
      else if (EQ (AREF (menu_items, i), Qlambda))
	{
	  prev_wv = save_wv;
	  save_wv = submenu_stack[--submenu_depth];
	  i++;
	}
      else if (EQ (AREF (menu_items, i), Qt)
	       && submenu_depth != 0)
	i += MENU_ITEMS_PANE_LENGTH;
      /* Ignore a nil in the item list.
	 It's meaningful only for dialog boxes.  */
      else if (EQ (AREF (menu_items, i), Qquote))
	i += 1;
      else if (EQ (AREF (menu_items, i), Qt))
	{
	  /* Create a new pane.  */
	  Lisp_Object pane_name;
	  const char *pane_string;

	  panes_seen = 1;

	  pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);

	  /* TTY menus display menu items via tty_write_glyphs, which
	     will encode the strings as appropriate.  */
	  if (!FRAME_TERMCAP_P (f))
	    {
#ifdef HAVE_NTGUI
	      if (STRINGP (pane_name))
		{
		  if (unicode_append_menu)
		    /* Encode as UTF-8 for now.  */
		    pane_name = ENCODE_UTF_8 (pane_name);
		  else if (STRING_MULTIBYTE (pane_name))
		    pane_name = ENCODE_SYSTEM (pane_name);

		  ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
		}
#elif defined (USE_LUCID) && defined (HAVE_XFT)
	      if (STRINGP (pane_name))
		{
		  pane_name = ENCODE_UTF_8 (pane_name);
		  ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
		}
#elif !defined (HAVE_MULTILINGUAL_MENU)
	      if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
		{
		  pane_name = ENCODE_MENU_STRING (pane_name);
		  ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
		}
#endif
	    }

	  pane_string = (NILP (pane_name)
			 ? "" : SSDATA (pane_name));
	  /* If there is just one top-level pane, put all its items directly
	     under the top-level menu.  */
	  if (menu_items_n_panes == 1)
	    pane_string = "";

	  /* If the pane has a meaningful name,
	     make the pane a top-level menu item
	     with its items as a submenu beneath it.  */
	  if (strcmp (pane_string, ""))
	    {
	      /* Set value to 1 so update_submenu_strings can handle '@'.  */
	      wv = make_widget_value (NULL, (char *) 1, true, Qnil);
	      if (save_wv)
		save_wv->next = wv;
	      else
		first_wv->contents = wv;
	      wv->lname = pane_name;
	      wv->button_type = BUTTON_TYPE_NONE;
	      save_wv = wv;
	    }
	  else
	    save_wv = first_wv;

	  prev_wv = 0;
	  i += MENU_ITEMS_PANE_LENGTH;
	}
      else
	{
	  /* Create a new item within current pane.  */
	  Lisp_Object item_name, enable, descrip, def, type, selected;
	  Lisp_Object help;

	  /* All items should be contained in panes.  */
	  if (! panes_seen)
	    emacs_abort ();

	  item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
	  enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
	  descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
	  def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
	  type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
	  selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
	  help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);

	  /* TTY menu items and their descriptions will be encoded by
	     tty_write_glyphs.  */
	  if (!FRAME_TERMCAP_P (f))
	    {
#ifdef HAVE_NTGUI
	      if (STRINGP (item_name))
		{
		  if (unicode_append_menu)
		    item_name = ENCODE_UTF_8 (item_name);
		  else if (STRING_MULTIBYTE (item_name))
		    item_name = ENCODE_SYSTEM (item_name);

		  ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
		}

	      if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
		{
		  descrip = ENCODE_SYSTEM (descrip);
		  ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
		}
#elif USE_LUCID
	      if (STRINGP (item_name))
		{
		  item_name = ENCODE_UTF_8 (item_name);
		  ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
		}

	      if (STRINGP (descrip))
		{
		  descrip = ENCODE_UTF_8 (descrip);
		  ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
		}
#elif !defined (HAVE_MULTILINGUAL_MENU)
	      if (STRING_MULTIBYTE (item_name))
		{
		  item_name = ENCODE_MENU_STRING (item_name);
		  ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
		}

	      if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
		{
		  descrip = ENCODE_MENU_STRING (descrip);
		  ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
		}
#endif
	    }

	  wv = make_widget_value (NULL, NULL, !NILP (enable),
				  STRINGP (help) ? help : Qnil);
	  if (prev_wv)
	    prev_wv->next = wv;
	  else
	    save_wv->contents = wv;

	  wv->lname = item_name;
	  if (!NILP (descrip))
	    wv->lkey = descrip;
	  /* The intptr_t cast avoids a warning.  There's no problem
	     as long as pointers have enough bits to hold small integers.  */
	  wv->call_data = (!NILP (def) ? (void *) (intptr_t) i : 0);

	  if (NILP (type))
	    wv->button_type = BUTTON_TYPE_NONE;
	  else if (EQ (type, QCradio))
	    wv->button_type = BUTTON_TYPE_RADIO;
	  else if (EQ (type, QCtoggle))
	    wv->button_type = BUTTON_TYPE_TOGGLE;
	  else
	    emacs_abort ();

	  wv->selected = !NILP (selected);

	  prev_wv = wv;

	  i += MENU_ITEMS_ITEM_LENGTH;
	}
    }

  /* If we have just one "menu item"
     that was originally a button, return it by itself.  */
  if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
    {
      wv = first_wv;
      first_wv = first_wv->contents;
      xfree (wv);
    }

  SAFE_FREE ();
  return first_wv;
}
Example #4
0
static void
do_direct_scrolling (struct frame *frame, struct glyph_matrix *current_matrix,
		     struct matrix_elt *cost_matrix, int window_size,
		     int unchanged_at_top)
{
  struct matrix_elt *p;
  int i, j;
  USE_SAFE_ALLOCA;

  /* A queue of deletions and insertions to be performed.  */
  struct alt_queue { int count, pos, window; };
  struct alt_queue *queue_start;
  SAFE_NALLOCA (queue_start, 1, window_size);
  struct alt_queue *queue = queue_start;

  /* True if a terminal window has been set with set_terminal_window.  */
  bool terminal_window_p = 0;

  /* If true, a write has been selected, allowing either an insert or a
     delete to be selected next.  If false, a delete cannot be selected
     unless j < i, and an insert cannot be selected unless i < j.
     This corresponds to a similar restriction (with the ordering
     reversed) in calculate_direct_scrolling, which is intended to
     ensure that lines marked as inserted will be blank. */
  bool write_follows_p = 1;

  /* For each row in the new matrix what row of the old matrix it is.  */
  int *copy_from;
  SAFE_NALLOCA (copy_from, 1, window_size);

  /* Non-zero for each row in the new matrix that is retained from the
     old matrix.  Lines not retained are empty.  */
  char *retained_p = SAFE_ALLOCA (window_size);

  memset (retained_p, 0, window_size * sizeof (char));

  /* Perform some sanity checks when GLYPH_DEBUG is on.  */
  CHECK_MATRIX (current_matrix);

  /* We are working on the line range UNCHANGED_AT_TOP ...
     UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX.
     We step through lines in this range from the end to the start.  I
     is an index into new lines, j an index into old lines.  The cost
     matrix determines what to do for ranges of indices.

     If i is decremented without also decrementing j, this corresponds
     to inserting empty lines in the result.  If j is decremented
     without also decrementing i, this corresponds to omitting these
     lines in the new rows, i.e. rows are deleted.  */
  i = j = window_size;

  while (i > 0 || j > 0)
    {
      p = cost_matrix + i * (window_size + 1) + j;

      if (p->insertcost < p->writecost
	  && p->insertcost < p->deletecost
	  && (write_follows_p || i < j))
	{
	  /* Insert is cheaper than deleting or writing lines.  Leave
	     a hole in the result display that will be filled with
	     empty lines when the queue is emptied.  */
	  queue->count = 0;
	  queue->window = i;
	  queue->pos = i - p->insertcount;
	  ++queue;

	  i -= p->insertcount;
	  write_follows_p = 0;
	}
      else if (p->deletecost < p->writecost
	       && (write_follows_p || i > j))
	{
	  /* Deleting lines is cheaper.  By decrementing J, omit
	     deletecount lines from the original.  */
	  write_follows_p = 0;
	  j -= p->deletecount;
	}
      else
	{
	  /* One or more lines should be written.  In the direct
	     scrolling method we do this by scrolling the lines to the
	     place they belong.  */
	  int n_to_write = p->writecount;
	  write_follows_p = 1;
	  eassert (n_to_write > 0);

	  if (i > j)
	    {
	      /* Immediately insert lines */
	      set_terminal_window (frame, i + unchanged_at_top);
	      terminal_window_p = 1;
	      ins_del_lines (frame, j - n_to_write + unchanged_at_top, i - j);
	    }
	  else if (i < j)
	    {
	      /* Queue the deletion of a group of lines */
	      queue->pos = i - n_to_write + unchanged_at_top;
	      queue->window = j + unchanged_at_top;
	      queue->count = i - j;
	      ++queue;
	    }

	  while (n_to_write > 0)
	    {
	      --i, --j, --n_to_write;
	      copy_from[i] = j;
	      retained_p[j] = 1;
	    }
	}
    }

  /* Do queued operations.  */
  if (queue > queue_start)
    {
      int next = -1;

      do
	{
	  --queue;
	  if (queue->count)
	    {
	      set_terminal_window (frame, queue->window);
	      terminal_window_p = 1;
	      ins_del_lines (frame, queue->pos, queue->count);
	    }
	  else
	    {
	      for (j = queue->window - 1; j >= queue->pos; --j)
		{
		  while (retained_p[++next])
		    ;
		  copy_from[j] = next;
		}
	    }
	}
      while (queue > queue_start);
    }

  /* Now, for each row I in the range of rows we are working on,
     copy_from[i] gives the original line to copy to I, and
     retained_p[copy_from[i]] is zero if line I in the new display is
     empty.  */
  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
		       copy_from, retained_p);

  if (terminal_window_p)
    set_terminal_window (frame, 0);
  SAFE_FREE ();
}
Example #5
0
static void
do_scrolling (struct frame *frame, struct glyph_matrix *current_matrix,
              struct matrix_elt *matrix, int window_size,
              int unchanged_at_top)
{
  struct matrix_elt *p;
  int i, j, k;
  USE_SAFE_ALLOCA;

  /* True if we have set a terminal window with set_terminal_window.  */
  bool terminal_window_p = 0;

  /* A queue for line insertions to be done.  */
  struct queue { int count, pos; };
  struct queue *queue_start;
  SAFE_NALLOCA (queue_start, 1, current_matrix->nrows);
  struct queue *queue = queue_start;

  char *retained_p = SAFE_ALLOCA (window_size);
  int *copy_from;
  SAFE_NALLOCA (copy_from, 1, window_size);

  /* Zero means line is empty.  */
  memset (retained_p, 0, window_size * sizeof (char));
  for (k = 0; k < window_size; ++k)
    copy_from[k] = -1;

#ifdef GLYPH_DEBUG
# define CHECK_BOUNDS							\
  do									\
    {									\
      int ck;								\
      for (ck = 0; ck < window_size; ++ck)				\
	eassert (copy_from[ck] == -1					\
		 || (copy_from[ck] >= 0 && copy_from[ck] < window_size)); \
    }									\
  while (0);
#endif

  /* When j is advanced, this corresponds to deleted lines.
     When i is advanced, this corresponds to inserted lines.  */
  i = j = window_size;
  while (i > 0 || j > 0)
    {
      p = matrix + i * (window_size + 1) + j;

      if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
	{
	  /* Insert should be done at vpos i-1, plus maybe some before.
	     Queue the screen operation to be performed.  */
	  queue->count = p->insertcount;
	  queue->pos = i + unchanged_at_top - p->insertcount;
	  ++queue;

	  /* By incrementing I, we leave room in the result rows
	     for the empty rows opened up.  */
	  i -= p->insertcount;
	}
      else if (p->deletecost < p->writecost)
	{
	  /* Old line at vpos j-1, and maybe some before it, should be
	     deleted.  By decrementing J, we skip some lines in the
	     temp_rows which is equivalent to omitting these lines in
	     the result rows, thus deleting them.  */
	  j -= p->deletecount;

	  /* Set the terminal window, if not done already.  */
 	  if (! terminal_window_p)
	    {
	      set_terminal_window (frame, window_size + unchanged_at_top);
	      terminal_window_p = 1;
	    }

	  /* Delete lines on the terminal.  */
	  ins_del_lines (frame, j + unchanged_at_top, - p->deletecount);
	}
      else
	{
	  /* Best thing done here is no insert or delete, i.e. a write.  */
	  --i, --j;
	  eassert (i >= 0 && i < window_size);
	  eassert (j >= 0 && j < window_size);
	  copy_from[i] = j;
	  retained_p[j] = 1;

#ifdef GLYPH_DEBUG
	  CHECK_BOUNDS;
#endif
	}
    }

  /* Now do all insertions queued above.  */
  if (queue > queue_start)
    {
      int next = -1;

      /* Set the terminal window if not yet done.  */
      if (!terminal_window_p)
	{
	  set_terminal_window (frame, window_size + unchanged_at_top);
	  terminal_window_p = 1;
	}

      do
	{
	  --queue;

	  /* Do the deletion on the terminal.  */
	  ins_del_lines (frame, queue->pos, queue->count);

	  /* All lines in the range deleted become empty in the glyph
	     matrix.  Assign to them glyph rows that are not retained.
	     K is the starting position of the deleted range relative
	     to the window we are working in.  */
	  k = queue->pos - unchanged_at_top;
	  for (j = 0; j < queue->count; ++j)
	    {
	      /* Find the next row not retained.  */
	      while (retained_p[++next])
		;

	      /* Record that this row is to be used for the empty
		 glyph row j.  */
	      copy_from[k + j] = next;
	    }
	}
      while (queue > queue_start);

    }

  for (k = 0; k < window_size; ++k)
    eassert (copy_from[k] >= 0 && copy_from[k] < window_size);

  /* Perform the row swizzling.  */
  mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
		       copy_from, retained_p);

  /* Some sanity checks if GLYPH_DEBUG is defined.  */
  CHECK_MATRIX (current_matrix);

  if (terminal_window_p)
    set_terminal_window (frame, 0);
  SAFE_FREE ();
}
Example #6
0
int
xg_select (int fds_lim, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
	   EMACS_TIME *timeout, sigset_t *sigmask)
{
  SELECT_TYPE all_rfds, all_wfds;
  EMACS_TIME tmo, *tmop = timeout;

  GMainContext *context;
  int have_wfds = wfds != NULL;
  GPollFD gfds_buf[128];
  GPollFD *gfds = gfds_buf;
  int gfds_size = sizeof gfds_buf / sizeof *gfds_buf;
  int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
  int i, nfds, tmo_in_millisec;
  USE_SAFE_ALLOCA;

  if (! (x_in_use
	 && g_main_context_pending (context = g_main_context_default ())))
    return pselect (fds_lim, rfds, wfds, efds, timeout, sigmask);

  if (rfds) all_rfds = *rfds;
  else FD_ZERO (&all_rfds);
  if (wfds) all_wfds = *wfds;
  else FD_ZERO (&all_wfds);

  n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				 gfds, gfds_size);
  if (gfds_size < n_gfds)
    {
      SAFE_NALLOCA (gfds, sizeof *gfds, n_gfds);
      gfds_size = n_gfds;
      n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				     gfds, gfds_size);
    }

  for (i = 0; i < n_gfds; ++i)
    {
      if (gfds[i].events & G_IO_IN)
        {
          FD_SET (gfds[i].fd, &all_rfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
        }
      if (gfds[i].events & G_IO_OUT)
        {
          FD_SET (gfds[i].fd, &all_wfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
          have_wfds = 1;
        }
    }

  SAFE_FREE ();

  if (tmo_in_millisec >= 0)
    {
      tmo = make_emacs_time (tmo_in_millisec / 1000,
			     1000 * 1000 * (tmo_in_millisec % 1000));
      if (!timeout || EMACS_TIME_LT (tmo, *timeout))
	tmop = &tmo;
    }

  fds_lim = max_fds + 1;
  nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
		  efds, tmop, sigmask);

  if (nfds < 0)
    retval = nfds;
  else if (nfds > 0)
    {
      for (i = 0; i < fds_lim; ++i)
        {
          if (FD_ISSET (i, &all_rfds))
            {
              if (rfds && FD_ISSET (i, rfds)) ++retval;
              else ++our_fds;
            }
          else if (rfds)
            FD_CLR (i, rfds);

          if (have_wfds && FD_ISSET (i, &all_wfds))
            {
              if (wfds && FD_ISSET (i, wfds)) ++retval;
              else ++our_fds;
            }
          else if (wfds)
            FD_CLR (i, wfds);

          if (efds && FD_ISSET (i, efds))
            ++retval;
        }
    }

  if (our_fds > 0 || (nfds == 0 && tmop == &tmo))
    {

      /* If Gtk+ is in use eventually gtk_main_iteration will be called,
         unless retval is zero.  */
#ifdef USE_GTK
      if (retval == 0)
#endif
        while (g_main_context_pending (context))
          g_main_context_dispatch (context);

      /* To not have to recalculate timeout, return like this.  */
      if (retval == 0)
        {
          retval = -1;
          errno = EINTR;
        }
    }

  return retval;
}
Example #7
0
int
xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
	   struct timespec const *timeout, sigset_t const *sigmask)
{
  fd_set all_rfds, all_wfds;
  struct timespec tmo;
  struct timespec const *tmop = timeout;

  GMainContext *context;
  int have_wfds = wfds != NULL;
  GPollFD gfds_buf[128];
  GPollFD *gfds = gfds_buf;
  int gfds_size = ARRAYELTS (gfds_buf);
  int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
  int i, nfds, tmo_in_millisec;
  bool need_to_dispatch;
  USE_SAFE_ALLOCA;

  context = g_main_context_default ();

  if (rfds) all_rfds = *rfds;
  else FD_ZERO (&all_rfds);
  if (wfds) all_wfds = *wfds;
  else FD_ZERO (&all_wfds);

  n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				 gfds, gfds_size);
  if (gfds_size < n_gfds)
    {
      SAFE_NALLOCA (gfds, sizeof *gfds, n_gfds);
      gfds_size = n_gfds;
      n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				     gfds, gfds_size);
    }

  for (i = 0; i < n_gfds; ++i)
    {
      if (gfds[i].events & G_IO_IN)
        {
          FD_SET (gfds[i].fd, &all_rfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
        }
      if (gfds[i].events & G_IO_OUT)
        {
          FD_SET (gfds[i].fd, &all_wfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
          have_wfds = 1;
        }
    }

  SAFE_FREE ();

  if (tmo_in_millisec >= 0)
    {
      tmo = make_timespec (tmo_in_millisec / 1000,
			   1000 * 1000 * (tmo_in_millisec % 1000));
      if (!timeout || timespec_cmp (tmo, *timeout) < 0)
	tmop = &tmo;
    }

  fds_lim = max_fds + 1;
  nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
		  efds, tmop, sigmask);

  if (nfds < 0)
    retval = nfds;
  else if (nfds > 0)
    {
      for (i = 0; i < fds_lim; ++i)
        {
          if (FD_ISSET (i, &all_rfds))
            {
              if (rfds && FD_ISSET (i, rfds)) ++retval;
              else ++our_fds;
            }
          else if (rfds)
            FD_CLR (i, rfds);

          if (have_wfds && FD_ISSET (i, &all_wfds))
            {
              if (wfds && FD_ISSET (i, wfds)) ++retval;
              else ++our_fds;
            }
          else if (wfds)
            FD_CLR (i, wfds);

          if (efds && FD_ISSET (i, efds))
            ++retval;
        }
    }

  /* If Gtk+ is in use eventually gtk_main_iteration will be called,
     unless retval is zero.  */
#ifdef USE_GTK
  need_to_dispatch = retval == 0;
#else
  need_to_dispatch = true;
#endif
  if (need_to_dispatch)
    {
      int pselect_errno = errno;
      /* Prevent g_main_dispatch recursion, that would occur without
         block_input wrapper, because event handlers call
         unblock_input.  Event loop recursion was causing Bug#15801.  */
      block_input ();
      while (g_main_context_pending (context))
        g_main_context_dispatch (context);
      unblock_input ();
      errno = pselect_errno;
    }

  /* To not have to recalculate timeout, return like this.  */
  if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
    {
      retval = -1;
      errno = EINTR;
    }

  return retval;
}