static int read_char(WINDOW *win, wint_t *c, int timeout) { static const int T = 150; static const int IPC_F = 10; int i; int result = ERR; for(i = 0; i <= timeout/T; i++) { int j; if(is_redraw_scheduled()) { modes_redraw(); } if(!is_status_bar_multiline() && !is_in_menu_like_mode() && get_mode() != CMDLINE_MODE) { check_if_filelists_have_changed(curr_view); if(curr_stats.number_of_windows != 1 && !curr_stats.view) check_if_filelists_have_changed(other_view); } check_background_jobs(); for(j = 0; j < IPC_F; j++) { ipc_check(); wtimeout(win, MIN(T, timeout)/IPC_F); if((result = wget_wch(win, c)) != ERR) break; if(is_redraw_scheduled()) { modes_redraw(); } } if(result != ERR) break; timeout -= T; } return result; }
/* Ensures that terminal is in proper state for a loop iteration. Returns * non-zero if so, otherwise zero is returned. */ static int ensure_term_is_ready(void) { ui_update_term_state(); update_terminal_settings(); if(curr_stats.term_state == TS_TOO_SMALL) { ui_display_too_small_term_msg(); wait_for_signal(); return 0; } if(curr_stats.term_state == TS_BACK_TO_NORMAL) { wint_t c; wtimeout(status_bar, 0); while(compat_wget_wch(status_bar, &c) != ERR); curr_stats.term_state = TS_NORMAL; modes_redraw(); wtimeout(status_bar, cfg.timeout_len); curr_stats.save_msg = 0; status_bar_message(""); } return 1; }
/* Ensures that terminal is in proper state for a loop iteration. Returns * non-zero if so, otherwise zero is returned. */ static int ensure_term_is_ready(void) { ui_update_term_state(); update_terminal_settings(); if(curr_stats.term_state == TS_TOO_SMALL) { ui_display_too_small_term_msg(); wait_for_signal(); return 0; } if(curr_stats.term_state == TS_BACK_TO_NORMAL) { ui_drain_input(); curr_stats.term_state = TS_NORMAL; modes_redraw(); curr_stats.save_msg = 0; ui_sb_msg(""); } return 1; }
/* Internal function for displaying error messages to a user. Automatically * skips whitespace in front of the message and does nothing for empty messages * (due to skipping whitespace-only are counted as empty). When the prompt_skip * isn't zero, asks user about successive messages. Returns non-zero if all * successive messages should be skipped, otherwise zero is returned. */ static int prompt_error_msg_internal(const char title[], const char message[], int prompt_skip) { static int skip_until_started; if(curr_stats.load_stage == 0) return 1; if(curr_stats.load_stage < 2 && skip_until_started) return 1; message = skip_whitespace(message); if(*message == '\0') { return 0; } msg_kind = D_ERROR; redraw_error_msg(title, message, prompt_skip, 0); enter(MASK(R_OK) | (prompt_skip ? MASK(R_CANCEL) : 0)); if(curr_stats.load_stage < 2) skip_until_started = (result == R_CANCEL); modes_update(); if(curr_stats.need_update != UT_NONE) modes_redraw(); return result == R_CANCEL; }
/* Updates TUI or it's elements if something is scheduled. */ static void process_scheduled_updates(void) { if(is_redraw_scheduled()) { modes_redraw(); } process_scheduled_updates_of_view(curr_view); process_scheduled_updates_of_view(other_view); }
/* Common implementation of prompt message. The variants can be NULL. */ static void prompt_msg_internal(const char title[], const char message[], const response_variant variants[]) { responses = variants; msg_kind = D_QUERY; redraw_error_msg(title, message, 0, 0); enter(variants == NULL ? MASK(R_YES, R_NO) : 0); modes_redraw(); }
void stats_silence_ui(int more) { if(more) { ++curr_stats.silent_ui; } else if(--curr_stats.silent_ui == 0) { modes_redraw(); } assert(curr_stats.silent_ui >= 0 && "Unbalanced calls to stats_silence_ui()"); }
/* Updates TUI or its elements if something is scheduled. */ static void process_scheduled_updates(void) { int need_redraw = 0; ui_stat_job_bar_check_for_updates(); if(vle_mode_get_primary() != MENU_MODE) { need_redraw += (process_scheduled_updates_of_view(curr_view) != 0); need_redraw += (process_scheduled_updates_of_view(other_view) != 0); } need_redraw += (fetch_redraw_scheduled() != 0); if(need_redraw) { modes_redraw(); } }
/* Internal function for displaying error messages to a user. Automatically * skips whitespace in front of the message and does nothing for empty messages * (due to skipping whitespace-only are counted as empty). When the prompt_skip * isn't zero, asks user about successive messages. Returns non-zero if all * successive messages should be skipped, otherwise zero is returned. */ static int prompt_error_msg_internal(const char title[], const char message[], int prompt_skip) { static int skip_until_started; int key; if(curr_stats.load_stage == 0) return 1; if(curr_stats.load_stage < 2 && skip_until_started) return 1; message = skip_whitespace(message); if(*message == '\0') { return 0; } curr_stats.errmsg_shown = 1; redraw_error_msg(title, message, prompt_skip); do key = wgetch(error_win); while(key != 13 && (!prompt_skip || key != 3)); /* ascii Return, Ctrl-c */ if(curr_stats.load_stage < 2) skip_until_started = key == 3; werase(error_win); wrefresh(error_win); curr_stats.errmsg_shown = 0; modes_update(); if(curr_stats.need_update != UT_NONE) modes_redraw(); return key == 3; }
void modes_redraw(void) { LOG_FUNC_ENTER; static int in_here; if(curr_stats.load_stage < 2) { return; } if(in_here++ > 0) { /* TODO: is this still needed? Update scheduling might have solved issues * caused by asynchronous execution of this function in the past. */ return; } if(curr_stats.term_state != TS_NORMAL) { update_screen(UT_REDRAW); goto finish; } if(vle_mode_is(CMDLINE_MODE)) { redraw_cmdline(); goto finish; } else if(vle_primary_mode_is(MENU_MODE)) { menu_redraw(); if(vle_mode_is(MSG_MODE)) { redraw_msg_dialog(0); } goto finish; } else if(vle_mode_is(FILE_INFO_MODE)) { redraw_file_info_dialog(); goto finish; } update_screen(UT_REDRAW); if(curr_stats.save_msg) { status_bar_message(NULL); } if(vle_mode_is(SORT_MODE)) { redraw_sort_dialog(); } else if(vle_mode_is(CHANGE_MODE)) { redraw_change_dialog(); } else if(vle_mode_is(ATTR_MODE)) { redraw_attr_dialog(); } else if(vle_mode_is(VIEW_MODE)) { view_redraw(); } else if(vle_mode_is(MSG_MODE)) { redraw_msg_dialog(0); } finish: if(--in_here > 0) { modes_redraw(); } }
void event_loop(const int *quit) { /* TODO: refactor this function event_loop(). */ LOG_FUNC_ENTER; const wchar_t *const prev_input_buf = curr_input_buf; const size_t *const prev_input_buf_pos = curr_input_buf_pos; wchar_t input_buf[128]; size_t input_buf_pos; int last_result = 0; int wait_for_enter = 0; int wait_for_suggestion = 0; int timeout = cfg.timeout_len; input_buf[0] = L'\0'; input_buf_pos = 0; curr_input_buf = &input_buf[0]; curr_input_buf_pos = &input_buf_pos; /* Make sure to set the working directory once in order to have the * desired state even before any events are processed. */ (void)vifm_chdir(flist_get_dir(curr_view)); while(!*quit) { wint_t c; size_t counter; int got_input; lwin.user_selection = 1; rwin.user_selection = 1; modes_pre(); /* Waits for timeout then skips if no key press. Short-circuit if we're not * waiting for the next key after timeout. */ do { const int actual_timeout = wait_for_suggestion ? MIN(timeout, cfg.sug.delay) : timeout; if(!ensure_term_is_ready()) { wait_for_enter = 0; continue; } modes_periodic(); bg_check(); got_input = (get_char_async_loop(status_bar, &c, actual_timeout) != ERR); /* If suggestion delay timed out, reset it and wait the rest of the * timeout. */ if(!got_input && wait_for_suggestion) { wait_for_suggestion = 0; timeout -= actual_timeout; display_suggestion_box(input_buf); continue; } wait_for_suggestion = 0; if(!got_input && (input_buf_pos == 0 || last_result == KEYS_WAIT)) { timeout = cfg.timeout_len; continue; } if(got_input && c == K(KEY_RESIZE)) { modes_redraw(); continue; } break; } while(1); suggestions_are_visible = 0; /* Ensure that current working directory is set correctly (some pieces of * code rely on this, e.g. %c macro in current directory). */ (void)vifm_chdir(flist_get_dir(curr_view)); if(got_input) { if(wait_for_enter) { wait_for_enter = 0; curr_stats.save_msg = 0; ui_sb_clear(); if(c == WC_CR) { continue; } } if(c == WC_C_z) { ui_shutdown(); stop_process(); continue; } if(input_buf_pos < ARRAY_LEN(input_buf) - 2) { input_buf[input_buf_pos++] = c; input_buf[input_buf_pos] = L'\0'; } else { /* Recover from input buffer overflow by resetting its contents. */ reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); continue; } } counter = vle_keys_counter(); if(!got_input && last_result == KEYS_WAIT_SHORT) { hide_suggestion_box(); last_result = vle_keys_exec_timed_out(input_buf); counter = vle_keys_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } } else { if(got_input) { curr_stats.save_msg = 0; } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { hide_suggestion_box(); } last_result = vle_keys_exec(input_buf); counter = vle_keys_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { input_buf_pos -= counter; memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { if(should_display_suggestion_box()) { wait_for_suggestion = 1; } if(got_input) { modupd_input_bar(input_buf); } if(last_result == KEYS_WAIT_SHORT && wcscmp(input_buf, L"\033") == 0) { timeout = 1; } if(counter > 0) { clear_input_bar(); } if(!curr_stats.save_msg && curr_view->selected_files && !vle_mode_is(CMDLINE_MODE)) { print_selected_msg(); } continue; } } timeout = cfg.timeout_len; process_scheduled_updates(); reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); if(ui_sb_multiline()) { wait_for_enter = 1; update_all_windows(); continue; } /* Ensure that current working directory is set correctly (some pieces of * code rely on this). PWD could be changed during command execution, but * it should be correct for modes_post() in case of preview modes. */ (void)vifm_chdir(flist_get_dir(curr_view)); modes_post(); } curr_input_buf = prev_input_buf; curr_input_buf_pos = prev_input_buf_pos; }
/* * Main Loop * Everything is driven from this function with the exception of * signals which are handled in signals.c */ void main_loop(void) { LOG_FUNC_ENTER; int last_result = 0; int wait_enter = 0; int timeout = cfg.timeout_len; buf[0] = L'\0'; while(1) { wchar_t c; size_t counter; int ret; is_term_working(); #ifdef _WIN32 update_win_console(); #endif lwin.user_selection = 1; rwin.user_selection = 1; if(curr_stats.too_small_term > 0) { touchwin(stdscr); wrefresh(stdscr); mvwin(status_bar, 0, 0); wresize(status_bar, getmaxy(stdscr), getmaxx(stdscr)); werase(status_bar); waddstr(status_bar, "Terminal is too small for vifm"); touchwin(status_bar); wrefresh(status_bar); #ifndef _WIN32 pause(); #endif continue; } else if(curr_stats.too_small_term < 0) { wtimeout(status_bar, 0); while(wget_wch(status_bar, (wint_t*)&c) != ERR); curr_stats.too_small_term = 0; modes_redraw(); wtimeout(status_bar, cfg.timeout_len); wait_enter = 0; curr_stats.save_msg = 0; status_bar_message(""); } modes_pre(); /* This waits for timeout then skips if no keypress. */ ret = read_char(status_bar, (wint_t*)&c, timeout); /* Ensure that current working directory is set correctly (some pieces of * code rely on this). */ (void)vifm_chdir(curr_view->curr_dir); if(ret != ERR && pos != ARRAY_LEN(buf) - 2) { if(c == L'\x1a') /* Ctrl-Z */ { def_prog_mode(); endwin(); #ifndef _WIN32 { void (*saved_stp_sig_handler)(int) = signal(SIGTSTP, SIG_DFL); kill(0, SIGTSTP); signal(SIGTSTP, saved_stp_sig_handler); } #endif continue; } if(wait_enter) { wait_enter = 0; curr_stats.save_msg = 0; clean_status_bar(); if(c == L'\x0d') continue; } buf[pos++] = c; buf[pos] = L'\0'; } if(wait_enter && ret == ERR) continue; counter = get_key_counter(); if(ret == ERR && last_result == KEYS_WAIT_SHORT) { last_result = execute_keys_timed_out(buf); counter = get_key_counter() - counter; assert(counter <= pos); if(counter > 0) { memmove(buf, buf + counter, (wcslen(buf) - counter + 1)*sizeof(wchar_t)); } } else { if(ret != ERR) curr_stats.save_msg = 0; last_result = execute_keys(buf); counter = get_key_counter() - counter; assert(counter <= pos); if(counter > 0) { pos -= counter; memmove(buf, buf + counter, (wcslen(buf) - counter + 1)*sizeof(wchar_t)); } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { if(ret != ERR) modupd_input_bar(buf); if(last_result == KEYS_WAIT_SHORT && wcscmp(buf, L"\033") == 0) timeout = 1; if(counter > 0) clear_input_bar(); if(!curr_stats.save_msg && curr_view->selected_files && get_mode() != CMDLINE_MODE) print_selected_msg(); continue; } } timeout = cfg.timeout_len; process_scheduled_updates(); pos = 0; buf[0] = L'\0'; clear_input_bar(); if(is_status_bar_multiline()) { wait_enter = 1; update_all_windows(); continue; } /* Ensure that current working directory is set correctly (some pieces of * code rely on this). PWD could be changed during command execution, but * it should be correct for modes_post() in case of preview modes. */ (void)vifm_chdir(curr_view->curr_dir); modes_post(); } }