To: vim_dev@googlegroups.com Subject: Patch 8.0.1000 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1000 Problem: Cannot open a terminal without running a job in it. Solution: Make ":terminal NONE" open a terminal with a pty. Files: src/terminal.c, src/os_unix.c, src/proto/os_unix.pro, src/channel.c, src/proto/channel.pro, src/structs.h, src/testdir/test_terminal.vim, src/misc2.c, src/gui_gtk_x11.c *** ../vim-8.0.0999/src/terminal.c 2017-08-24 21:48:22.536643534 +0200 --- src/terminal.c 2017-08-26 20:35:19.554845869 +0200 *************** *** 38,43 **** --- 38,44 ---- * in tl_scrollback are no longer used. * * TODO: + * - ":term NONE" does not work in MS-Windows. * - better check for blinking - reply from Thomas Dickey Aug 22 * - test for writing lines to terminal job does not work on MS-Windows * - implement term_setsize() *************** *** 47,52 **** --- 48,54 ---- * - do not set bufhidden to "hide"? works like a buffer with changes. * document that CTRL-W :hide can be used. * - GUI: when using tabs, focus in terminal, click on tab does not work. + * - When $HOME was set by Vim (MS-Windows), do not pass it to the job. * - GUI: when 'confirm' is set and trying to exit Vim, dialog offers to save * changes to "!shell". * (justrajdeep, 2017 Aug 22) *************** *** 62,69 **** * shell writing stderr to a file or buffer * - For the GUI fill termios with default values, perhaps like pangoterm: * http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134 - * - support ":term NONE" to open a terminal with a pty but not running a job - * in it. The pty can be passed to gdb to run the executable in. * - if the job in the terminal does not support the mouse, we can use the * mouse in the Terminal window for copy/paste. * - when 'encoding' is not utf-8, or the job is using another encoding, setup --- 64,69 ---- *************** *** 163,170 **** /* * Functions with separate implementation for MS-Windows and Unix-like systems. */ ! static int term_and_job_init(term_T *term, int rows, int cols, ! typval_T *argvar, jobopt_T *opt); static void term_report_winsize(term_T *term, int rows, int cols); static void term_free_vterm(term_T *term); --- 163,170 ---- /* * Functions with separate implementation for MS-Windows and Unix-like systems. */ ! static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt); ! static int create_pty_only(term_T *term, jobopt_T *opt); static void term_report_winsize(term_T *term, int rows, int cols); static void term_free_vterm(term_T *term); *************** *** 256,261 **** --- 256,262 ---- win_T *old_curwin = curwin; term_T *term; buf_T *old_curbuf = NULL; + int res; if (check_restricted() || check_secure()) return; *************** *** 355,361 **** --- 356,368 ---- char_u *cmd, *p; if (argvar->v_type == VAR_STRING) + { cmd = argvar->vval.v_string; + if (cmd == NULL) + cmd = (char_u *)""; + else if (STRCMP(cmd, "NONE") == 0) + cmd = (char_u *)"pty"; + } else if (argvar->v_type != VAR_LIST || argvar->vval.v_list == NULL || argvar->vval.v_list->lv_len < 1) *************** *** 400,408 **** set_term_and_win_size(term); setup_job_options(opt, term->tl_rows, term->tl_cols); ! /* System dependent: setup the vterm and start the job in it. */ ! if (term_and_job_init(term, term->tl_rows, term->tl_cols, argvar, opt) ! == OK) { /* Get and remember the size we ended up with. Update the pty. */ vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); --- 407,421 ---- set_term_and_win_size(term); setup_job_options(opt, term->tl_rows, term->tl_cols); ! /* System dependent: setup the vterm and maybe start the job in it. */ ! if (argvar->v_type == VAR_STRING ! && argvar->vval.v_string != NULL ! && STRCMP(argvar->vval.v_string, "NONE") == 0) ! res = create_pty_only(term, opt); ! else ! res = term_and_job_init(term, argvar, opt); ! ! if (res == OK) { /* Get and remember the size we ended up with. Update the pty. */ vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); *************** *** 553,559 **** if (term->tl_job != NULL) { if (term->tl_job->jv_status != JOB_ENDED ! && term->tl_job->jv_status != JOB_FAILED) job_stop(term->tl_job, NULL, "kill"); job_unref(term->tl_job); } --- 566,573 ---- if (term->tl_job != NULL) { if (term->tl_job->jv_status != JOB_ENDED ! && term->tl_job->jv_status != JOB_FINISHED ! && term->tl_job->jv_status != JOB_FAILED) job_stop(term->tl_job, NULL, "kill"); job_unref(term->tl_job); } *************** *** 839,846 **** * race condition when updating the title. */ return term != NULL && term->tl_job != NULL ! && term->tl_job->jv_status == JOB_STARTED ! && channel_is_open(term->tl_job->jv_channel); } /* --- 853,861 ---- * race condition when updating the title. */ return term != NULL && term->tl_job != NULL ! && channel_is_open(term->tl_job->jv_channel) ! && (term->tl_job->jv_status == JOB_STARTED ! || term->tl_job->jv_channel->ch_keep_open); } /* *************** *** 2842,2850 **** ch_log(NULL, "term_wait(): no job to wait for"); return; } /* Get the job status, this will detect a job that finished. */ ! if (STRCMP(job_status(buf->b_term->tl_job), "dead") == 0) { /* The job is dead, keep reading channel I/O until the channel is * closed. */ --- 2857,2870 ---- ch_log(NULL, "term_wait(): no job to wait for"); return; } + if (buf->b_term->tl_job->jv_channel == NULL) + /* channel is closed, nothing to do */ + return; /* Get the job status, this will detect a job that finished. */ ! if ((buf->b_term->tl_job->jv_channel == NULL ! || !buf->b_term->tl_job->jv_channel->ch_keep_open) ! && STRCMP(job_status(buf->b_term->tl_job), "dead") == 0) { /* The job is dead, keep reading channel I/O until the channel is * closed. */ *************** *** 2976,2983 **** static int term_and_job_init( term_T *term, - int rows, - int cols, typval_T *argvar, jobopt_T *opt) { --- 2996,3001 ---- *************** *** 3023,3029 **** if (term->tl_winpty_config == NULL) goto failed; ! winpty_config_set_initial_size(term->tl_winpty_config, cols, rows); term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err); if (term->tl_winpty == NULL) goto failed; --- 3041,3048 ---- if (term->tl_winpty_config == NULL) goto failed; ! winpty_config_set_initial_size(term->tl_winpty_config, ! term->tl_cols, term->tl_rows); term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err); if (term->tl_winpty == NULL) goto failed; *************** *** 3085,3091 **** winpty_spawn_config_free(spawn_config); vim_free(cmd_wchar); ! create_vterm(term, rows, cols); channel_set_job(channel, job, opt); job_set_options(job, opt); --- 3104,3110 ---- winpty_spawn_config_free(spawn_config); vim_free(cmd_wchar); ! create_vterm(term, term->tl_rows, term->tl_cols); channel_set_job(channel, job, opt); job_set_options(job, opt); *************** *** 3137,3142 **** --- 3156,3168 ---- return FAIL; } + static int + create_pty_only(term_T *term, jobopt_T *opt) + { + /* TODO: implement this */ + return FAIL; + } + /* * Free the terminal emulator part of "term". */ *************** *** 3185,3196 **** static int term_and_job_init( term_T *term, - int rows, - int cols, typval_T *argvar, jobopt_T *opt) { ! create_vterm(term, rows, cols); /* TODO: if the command is "NONE" only create a pty. */ term->tl_job = job_start(argvar, opt); --- 3211,3220 ---- static int term_and_job_init( term_T *term, typval_T *argvar, jobopt_T *opt) { ! create_vterm(term, term->tl_rows, term->tl_cols); /* TODO: if the command is "NONE" only create a pty. */ term->tl_job = job_start(argvar, opt); *************** *** 3202,3207 **** --- 3226,3251 ---- && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL; } + static int + create_pty_only(term_T *term, jobopt_T *opt) + { + int ret; + + create_vterm(term, term->tl_rows, term->tl_cols); + + term->tl_job = job_alloc(); + if (term->tl_job == NULL) + return FAIL; + ++term->tl_job->jv_refcount; + + /* behave like the job is already finished */ + term->tl_job->jv_status = JOB_FINISHED; + + ret = mch_create_pty_channel(term->tl_job, opt); + + return ret; + } + /* * Free the terminal emulator part of "term". */ *** ../vim-8.0.0999/src/os_unix.c 2017-08-21 21:07:24.478671491 +0200 --- src/os_unix.c 2017-08-26 19:57:33.001144732 +0200 *************** *** 5466,5472 **** job->jv_channel = channel; /* ch_refcount was set above */ if (pty_master_fd >= 0) ! close(pty_slave_fd); /* duped above */ /* close child stdin, stdout and stderr */ if (!use_file_for_in && fd_in[0] >= 0) close(fd_in[0]); --- 5466,5472 ---- job->jv_channel = channel; /* ch_refcount was set above */ if (pty_master_fd >= 0) ! close(pty_slave_fd); /* not used in the parent */ /* close child stdin, stdout and stderr */ if (!use_file_for_in && fd_in[0] >= 0) close(fd_in[0]); *************** *** 5669,5674 **** --- 5669,5697 ---- } #endif + #if defined(FEAT_TERMINAL) || defined(PROTO) + int + mch_create_pty_channel(job_T *job, jobopt_T *options) + { + int pty_master_fd = -1; + int pty_slave_fd = -1; + channel_T *channel; + + open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name); + close(pty_slave_fd); + + channel = add_channel(); + if (channel == NULL) + return FAIL; + job->jv_channel = channel; /* ch_refcount was set by add_channel() */ + channel->ch_keep_open = TRUE; + + channel_set_pipes(channel, pty_master_fd, pty_master_fd, pty_master_fd); + channel_set_job(channel, job, options); + return OK; + } + #endif + /* * Check for CTRL-C typed by reading all available characters. * In cooked mode we should get SIGINT, no need to check. *** ../vim-8.0.0999/src/proto/os_unix.pro 2017-08-13 20:06:14.963846989 +0200 --- src/proto/os_unix.pro 2017-08-26 18:08:40.619342952 +0200 *************** *** 66,71 **** --- 66,72 ---- job_T *mch_detect_ended_job(job_T *job_list); int mch_signal_job(job_T *job, char_u *how); void mch_clear_job(job_T *job); + int mch_create_pty_channel(job_T *job, jobopt_T *options); void mch_breakcheck(int force); int mch_expandpath(garray_T *gap, char_u *path, int flags); int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags); *** ../vim-8.0.0999/src/channel.c 2017-08-19 21:26:40.081756056 +0200 --- src/channel.c 2017-08-26 22:00:19.042412608 +0200 *************** *** 503,508 **** --- 503,512 ---- if (!CH_HAS_GUI) return; + /* gets stuck in handling events for a not connected channel */ + if (channel->ch_keep_open) + return; + # ifdef FEAT_GUI_X11 /* Tell notifier we are interested in being called * when there is input on the editor connection socket. */ *************** *** 548,556 **** { if (channel->CH_SOCK_FD != INVALID_FD) channel_gui_register_one(channel, PART_SOCK); ! if (channel->CH_OUT_FD != INVALID_FD) channel_gui_register_one(channel, PART_OUT); ! if (channel->CH_ERR_FD != INVALID_FD) channel_gui_register_one(channel, PART_ERR); } --- 552,563 ---- { if (channel->CH_SOCK_FD != INVALID_FD) channel_gui_register_one(channel, PART_SOCK); ! if (channel->CH_OUT_FD != INVALID_FD ! && channel->CH_OUT_FD != channel->CH_SOCK_FD) channel_gui_register_one(channel, PART_OUT); ! if (channel->CH_ERR_FD != INVALID_FD ! && channel->CH_ERR_FD != channel->CH_SOCK_FD ! && channel->CH_ERR_FD != channel->CH_OUT_FD) channel_gui_register_one(channel, PART_ERR); } *************** *** 3247,3257 **** /* Reading a disconnection (readlen == 0), or an error. */ if (readlen <= 0) ! ch_close_part_on_error(channel, part, (len < 0), func); ! #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK) ! /* signal the main loop that there is something to read */ ! if (CH_HAS_GUI && gtk_main_level() > 0) gtk_main_quit(); #endif } --- 3254,3266 ---- /* Reading a disconnection (readlen == 0), or an error. */ if (readlen <= 0) ! { ! if (!channel->ch_keep_open) ! ch_close_part_on_error(channel, part, (len < 0), func); ! } #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK) ! else if (CH_HAS_GUI && gtk_main_level() > 0) ! /* signal the main loop that there is something to read */ gtk_main_quit(); #endif } *************** *** 3509,3521 **** } # endif ! # if defined(WIN32) || defined(PROTO) /* * Check the channels for anything that is ready to be read. * The data is put in the read queue. */ void ! channel_handle_events(void) { channel_T *channel; ch_part_T part; --- 3518,3531 ---- } # endif ! # if defined(WIN32) || defined(FEAT_GUI) || defined(PROTO) /* * Check the channels for anything that is ready to be read. * The data is put in the read queue. + * if "only_keep_open" is TRUE only check channels where ch_keep_open is set. */ void ! channel_handle_events(int only_keep_open) { channel_T *channel; ch_part_T part; *************** *** 3523,3528 **** --- 3533,3541 ---- for (channel = first_channel; channel != NULL; channel = channel->ch_next) { + if (only_keep_open && !channel->ch_keep_open) + continue; + /* check the socket and pipes */ for (part = PART_SOCK; part < PART_IN; ++part) { *** ../vim-8.0.0999/src/proto/channel.pro 2017-08-18 20:50:26.441516959 +0200 --- src/proto/channel.pro 2017-08-26 20:29:32.725031621 +0200 *************** *** 34,42 **** char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout); void common_channel_read(typval_T *argvars, typval_T *rettv, int raw); channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp); ! void channel_handle_events(void); void channel_set_nonblock(channel_T *channel, ch_part_T part); ! int channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, char *fun); void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval); void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval); int channel_poll_setup(int nfd_in, void *fds_in); --- 34,42 ---- char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout); void common_channel_read(typval_T *argvars, typval_T *rettv, int raw); channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp); ! void channel_handle_events(int only_keep_open); void channel_set_nonblock(channel_T *channel, ch_part_T part); ! int channel_send(channel_T *channel, ch_part_T part, char_u *buf_arg, int len_arg, char *fun); void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval); void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval); int channel_poll_setup(int nfd_in, void *fds_in); *** ../vim-8.0.0999/src/structs.h 2017-08-18 20:50:26.441516959 +0200 --- src/structs.h 2017-08-26 18:27:54.471843502 +0200 *************** *** 1656,1661 **** --- 1656,1662 ---- char_u *ch_close_cb; /* call when channel is closed */ partial_T *ch_close_partial; int ch_drop_never; + int ch_keep_open; /* do not close on read error */ job_T *ch_job; /* Job that uses this channel; this does not * count as a reference to avoid a circular *** ../vim-8.0.0999/src/testdir/test_terminal.vim 2017-08-25 23:22:01.689645666 +0200 --- src/testdir/test_terminal.vim 2017-08-26 20:03:16.958970560 +0200 *************** *** 505,507 **** --- 505,527 ---- bwipe! endfunc + + func Test_terminal_no_cmd() + " Todo: make this work on all systems. + if !has('unix') + return + endif + " Todo: make this work in the GUI + if !has('gui_running') + return + endif + let buf = term_start('NONE', {}) + call assert_notequal(0, buf) + + let pty = job_info(term_getjob(buf))['tty'] + call assert_notequal('', pty) + call system('echo "look here" > ' . pty) + call term_wait(buf) + call assert_equal('look here', term_getline(buf, 1)) + bwipe! + endfunc *** ../vim-8.0.0999/src/misc2.c 2017-08-19 15:05:16.048003367 +0200 --- src/misc2.c 2017-08-26 20:30:31.316662527 +0200 *************** *** 6321,6327 **** { /* For Win32 mch_breakcheck() does not check for input, do it here. */ # if defined(WIN32) && defined(FEAT_JOB_CHANNEL) ! channel_handle_events(); # endif # ifdef FEAT_NETBEANS_INTG --- 6321,6327 ---- { /* For Win32 mch_breakcheck() does not check for input, do it here. */ # if defined(WIN32) && defined(FEAT_JOB_CHANNEL) ! channel_handle_events(FALSE); # endif # ifdef FEAT_NETBEANS_INTG *** ../vim-8.0.0999/src/gui_gtk_x11.c 2017-07-23 16:45:05.669761183 +0200 --- src/gui_gtk_x11.c 2017-08-26 20:32:13.764017011 +0200 *************** *** 6643,6648 **** --- 6643,6654 ---- focus = gui.in_focus; } + # if defined(FEAT_JOB_CHANNEL) + /* Using an event handler for a channel that may be disconnected does + * not work, it hangs. Instead poll for messages. */ + channel_handle_events(TRUE); + # endif + #ifdef MESSAGE_QUEUE # ifdef FEAT_TIMERS did_add_timer = FALSE; *** ../vim-8.0.0999/src/version.c 2017-08-26 17:48:57.582995164 +0200 --- src/version.c 2017-08-26 18:43:27.801784838 +0200 *************** *** 771,772 **** --- 771,774 ---- { /* Add new patch number below this line */ + /**/ + 1000, /**/ -- I have a drinking problem -- I don't have a drink! /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///