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 (); }
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 (); }
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; }
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 (); }
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 (); }
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; }
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; }