patch 8.1.1391: no popup window support

Problem:    No popup window support.
Solution:   Add initial code for popup windows.  Add the 'wincolor' option.
diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak
index 7844e00..f479812 100644
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -740,6 +740,7 @@
 	$(OUTDIR)/os_win32.o \
 	$(OUTDIR)/pathdef.o \
 	$(OUTDIR)/popupmnu.o \
+	$(OUTDIR)/popupwin.o \
 	$(OUTDIR)/quickfix.o \
 	$(OUTDIR)/regexp.o \
 	$(OUTDIR)/screen.o \
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index f8e406e..c1cd0a4 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -743,6 +743,8 @@
 	$(OUTDIR)\os_win32.obj \
 	$(OUTDIR)\pathdef.obj \
 	$(OUTDIR)\popupmnu.obj \
+	$(OUTDIR)\popupwin.obj \
+	$(OUTDIR)\popupwin.obj \
 	$(OUTDIR)\quickfix.obj \
 	$(OUTDIR)\regexp.obj \
 	$(OUTDIR)\screen.obj \
@@ -1575,6 +1577,8 @@
 
 $(OUTDIR)/popupmnu.obj:	$(OUTDIR) popupmnu.c  $(INCL)
 
+$(OUTDIR)/popupwin.obj:	$(OUTDIR) popupwin.c  $(INCL)
+
 $(OUTDIR)/quickfix.obj:	$(OUTDIR) quickfix.c  $(INCL)
 
 $(OUTDIR)/regexp.obj:	$(OUTDIR) regexp.c regexp_nfa.c  $(INCL)
@@ -1745,6 +1749,7 @@
 	proto/winclip.pro \
 	proto/os_win32.pro \
 	proto/popupmnu.pro \
+	proto/popupwin.pro \
 	proto/quickfix.pro \
 	proto/regexp.pro \
 	proto/screen.pro \
diff --git a/src/Make_vms.mms b/src/Make_vms.mms
index 1a0537b..f01d325 100644
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -2,7 +2,7 @@
 # Makefile for Vim on OpenVMS
 #
 # Maintainer:   Zoltan Arpadffy <arpadffy@polarhome.com>
-# Last change:  2019 May 11
+# Last change:  2019 May 24
 #
 # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64
 # with MMS and MMK
@@ -313,7 +313,7 @@
 	if_cscope.c if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c \
 	hardcopy.c hashtab.c indent.c insexpand.c json.c list.c main.c mark.c \
 	menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \
-	normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \
+	normal.c ops.c option.c popupmnu.c popupwin.c, quickfix.c regexp.c search.c \
 	sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \
 	textprop.c ui.c undo.c usercmd.c userfunc.c version.c screen.c \
 	window.c os_unix.c os_vms.c pathdef.c \
@@ -327,7 +327,7 @@
 	fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \
 	indent.obj insexpand.obj json.obj list.obj main.obj mark.obj \
 	menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \
-	move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \
+	move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj popupwin.obj\
 	quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \
 	spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \
 	ui.obj undo.obj usercmd.obj userfunc.obj screen.obj version.obj \
@@ -688,6 +688,10 @@
  ascii.h keymap.h term.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
  globals.h
+popupwin.obj : popupwin.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h term.h macros.h structs.h regexp.h \
+ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
+ globals.h
 quickfix.obj : quickfix.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h term.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
diff --git a/src/Makefile b/src/Makefile
index ff5ae10..d74eac4 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1620,6 +1620,7 @@
 	os_unix.c \
 	auto/pathdef.c \
 	popupmnu.c \
+	popupwin.c \
 	pty.c \
 	quickfix.c \
 	regexp.c \
@@ -1734,6 +1735,7 @@
 	objects/os_unix.o \
 	objects/pathdef.o \
 	objects/popupmnu.o \
+	objects/popupwin.o \
 	objects/pty.o \
 	objects/quickfix.o \
 	objects/regexp.o \
@@ -1873,6 +1875,7 @@
 	os_mac_conv.pro \
 	os_unix.pro \
 	popupmnu.pro \
+	popupwin.pro \
 	pty.pro \
 	quickfix.pro \
 	regexp.pro \
@@ -3208,6 +3211,9 @@
 objects/popupmnu.o: popupmnu.c
 	$(CCC) -o $@ popupmnu.c
 
+objects/popupwin.o: popupwin.c
+	$(CCC) -o $@ popupwin.c
+
 objects/pty.o: pty.c
 	$(CCC) -o $@ pty.c
 
@@ -3612,6 +3618,10 @@
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
  proto.h globals.h
+objects/popupwin.o: popupwin.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+ proto.h globals.h
 objects/pty.o: pty.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
diff --git a/src/autocmd.c b/src/autocmd.c
index 614bc33..152dabd 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -1349,7 +1349,7 @@
      */
     FOR_ALL_BUFFERS(buf)
     {
-	if (buf->b_ml.ml_mfp != NULL)
+	if (buf->b_ml.ml_mfp != NULL && !bt_popup(buf))
 	{
 	    // find a window for this buffer and save some values
 	    aucmd_prepbuf(&aco, buf);
@@ -1423,7 +1423,7 @@
     // back to using the current window.
     if (win == NULL && aucmd_win == NULL)
     {
-	win_alloc_aucmd_win();
+	aucmd_win = win_alloc_popup_win();
 	if (aucmd_win == NULL)
 	    win = curwin;
     }
@@ -1451,20 +1451,12 @@
 	// unexpected results.
 	aco->use_aucmd_win = TRUE;
 	aucmd_win_used = TRUE;
-	aucmd_win->w_buffer = buf;
-#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
-	aucmd_win->w_s = &buf->b_s;
-#endif
-	++buf->b_nwindows;
-	win_init_empty(aucmd_win); // set cursor and topline to safe values
 
-	// Make sure w_localdir and globaldir are NULL to avoid a chdir() in
-	// win_enter_ext().
-	VIM_CLEAR(aucmd_win->w_localdir);
+	win_init_popup_win(aucmd_win, buf);
+
 	aco->globaldir = globaldir;
 	globaldir = NULL;
 
-
 	// Split the current window, put the aucmd_win in the upper half.
 	// We don't want the BufEnter or WinEnter autocommands.
 	block_autocmds();
@@ -1620,6 +1612,8 @@
     int		force,	    // when TRUE, ignore autocmd_busy
     buf_T	*buf)	    // buffer for <abuf>
 {
+    if (bt_popup(buf))
+	return FALSE;
     return apply_autocmds_group(event, fname, fname_io, force,
 						      AUGROUP_ALL, buf, NULL);
 }
diff --git a/src/buffer.c b/src/buffer.c
index f621095..6174d80 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -456,7 +456,7 @@
     win_T	*win,		/* if not NULL, set b_last_cursor */
     buf_T	*buf,
     int		action,
-    int		abort_if_last UNUSED)
+    int		abort_if_last)
 {
     int		is_curbuf;
     int		nwindows;
@@ -5678,7 +5678,17 @@
     int
 bt_prompt(buf_T *buf)
 {
-    return buf != NULL && buf->b_p_bt[0] == 'p';
+    return buf != NULL && buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'r';
+}
+
+/*
+ * Return TRUE if "buf" is a buffer for a popup window.
+ */
+    int
+bt_popup(buf_T *buf)
+{
+    return buf != NULL && buf->b_p_bt != NULL
+	&& buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'o';
 }
 
 /*
diff --git a/src/eval.c b/src/eval.c
index 1d5d0f1..fb417c8 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -5585,6 +5585,19 @@
     if (aucmd_win != NULL)
 	abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID,
 								  NULL, NULL);
+#ifdef FEAT_TEXT_PROP
+    for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+								  NULL, NULL);
+    for (wp = first_tab_popupwin; wp != NULL; wp = wp->w_next)
+	abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+								  NULL, NULL);
+    FOR_ALL_TABPAGES(tp)
+	if (tp != curtab)
+	    for (wp = tp->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+		abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+								  NULL, NULL);
+#endif
 
     /* tabpage-local variables */
     FOR_ALL_TABPAGES(tp)
@@ -8801,7 +8814,20 @@
 	    break;
     }
     if (nr >= LOWEST_WIN_ID)
+    {
+#ifdef FEAT_TEXT_PROP
+	// popup windows are in a separate list
+	for (wp = (tp == NULL || tp == curtab)
+		? first_tab_popupwin : tp->tp_first_popupwin;
+						   wp != NULL; wp = wp->w_next)
+	    if (wp->w_id == nr)
+		return wp;
+	for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	    if (wp->w_id == nr)
+		return wp;
+#endif
 	return NULL;
+    }
     return wp;
 }
 
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 58ea710..914efb1 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -807,6 +807,10 @@
 #ifdef FEAT_PERL
     {"perleval",	1, 1, f_perleval},
 #endif
+#ifdef FEAT_TEXT_PROP
+    {"popup_close",	1, 1, f_popup_close},
+    {"popup_create",	2, 2, f_popup_create},
+#endif
 #ifdef FEAT_FLOAT
     {"pow",		2, 2, f_pow},
 #endif
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 0103a17..2aae4b6 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -21,16 +21,16 @@
   /* n */ 285,
   /* o */ 305,
   /* p */ 317,
-  /* q */ 356,
-  /* r */ 359,
-  /* s */ 379,
-  /* t */ 447,
-  /* u */ 492,
-  /* v */ 503,
-  /* w */ 521,
-  /* x */ 535,
-  /* y */ 545,
-  /* z */ 546
+  /* q */ 357,
+  /* r */ 360,
+  /* s */ 380,
+  /* t */ 448,
+  /* u */ 493,
+  /* v */ 504,
+  /* w */ 522,
+  /* x */ 536,
+  /* y */ 546,
+  /* z */ 547
 };
 
 /*
@@ -56,7 +56,7 @@
   /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 16 },
   /* n */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  8, 10,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0 },
   /* o */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  5,  0,  0,  0,  0,  0,  0,  9,  0, 11,  0,  0,  0 },
-  /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 27,  0, 28,  0 },
+  /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7, 10,  0,  0, 17, 18, 27,  0, 28,  0, 29,  0 },
   /* q */ {  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* r */ {  0,  0,  0,  0,  0,  0,  0,  0, 12,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 14, 19,  0,  0,  0,  0 },
   /* s */ {  2,  6, 15,  0, 19, 23,  0, 25, 26,  0,  0, 29, 31, 35, 39, 41,  0, 49,  0, 50,  0, 62, 63,  0, 64,  0 },
@@ -69,4 +69,4 @@
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 559;
+static const int command_count = 560;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 5ec69be..9c913a8 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -1091,6 +1091,9 @@
 EX(CMD_popup,		"popup",	ex_popup,
 			NEEDARG|EXTRA|BANG|TRLBAR|NOTRLCOM|CMDWIN,
 			ADDR_NONE),
+EX(CMD_popupclear,	"popupclear",	ex_popupclear,
+			TRLBAR,
+			ADDR_NONE),
 EX(CMD_ppop,		"ppop",		ex_ptag,
 			RANGE|BANG|COUNT|TRLBAR|ZEROR,
 			ADDR_OTHER),
diff --git a/src/feature.h b/src/feature.h
index bfbabe0..c613c4b 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -477,7 +477,7 @@
 #endif
 
 /*
- * +textprop		Text properties
+ * +textprop		Text properties and popup windows
  */
 #if defined(FEAT_EVAL) && defined(FEAT_SYN_HL)
 # define FEAT_TEXT_PROP
diff --git a/src/globals.h b/src/globals.h
index 09ac6b5..7d250f4 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -550,9 +550,10 @@
 #endif
 
 /*
- * All windows are linked in a list. firstwin points to the first entry,
- * lastwin to the last entry (can be the same as firstwin) and curwin to the
- * currently active window.
+ * All regular windows are linked in a list. "firstwin" points to the first
+ * entry, "lastwin" to the last entry (can be the same as firstwin) and
+ * "curwin" to the currently active window.
+ * When switching tabs these swapped with the pointers in "tabpage_T".
  */
 EXTERN win_T	*firstwin;		/* first window */
 EXTERN win_T	*lastwin;		/* last window */
@@ -580,6 +581,11 @@
 EXTERN win_T	*aucmd_win;	/* window used in aucmd_prepbuf() */
 EXTERN int	aucmd_win_used INIT(= FALSE);	/* aucmd_win is being used */
 
+#ifdef FEAT_TEXT_PROP
+EXTERN win_T    *first_tab_popupwin;	// first popup window local to tab page
+EXTERN win_T    *first_popupwin;	// first global popup window
+#endif
+
 /*
  * The window layout is kept in a tree of frames.  topframe points to the top
  * of the tree.
diff --git a/src/option.c b/src/option.c
index 42a252e..9b23eed 100644
--- a/src/option.c
+++ b/src/option.c
@@ -196,6 +196,7 @@
 # define PV_BRI		OPT_WIN(WV_BRI)
 # define PV_BRIOPT	OPT_WIN(WV_BRIOPT)
 #endif
+# define PV_WCR		OPT_WIN(WV_WCR)
 #ifdef FEAT_DIFF
 # define PV_DIFF	OPT_WIN(WV_DIFF)
 #endif
@@ -3033,6 +3034,10 @@
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
+    {"wincolor", "wcr",	    P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN,
+			    (char_u *)VAR_WIN, PV_WCR,
+			    {(char_u *)"", (char_u *)NULL}
+			    SCTX_INIT},
     {"window",	    "wi",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_window, PV_NONE,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
@@ -3211,7 +3216,7 @@
 static char *(p_scbopt_values[]) = {"ver", "hor", "jump", NULL};
 static char *(p_debug_values[]) = {"msg", "throw", "beep", NULL};
 static char *(p_ead_values[]) = {"both", "ver", "hor", NULL};
-static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", "acwrite", "prompt", NULL};
+static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", "acwrite", "prompt", "popup", NULL};
 static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL};
 static char *(p_bs_values[]) = {"indent", "eol", "start", NULL};
 #ifdef FEAT_FOLDING
@@ -10940,6 +10945,7 @@
 	case PV_BRI:	return (char_u *)&(curwin->w_p_bri);
 	case PV_BRIOPT: return (char_u *)&(curwin->w_p_briopt);
 #endif
+	case PV_WCR:	return (char_u *)&(curwin->w_p_wcr);
 	case PV_SCBIND: return (char_u *)&(curwin->w_p_scb);
 	case PV_CRBIND: return (char_u *)&(curwin->w_p_crb);
 #ifdef FEAT_CONCEAL
@@ -11124,6 +11130,7 @@
     to->wo_bri = from->wo_bri;
     to->wo_briopt = vim_strsave(from->wo_briopt);
 #endif
+    to->wo_wcr = vim_strsave(from->wo_wcr);
     to->wo_scb = from->wo_scb;
     to->wo_scb_save = from->wo_scb_save;
     to->wo_crb = from->wo_crb;
@@ -11221,6 +11228,7 @@
 #ifdef FEAT_LINEBREAK
     check_string_option(&wop->wo_briopt);
 #endif
+    check_string_option(&wop->wo_wcr);
 }
 
 /*
@@ -11245,6 +11253,7 @@
 #ifdef FEAT_LINEBREAK
     clear_string_option(&wop->wo_briopt);
 #endif
+    clear_string_option(&wop->wo_wcr);
 #ifdef FEAT_RIGHTLEFT
     clear_string_option(&wop->wo_rlc);
 #endif
diff --git a/src/option.h b/src/option.h
index 082ff9b..7e657dd 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1116,6 +1116,7 @@
     , WV_BRI
     , WV_BRIOPT
 #endif
+    , WV_WCR
 #ifdef FEAT_DIFF
     , WV_DIFF
 #endif
diff --git a/src/popupwin.c b/src/popupwin.c
new file mode 100644
index 0000000..f7a8921
--- /dev/null
+++ b/src/popupwin.c
@@ -0,0 +1,231 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read a list of people who contributed.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * Implementation of popup windows.  See ":help popup".
+ */
+
+#include "vim.h"
+
+#ifdef FEAT_TEXT_PROP
+
+/*
+ * Go through the options in "dict" and apply them to buffer "buf" displayed in
+ * popup window "wp".
+ */
+    static void
+apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
+{
+    wp->w_maxwidth = dict_get_number(dict, (char_u *)"maxwidth");
+    wp->w_maxheight = dict_get_number(dict, (char_u *)"maxheight");
+    wp->w_winrow = dict_get_number(dict, (char_u *)"line");
+    wp->w_wincol = dict_get_number(dict, (char_u *)"col");
+    wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
+}
+
+/*
+ * popup_create({text}, {options})
+ */
+    void
+f_popup_create(typval_T *argvars, typval_T *rettv)
+{
+    win_T   *wp;
+    buf_T   *buf;
+    dict_T  *d;
+    int	    nr;
+
+    // Check arguments look OK.
+    if (!(argvars[0].v_type == VAR_STRING
+		&& argvars[0].vval.v_string != NULL
+		&& STRLEN(argvars[0].vval.v_string) > 0)
+	&& !(argvars[0].v_type == VAR_LIST
+	    && argvars[0].vval.v_list != NULL
+	    && argvars[0].vval.v_list->lv_len > 0))
+    {
+	emsg(_(e_listreq));
+	return;
+    }
+    if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
+    {
+	emsg(_(e_dictreq));
+	return;
+    }
+    d = argvars[1].vval.v_dict;
+
+    // Create the window and buffer.
+    wp = win_alloc_popup_win();
+    if (wp == NULL)
+	return;
+    rettv->vval.v_number = wp->w_id;
+    wp->w_p_wrap = TRUE;  // 'wrap' is default on
+
+    buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
+    if (buf == NULL)
+	return;
+    ml_open(buf);
+    curbuf = buf;
+    set_string_option_direct((char_u *)"buftype", -1,
+				     (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
+    set_string_option_direct((char_u *)"bufhidden", -1,
+				     (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
+    curbuf = curwin->w_buffer;
+    buf->b_p_ul = -1;	    // no undo
+    buf->b_p_swf = FALSE;   // no swap file
+    buf->b_p_bl = FALSE;    // unlisted buffer
+
+    win_init_popup_win(wp, buf);
+
+    nr = (int)dict_get_number(d, (char_u *)"tab");
+    if (nr == 0)
+    {
+	// popup on current tab
+	wp->w_next = first_tab_popupwin;
+	first_tab_popupwin = wp;
+    }
+    else if (nr < 0)
+    {
+	// global popup
+	wp->w_next = first_popupwin;
+	first_popupwin = wp;
+    }
+    else
+	// TODO: find tab page "nr"
+	emsg("Not implemented yet");
+
+    // Add text to the buffer.
+    if (argvars[0].v_type == VAR_STRING)
+	// just a string
+	ml_append_buf(buf, 0, argvars[0].vval.v_string, (colnr_T)0, TRUE);
+    else if (argvars[0].vval.v_list->lv_first->li_tv.v_type == VAR_STRING)
+    {
+	listitem_T  *li;
+	linenr_T    lnum = 0;
+	char_u	    *p;
+
+	// list of strings
+	for (li = argvars[0].vval.v_list->lv_first; li != NULL;
+							      li = li->li_next)
+	    if (li->li_tv.v_type == VAR_STRING)
+	    {
+		p = li->li_tv.vval.v_string;
+		ml_append_buf(buf, lnum++,
+			       p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
+	    }
+    }
+    else
+	// TODO: handle a list of dictionaries
+	emsg("Not implemented yet");
+
+    // Delete the line of the empty buffer.
+    curbuf = buf;
+    ml_delete(buf->b_ml.ml_line_count, FALSE);
+    curbuf = curwin->w_buffer;
+
+    // Deal with options.
+    apply_options(wp, buf, argvars[1].vval.v_dict);
+
+    // set default values
+    if (wp->w_zindex == 0)
+	wp->w_zindex = 50;
+
+    // TODO: Compute the size and position properly.
+
+    // Default position is in middle of the screen, assuming a small popup
+    if (wp->w_winrow == 0)
+	wp->w_winrow = Rows > 5 ? Rows / 2 - 2 : 0;
+    else
+	--wp->w_winrow;  // option value is one-based
+    if (wp->w_wincol == 0)
+	wp->w_wincol = Columns > 20 ? Columns / 2 - 10 : 0;
+    else
+	--wp->w_wincol;  // option value is one-based
+
+
+    // TODO: set width based on longest text line and the 'wrap' option
+    wp->w_width = wp->w_maxwidth == 0 ? 20 : wp->w_maxwidth;
+    if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
+	wp->w_width = wp->w_maxwidth;
+    if (wp->w_width > Columns - wp->w_wincol)
+	wp->w_width = Columns - wp->w_wincol;
+
+    // TODO: adjust height for wrapped lines
+    wp->w_height = buf->b_ml.ml_line_count;
+    if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
+	wp->w_height = wp->w_maxheight;
+    if (wp->w_height > Rows - wp->w_winrow)
+	wp->w_height = Rows - wp->w_winrow;
+
+    wp->w_vsep_width = 0;
+
+    redraw_all_later(NOT_VALID);
+}
+
+/*
+ * popup_close({id})
+ */
+    void
+f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    int		nr = (int)tv_get_number(argvars);
+
+    popup_close(nr);
+}
+
+    void
+popup_close(int nr)
+{
+    win_T	*wp;
+    win_T	*prev = NULL;
+
+    for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
+	if (wp->w_id == nr)
+	{
+	    if (prev == NULL)
+		first_popupwin = wp->w_next;
+	    else
+		prev->w_next = wp->w_next;
+	    break;
+	}
+
+    if (wp == NULL)
+    {
+	prev = NULL;
+	for (wp = first_tab_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
+	    if (wp->w_id == nr)
+	    {
+		if (prev == NULL)
+		    first_tab_popupwin = wp->w_next;
+		else
+		    prev->w_next = wp->w_next;
+		break;
+	    }
+    }
+    if (wp != NULL)
+    {
+	win_free_popup(wp);
+	redraw_all_later(NOT_VALID);
+    }
+}
+
+    void
+close_all_popups(void)
+{
+    while (first_popupwin != NULL)
+	popup_close(first_popupwin->w_id);
+    while (first_tab_popupwin != NULL)
+	popup_close(first_tab_popupwin->w_id);
+}
+
+    void
+ex_popupclear(exarg_T *eap UNUSED)
+{
+    close_all_popups();
+}
+
+#endif // FEAT_TEXT_PROP
diff --git a/src/proto.h b/src/proto.h
index 4d2eded..e606f08 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -195,6 +195,7 @@
 #  include "termlib.pro"
 # endif
 # ifdef FEAT_TEXT_PROP
+#  include "popupwin.pro"
 #  include "textprop.pro"
 # endif
 # include "ui.pro"
diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro
index 5e1aa7a..1748fb5 100644
--- a/src/proto/buffer.pro
+++ b/src/proto/buffer.pro
@@ -61,6 +61,7 @@
 int bt_terminal(buf_T *buf);
 int bt_help(buf_T *buf);
 int bt_prompt(buf_T *buf);
+int bt_popup(buf_T *buf);
 int bt_nofile(buf_T *buf);
 int bt_dontwrite(buf_T *buf);
 int bt_dontwrite_msg(buf_T *buf);
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
new file mode 100644
index 0000000..a3d78b8
--- /dev/null
+++ b/src/proto/popupwin.pro
@@ -0,0 +1,7 @@
+/* popupwin.c */
+void f_popup_create(typval_T *argvars, typval_T *rettv);
+void f_popup_close(typval_T *argvars, typval_T *rettv);
+void popup_close(int nr);
+void close_all_popups(void);
+void ex_popupclear(exarg_T *eap);
+/* vim: set ft=c : */
diff --git a/src/proto/window.pro b/src/proto/window.pro
index b4ad98a..b5cbf69 100644
--- a/src/proto/window.pro
+++ b/src/proto/window.pro
@@ -19,7 +19,8 @@
 void curwin_init(void);
 void win_init_empty(win_T *wp);
 int win_alloc_first(void);
-void win_alloc_aucmd_win(void);
+win_T *win_alloc_popup_win(void);
+void win_init_popup_win(win_T *wp, buf_T *buf);
 void win_init_size(void);
 void free_tabpage(tabpage_T *tp);
 int win_new_tabpage(int after);
@@ -42,6 +43,8 @@
 void win_enter(win_T *wp, int undo_sync);
 win_T *buf_jump_open_win(buf_T *buf);
 win_T *buf_jump_open_tab(buf_T *buf);
+int win_unlisted(win_T *wp);
+void win_free_popup(win_T *win);
 void win_append(win_T *after, win_T *wp);
 void win_remove(win_T *wp, tabpage_T *tp);
 int win_alloc_lines(win_T *wp);
diff --git a/src/screen.c b/src/screen.c
index 4691775..79baebe 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -121,6 +121,9 @@
  */
 static schar_T	*current_ScreenLine;
 
+#ifdef FEAT_TEXT_PROP
+static void update_popups(void);
+#endif
 static void win_update(win_T *wp);
 static void win_redr_status(win_T *wp, int ignore_pum);
 static void win_draw_end(win_T *wp, int c1, int c2, int draw_margin, int row, int endrow, hlf_T hl);
@@ -178,6 +181,10 @@
 # define HAS_RIGHTLEFT(x) FALSE
 #endif
 
+// flags for screen_line()
+#define SLF_RIGHTLEFT	1
+#define SLF_POPUP	2
+
 /*
  * Redraw the current window later, with update_screen(type).
  * Set must_redraw only if not already set to a higher value.
@@ -406,7 +413,7 @@
 		    mch_memmove(ScreenLines2 + off,
 				screenline2 + r * cols,
 				(size_t)cols * sizeof(schar_T));
-		screen_line(cmdline_row + r, 0, cols, cols, FALSE);
+		screen_line(cmdline_row + r, 0, cols, cols, 0);
 	    }
 	    ret = 4;
 	}
@@ -604,6 +611,11 @@
 	    curwin->w_lines_valid = 0;	/* don't use w_lines[].wl_size now */
 	return FAIL;
     }
+#ifdef FEAT_TEXT_PROP
+    // TODO: avoid redrawing everything when there is a popup window.
+    if (first_popupwin != NULL || first_tab_popupwin != NULL)
+	type = NOT_VALID;
+#endif
 
     updating_screen = TRUE;
 #ifdef FEAT_SYN_HL
@@ -811,6 +823,11 @@
 	maybe_intro_message();
     did_intro = TRUE;
 
+#ifdef FEAT_TEXT_PROP
+    // Display popup windows on top of the others.
+    update_popups();
+#endif
+
 #ifdef FEAT_GUI
     /* Redraw the cursor and update the scrollbars when all screen updating is
      * done. */
@@ -975,6 +992,50 @@
 }
 #endif
 
+#ifdef FEAT_TEXT_PROP
+    static void
+update_popups(void)
+{
+    win_T   *wp;
+    win_T   *lowest_wp;
+    int	    lowest_zindex;
+
+    // Reset all the VALID_POPUP flags.
+    for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	wp->w_valid &= ~VALID_POPUP;
+    for (wp = first_tab_popupwin; wp != NULL; wp = wp->w_next)
+	wp->w_valid &= ~VALID_POPUP;
+
+    // TODO: don't redraw every popup every time.
+    for (;;)
+    {
+	// Find the window with the lowest zindex that hasn't been updated yet,
+	// so that the window with a higher zindex is drawn later, thus goes on
+	// top.
+	lowest_zindex = INT_MAX;
+	lowest_wp = NULL;
+	for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	    if ((wp->w_valid & VALID_POPUP) == 0
+					       && wp->w_zindex < lowest_zindex)
+	    {
+		lowest_zindex = wp->w_zindex;
+		lowest_wp = wp;
+	    }
+	for (wp = first_tab_popupwin; wp != NULL; wp = wp->w_next)
+	    if ((wp->w_valid & VALID_POPUP) == 0
+					       && wp->w_zindex < lowest_zindex)
+	    {
+		lowest_zindex = wp->w_zindex;
+		lowest_wp = wp;
+	    }
+
+	if (lowest_wp == NULL)
+	    break;
+	win_update(lowest_wp);
+	lowest_wp->w_valid |= VALID_POPUP;
+    }
+}
+#endif
 
 #if defined(FEAT_GUI) || defined(PROTO)
 /*
@@ -2862,7 +2923,7 @@
 #endif
 
     screen_line(row + W_WINROW(wp), wp->w_wincol, (int)wp->w_width,
-						     (int)wp->w_width, FALSE);
+						     (int)wp->w_width, 0);
 
     /*
      * Update w_cline_height and w_cline_folded if the cursor line was
@@ -3046,7 +3107,8 @@
     int		attr_pri = FALSE;	/* char_attr has priority */
     int		area_highlighting = FALSE; /* Visual or incsearch highlighting
 					      in this line */
-    int		attr = 0;		/* attributes for area highlighting */
+    int		vi_attr = 0;		/* attributes for Visual and incsearch
+					   highlighting */
     int		area_attr = 0;		/* attributes desired by highlighting */
     int		search_attr = 0;	/* attributes desired by 'hlsearch' */
 #ifdef FEAT_SYN_HL
@@ -3127,8 +3189,8 @@
 #endif
 #ifdef FEAT_TERMINAL
     int		get_term_attr = FALSE;
-    int		term_attr = 0;		/* background for terminal window */
 #endif
+    int		win_attr = 0;		// background for whole window
 
     /* draw_state: items that are drawn in sequence: */
 #define WL_START	0		/* nothing done yet */
@@ -3164,6 +3226,7 @@
     int		feedback_col = 0;
     int		feedback_old_attr = -1;
 #endif
+    int		screen_line_flags = 0;
 
 #ifdef FEAT_CONCEAL
     int		syntax_flags	= 0;
@@ -3244,7 +3307,7 @@
 	{
 	    extra_check = TRUE;
 	    get_term_attr = TRUE;
-	    term_attr = term_get_attr(wp->w_buffer, lnum, -1);
+	    win_attr = term_get_attr(wp->w_buffer, lnum, -1);
 	}
 #endif
 
@@ -3362,13 +3425,13 @@
 	    if (fromcol >= 0)
 	    {
 		area_highlighting = TRUE;
-		attr = HL_ATTR(HLF_V);
+		vi_attr = HL_ATTR(HLF_V);
 #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
 		if ((clip_star.available && !clip_star.owned
-							 && clip_isautosel_star())
+						      && clip_isautosel_star())
 			|| (clip_plus.available && !clip_plus.owned
-							&& clip_isautosel_plus()))
-		    attr = HL_ATTR(HLF_VNC);
+						     && clip_isautosel_plus()))
+		    vi_attr = HL_ATTR(HLF_VNC);
 #endif
 	    }
 	}
@@ -3398,7 +3461,7 @@
 	    if (fromcol == tocol)
 		tocol = fromcol + 1;
 	    area_highlighting = TRUE;
-	    attr = HL_ATTR(HLF_I);
+	    vi_attr = HL_ATTR(HLF_I);
 	}
     }
 
@@ -3497,6 +3560,30 @@
 	}
     }
 
+    if (*wp->w_p_wcr != NUL)
+    {
+	int attr = syn_name2attr(wp->w_p_wcr);
+
+	// 'wincolor' highlighting for the whole window
+	if (attr != 0)
+	{
+	    win_attr = attr;
+	    area_highlighting = TRUE;
+	}
+    }
+#ifdef FEAT_TEXT_PROP
+    if (bt_popup(wp->w_buffer))
+    {
+	screen_line_flags |= SLF_POPUP;
+
+	if (win_attr == 0)
+	{
+	    win_attr = HL_ATTR(HLF_PNI);
+	    area_highlighting = TRUE;
+	}
+    }
+#endif
+
     /*
      * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
      * first character to be displayed.
@@ -3630,10 +3717,12 @@
     /*
      * Handle highlighting the last used search pattern and matches.
      * Do this for both search_hl and the match list.
+     * Not in a popup window.
      */
     cur = wp->w_match_head;
     shl_flag = FALSE;
-    while ((cur != NULL || shl_flag == FALSE) && !number_only)
+    while ((cur != NULL || shl_flag == FALSE) && !number_only
+					   && !(screen_line_flags & SLF_POPUP))
     {
 	if (shl_flag == FALSE)
 	{
@@ -3729,6 +3818,7 @@
 
     off = (unsigned)(current_ScreenLine - ScreenLines);
     col = 0;
+
 #ifdef FEAT_RIGHTLEFT
     if (wp->w_p_rl)
     {
@@ -3737,6 +3827,7 @@
 	 * rightmost column of the window. */
 	col = wp->w_width - 1;
 	off += col;
+	screen_line_flags |= SLF_RIGHTLEFT;
     }
 #endif
 
@@ -4048,7 +4139,7 @@
 		    char_attr = saved_char_attr;
 		}
 		else
-		    char_attr = 0;
+		    char_attr = win_attr;
 	    }
 	}
 
@@ -4064,7 +4155,7 @@
 		|| (number_only && draw_state > WL_NR))
 	{
 	    screen_line(screen_row, wp->w_wincol, col, -(int)wp->w_width,
-						    HAS_RIGHTLEFT(wp->w_p_rl));
+							    screen_line_flags);
 	    /* Pretend we have finished updating the window.  Except when
 	     * 'cursorcolumn' is set. */
 #ifdef FEAT_SYN_HL
@@ -4089,7 +4180,7 @@
 		    || ((int)vcol_prev == fromcol_prev
 			&& vcol_prev < vcol	/* not at margin */
 			&& vcol < tocol))
-		area_attr = attr;		/* start highlighting */
+		area_attr = vi_attr;		/* start highlighting */
 	    else if (area_attr != 0
 		    && (vcol == tocol
 			|| (noinvcur && (colnr_T)vcol == wp->w_virtcol)))
@@ -4344,6 +4435,8 @@
 		    char_attr = 0;
 	    }
 	}
+	if (char_attr == 0)
+	    char_attr = win_attr;
 
 	/*
 	 * Get the next character to put on the screen.
@@ -4672,9 +4765,15 @@
 		    {
 			wp->w_s->b_syn_error = TRUE;
 			has_syntax = FALSE;
+			syntax_attr = 0;
 		    }
 		    else
 			did_emsg = save_did_emsg;
+
+		    // combine syntax attribute with 'wincolor'
+		    if (win_attr != 0)
+			syntax_attr = hl_combine_attr(win_attr, syntax_attr);
+
 #ifdef SYN_TIME_LIMIT
 		    if (wp->w_s->b_syn_slow)
 			has_syntax = FALSE;
@@ -5149,7 +5248,7 @@
 			    diff_hlf != (hlf_T)0 ||
 # endif
 # ifdef FEAT_TERMINAL
-			    term_attr != 0 ||
+			    win_attr != 0 ||
 # endif
 			    line_attr != 0
 			) && (
@@ -5178,7 +5277,7 @@
 		    if (diff_hlf == HLF_TXD)
 		    {
 			diff_hlf = HLF_CHD;
-			if (attr == 0 || char_attr != attr)
+			if (vi_attr == 0 || char_attr != vi_attr)
 			{
 			    char_attr = HL_ATTR(diff_hlf);
 			    if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
@@ -5188,9 +5287,9 @@
 		    }
 # endif
 # ifdef FEAT_TERMINAL
-		    if (term_attr != 0)
+		    if (win_attr != 0)
 		    {
-			char_attr = term_attr;
+			char_attr = win_attr;
 			if (wp->w_p_cul && lnum == wp->w_cursor.lnum)
 			    char_attr = hl_combine_attr(char_attr,
 							    HL_ATTR(HLF_CUL));
@@ -5550,7 +5649,8 @@
 		      && (int)wp->w_virtcol <
 					wp->w_width * (row - startrow + 1) + v
 		      && lnum != wp->w_cursor.lnum)
-		    || draw_color_col)
+		    || draw_color_col
+		    || win_attr != 0)
 # ifdef FEAT_RIGHTLEFT
 		    && !wp->w_p_rl
 # endif
@@ -5582,9 +5682,9 @@
 		    else if (draw_color_col && VCOL_HLC == *color_cols)
 			ScreenAttrs[off++] = HL_ATTR(HLF_MC);
 		    else
-			ScreenAttrs[off++] = 0;
+			ScreenAttrs[off++] = win_attr;
 
-		    if (VCOL_HLC >= rightmost_vcol)
+		    if (VCOL_HLC >= rightmost_vcol && win_attr == 0)
 			break;
 
 		    ++vcol;
@@ -5593,7 +5693,7 @@
 #endif
 
 	    screen_line(screen_row, wp->w_wincol, col,
-				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
+					  (int)wp->w_width, screen_line_flags);
 	    row++;
 
 	    /*
@@ -5893,11 +5993,11 @@
 	{
 #ifdef FEAT_CONCEAL
 	    screen_line(screen_row, wp->w_wincol, col - boguscols,
-				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
+					  (int)wp->w_width, screen_line_flags);
 	    boguscols = 0;
 #else
 	    screen_line(screen_row, wp->w_wincol, col,
-				  (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl));
+					  (int)wp->w_width, screen_line_flags);
 #endif
 	    ++row;
 	    ++screen_row;
@@ -6107,7 +6207,9 @@
  * "endcol" gives the columns where valid characters are.
  * "clear_width" is the width of the window.  It's > 0 if the rest of the line
  * needs to be cleared, negative otherwise.
- * "rlflag" is TRUE in a rightleft window:
+ * "flags" can have bits:
+ * SLF_POPUP	    popup window
+ * SLF_RIGHTLEFT    rightleft window:
  *    When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
  *    When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
  */
@@ -6117,7 +6219,7 @@
     int	    coloff,
     int	    endcol,
     int	    clear_width,
-    int	    rlflag UNUSED)
+    int	    flags UNUSED)
 {
     unsigned	    off_from;
     unsigned	    off_to;
@@ -6153,7 +6255,7 @@
     max_off_to = LineOffset[row] + screen_Columns;
 
 #ifdef FEAT_RIGHTLEFT
-    if (rlflag)
+    if (flags & SLF_RIGHTLEFT)
     {
 	/* Clear rest first, because it's left of the text. */
 	if (clear_width > 0)
@@ -6377,7 +6479,7 @@
 
     if (clear_width > 0
 #ifdef FEAT_RIGHTLEFT
-		    && !rlflag
+		    && !(flags & SLF_RIGHTLEFT)
 #endif
 				   )
     {
@@ -6444,10 +6546,15 @@
 	}
     }
 
-    if (clear_width > 0)
+    if (clear_width > 0
+#ifdef FEAT_TEXT_PROP
+	    && !(flags & SLF_POPUP)  // no separator for popup window
+#endif
+	    )
     {
-	/* For a window that's left of another, draw the separator char. */
-	if (col + coloff < Columns)
+	// For a window that has a right neighbor, draw the separator char
+	// right of the window contents.
+	if (coloff + col < Columns)
 	{
 	    int c;
 
@@ -10784,7 +10891,7 @@
     wp->w_winbar_items[item_idx].wb_menu = NULL; /* end marker */
 
     screen_line(wp->w_winrow, wp->w_wincol, (int)wp->w_width,
-						     (int)wp->w_width, FALSE);
+							  (int)wp->w_width, 0);
 }
 #endif
 
diff --git a/src/structs.h b/src/structs.h
index afd3e02..e424830 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -163,17 +163,19 @@
 {
 #ifdef FEAT_ARABIC
     int		wo_arab;
-# define w_p_arab w_onebuf_opt.wo_arab	/* 'arabic' */
+# define w_p_arab w_onebuf_opt.wo_arab	// 'arabic'
 #endif
 #ifdef FEAT_LINEBREAK
     int		wo_bri;
-# define w_p_bri w_onebuf_opt.wo_bri	/* 'breakindent' */
+# define w_p_bri w_onebuf_opt.wo_bri	// 'breakindent'
     char_u	*wo_briopt;
-# define w_p_briopt w_onebuf_opt.wo_briopt /* 'breakindentopt' */
+# define w_p_briopt w_onebuf_opt.wo_briopt // 'breakindentopt'
 #endif
+    char_u	*wo_wcr;
+# define w_p_wcr w_onebuf_opt.wo_wcr	// 'wincolor'
 #ifdef FEAT_DIFF
     int		wo_diff;
-# define w_p_diff w_onebuf_opt.wo_diff	/* 'diff' */
+# define w_p_diff w_onebuf_opt.wo_diff	// 'diff'
 #endif
 #ifdef FEAT_FOLDING
     long	wo_fdc;
@@ -2592,19 +2594,22 @@
 typedef struct tabpage_S tabpage_T;
 struct tabpage_S
 {
-    tabpage_T	    *tp_next;	    /* next tabpage or NULL */
-    frame_T	    *tp_topframe;   /* topframe for the windows */
-    win_T	    *tp_curwin;	    /* current window in this Tab page */
-    win_T	    *tp_prevwin;    /* previous window in this Tab page */
-    win_T	    *tp_firstwin;   /* first window in this Tab page */
-    win_T	    *tp_lastwin;    /* last window in this Tab page */
-    long	    tp_old_Rows;    /* Rows when Tab page was left */
-    long	    tp_old_Columns; /* Columns when Tab page was left */
-    long	    tp_ch_used;	    /* value of 'cmdheight' when frame size
-				       was set */
+    tabpage_T	    *tp_next;	    // next tabpage or NULL
+    frame_T	    *tp_topframe;   // topframe for the windows
+    win_T	    *tp_curwin;	    // current window in this Tab page
+    win_T	    *tp_prevwin;    // previous window in this Tab page
+    win_T	    *tp_firstwin;   // first window in this Tab page
+    win_T	    *tp_lastwin;    // last window in this Tab page
+#ifdef FEAT_TEXT_PROP
+    win_T	    *tp_first_popupwin; // first popup window in this Tab page
+#endif
+    long	    tp_old_Rows;    // Rows when Tab page was left
+    long	    tp_old_Columns; // Columns when Tab page was left
+    long	    tp_ch_used;	    // value of 'cmdheight' when frame size
+				    // was set
 #ifdef FEAT_GUI
     int		    tp_prev_which_scrollbars[3];
-				    /* previous value of which_scrollbars */
+				    // previous value of which_scrollbars
 #endif
 
     char_u	    *tp_localdir;	// absolute path of local directory or
@@ -2615,18 +2620,18 @@
     int		    tp_diff_invalid;	// list of diffs is outdated
     int		    tp_diff_update;	// update diffs before redrawing
 #endif
-    frame_T	    *(tp_snapshot[SNAP_COUNT]);  /* window layout snapshots */
+    frame_T	    *(tp_snapshot[SNAP_COUNT]);  // window layout snapshots
 #ifdef FEAT_EVAL
-    dictitem_T	    tp_winvar;	    /* variable for "t:" Dictionary */
-    dict_T	    *tp_vars;	    /* internal variables, local to tab page */
+    dictitem_T	    tp_winvar;	    // variable for "t:" Dictionary
+    dict_T	    *tp_vars;	    // internal variables, local to tab page
 #endif
 
 #ifdef FEAT_PYTHON
-    void	    *tp_python_ref;	/* The Python value for this tab page */
+    void	    *tp_python_ref;	// The Python value for this tab page
 #endif
 
 #ifdef FEAT_PYTHON3
-    void	    *tp_python3_ref;	/* The Python value for this tab page */
+    void	    *tp_python3_ref;	// The Python value for this tab page
 #endif
 };
 
@@ -2775,15 +2780,15 @@
 {
     int		w_id;		    /* unique window ID */
 
-    buf_T	*w_buffer;	    /* buffer we are a window into (used
-				       often, keep it the first item!) */
+    buf_T	*w_buffer;	    /* buffer we are a window into */
+
+    win_T	*w_prev;	    /* link to previous window */
+    win_T	*w_next;	    /* link to next window */
 
 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
     synblock_T	*w_s;		    /* for :ownsyntax */
 #endif
 
-    win_T	*w_prev;	    /* link to previous window */
-    win_T	*w_next;	    /* link to next window */
     int		w_closing;	    /* window is being closed, don't let
 				       autocommands close it too. */
 
@@ -2847,6 +2852,11 @@
     int		w_width;	    /* Width of window, excluding separation. */
     int		w_vsep_width;	    /* Number of separator columns (0 or 1). */
     pos_save_T	w_save_cursor;	    /* backup of cursor pos and topline */
+#ifdef FEAT_TEXT_PROP
+    int		w_zindex;
+    int		w_maxheight;	    // "maxheight" for popup window
+    int		w_maxwidth;	    // "maxwidth" for popup window
+#endif
 
 
     /*
diff --git a/src/terminal.c b/src/terminal.c
index 1ceac3a..606a9db 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -3257,7 +3257,7 @@
 	else
 	    pos.col = 0;
 
-	screen_line(term->tl_toprow + pos.row, 0, pos.col, Columns, FALSE);
+	screen_line(term->tl_toprow + pos.row, 0, pos.col, Columns, 0);
     }
 
     term->tl_dirty_row_start = MAX_ROW;
@@ -3368,7 +3368,7 @@
 #ifdef FEAT_MENU
 				+ winbar_height(wp)
 #endif
-				, wp->w_wincol, pos.col, wp->w_width, FALSE);
+				, wp->w_wincol, pos.col, wp->w_width, 0);
     }
     term->tl_dirty_row_start = MAX_ROW;
     term->tl_dirty_row_end = 0;
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 6442236..9de5f16 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -196,6 +196,7 @@
 	test_perl \
 	test_plus_arg_edit \
 	test_popup \
+	test_popupwin \
 	test_preview \
 	test_profile \
 	test_prompt_buffer \
@@ -250,8 +251,8 @@
 	test_tagjump \
 	test_taglist \
 	test_tcl \
-	test_termencoding \
 	test_termcodes \
+	test_termencoding \
 	test_terminal \
 	test_terminal_fail \
 	test_textformat \
@@ -377,6 +378,7 @@
 	test_paste.res \
 	test_perl.res \
 	test_plus_arg_edit.res \
+	test_popupwin.res \
 	test_preview.res \
 	test_profile.res \
 	test_prompt_buffer.res \
@@ -409,8 +411,8 @@
 	test_system.res \
 	test_tab.res \
 	test_tcl.res \
-	test_termencoding.res \
 	test_termcodes.res \
+	test_termencoding.res \
 	test_terminal.res \
 	test_terminal_fail.res \
 	test_textformat.res \
diff --git a/src/testdir/dumps/Test_popupwin_01.dump b/src/testdir/dumps/Test_popupwin_01.dump
new file mode 100644
index 0000000..b3818c1
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_01.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @8|h+0&#5fd7ff255|e|l@1|o| |t|h|e|r|e| @8|r+0#0000001#ffd7ff255| |o|n|e| @8| +0#0000000#ffffff0@30
+|4| @22|a+0#0000001#ffd7ff255|n|o|t|h|e|r| |t|w|o| @8| +0#0000000#ffffff0@30
+|5| @22|a+0#0000001#ffd7ff255|n|o|t|h|e|r| |t|h|r|e@1| @6| +0#0000000#ffffff0@30
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+@57|1|,|1| @10|T|o|p| 
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
new file mode 100644
index 0000000..7bae0b5
--- /dev/null
+++ b/src/testdir/test_popupwin.vim
@@ -0,0 +1,26 @@
+" Tests for popup windows
+
+if !has('textprop')
+  finish
+endif
+
+source screendump.vim
+
+func Test_simple_popup()
+  if !CanRunVimInTerminal()
+    return
+  endif
+  call writefile([
+	\ "call setline(1, range(1, 100))",
+	\ "let winid = popup_create('hello there', {'line': 3, 'col': 11})",
+	\ "hi PopupColor ctermbg=lightblue",
+	\ "call setwinvar(winid, '&wincolor', 'PopupColor')",
+	\ "let winid2 = popup_create(['another one', 'another two', 'another three'], {'line': 3, 'col': 25})",
+	\], 'XtestPopup')
+  let buf = RunVimInTerminal('-S XtestPopup', {'rows': 10})
+  call VerifyScreenDump(buf, 'Test_popupwin_01', {})
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('XtestPopup')
+endfunc
diff --git a/src/version.c b/src/version.c
index a971633..5573f44 100644
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1391,
+/**/
     1390,
 /**/
     1389,
diff --git a/src/vim.h b/src/vim.h
index 57fbe36..3389ef5 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -604,14 +604,15 @@
  *     off		off		w_botline not valid
  *     on		off		not possible
  */
-#define VALID_WROW	0x01	/* w_wrow (window row) is valid */
-#define VALID_WCOL	0x02	/* w_wcol (window col) is valid */
-#define VALID_VIRTCOL	0x04	/* w_virtcol (file col) is valid */
-#define VALID_CHEIGHT	0x08	/* w_cline_height and w_cline_folded valid */
-#define VALID_CROW	0x10	/* w_cline_row is valid */
-#define VALID_BOTLINE	0x20	/* w_botine and w_empty_rows are valid */
-#define VALID_BOTLINE_AP 0x40	/* w_botine is approximated */
-#define VALID_TOPLINE	0x80	/* w_topline is valid (for cursor position) */
+#define VALID_WROW	0x01	// w_wrow (window row) is valid
+#define VALID_WCOL	0x02	// w_wcol (window col) is valid
+#define VALID_VIRTCOL	0x04	// w_virtcol (file col) is valid
+#define VALID_CHEIGHT	0x08	// w_cline_height and w_cline_folded valid
+#define VALID_CROW	0x10	// w_cline_row is valid
+#define VALID_BOTLINE	0x20	// w_botine and w_empty_rows are valid
+#define VALID_BOTLINE_AP 0x40	// w_botine is approximated
+#define VALID_TOPLINE	0x80	// w_topline is valid (for cursor position)
+#define VALID_POPUP	0x100	// popup has been redrawn
 
 /*
  * Terminal highlighting attribute bits.
diff --git a/src/window.c b/src/window.c
index 6e02caa..bed3d3f 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1362,6 +1362,21 @@
     win_copy_options(oldp, newp);
 }
 
+    static int
+win_valid_popup(win_T *win)
+{
+#ifdef FEAT_TEXT_PROP
+    win_T	*wp;
+
+    for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	if (wp == win)
+	    return TRUE;
+    for (wp = first_tab_popupwin; wp != NULL; wp = wp->w_next)
+	if (wp == win)
+	    return TRUE;
+#endif
+    return FALSE;
+}
 
 /*
  * Check if "win" is a pointer to an existing window in the current tab page.
@@ -1376,7 +1391,7 @@
     FOR_ALL_WINDOWS(wp)
 	if (wp == win)
 	    return TRUE;
-    return FALSE;
+    return win_valid_popup(win);
 }
 
 /*
@@ -1398,7 +1413,7 @@
 		return TRUE;
 	}
     }
-    return FALSE;
+    return win_valid_popup(win);
 }
 
 /*
@@ -2293,6 +2308,44 @@
 }
 
 /*
+ * Close the buffer of "win" and unload it if "free_buf" is TRUE.
+ * "abort_if_last" is passed to close_buffer(): abort closing if all other
+ * windows are closed.
+ */
+    static void
+win_close_buffer(win_T *win, int free_buf, int abort_if_last)
+{
+#ifdef FEAT_SYN_HL
+    // Free independent synblock before the buffer is freed.
+    if (win->w_buffer != NULL)
+	reset_synblock(win);
+#endif
+
+#ifdef FEAT_QUICKFIX
+    // When the quickfix/location list window is closed, unlist the buffer.
+    if (win->w_buffer != NULL && bt_quickfix(win->w_buffer))
+	win->w_buffer->b_p_bl = FALSE;
+#endif
+
+    // Close the link to the buffer.
+    if (win->w_buffer != NULL)
+    {
+	bufref_T    bufref;
+
+	set_bufref(&bufref, curbuf);
+	win->w_closing = TRUE;
+	close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,
+								abort_if_last);
+	if (win_valid_any_tab(win))
+	    win->w_closing = FALSE;
+	// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
+	// "wipe".
+	if (!bufref_valid(&bufref))
+	    curbuf = firstbuf;
+    }
+}
+
+/*
  * Close window "win".  Only works for the current tab page.
  * If "free_buf" is TRUE related buffer may be unloaded.
  *
@@ -2319,9 +2372,9 @@
     if (win->w_closing || (win->w_buffer != NULL
 					       && win->w_buffer->b_locked > 0))
 	return FAIL; /* window is already being closed */
-    if (win == aucmd_win)
+    if (win_unlisted(win))
     {
-	emsg(_("E813: Cannot close autocmd window"));
+	emsg(_("E813: Cannot close autocmd or popup window"));
 	return FAIL;
     }
     if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window())
@@ -2390,35 +2443,7 @@
 	out_flush();
 #endif
 
-#ifdef FEAT_SYN_HL
-    // Free independent synblock before the buffer is freed.
-    if (win->w_buffer != NULL)
-	reset_synblock(win);
-#endif
-
-#ifdef FEAT_QUICKFIX
-    // When the quickfix/location list window is closed, unlist the buffer.
-    if (win->w_buffer != NULL && bt_quickfix(win->w_buffer))
-	win->w_buffer->b_p_bl = FALSE;
-#endif
-
-    /*
-     * Close the link to the buffer.
-     */
-    if (win->w_buffer != NULL)
-    {
-	bufref_T    bufref;
-
-	set_bufref(&bufref, curbuf);
-	win->w_closing = TRUE;
-	close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE);
-	if (win_valid_any_tab(win))
-	    win->w_closing = FALSE;
-	/* Make sure curbuf is valid. It can become invalid if 'bufhidden' is
-	 * "wipe". */
-	if (!bufref_valid(&bufref))
-	    curbuf = firstbuf;
-    }
+    win_close_buffer(win, free_buf, TRUE);
 
     if (only_one_window() && win_valid(win) && win->w_buffer == NULL
 	    && (last_window() || curtab != prev_curtab
@@ -2627,6 +2652,9 @@
 	(void)win_free_mem(aucmd_win, &dummy, NULL);
 	aucmd_win = NULL;
     }
+# ifdef FEAT_TEXT_PROP
+    close_all_popups();
+# endif
 
     while (firstwin != NULL)
 	(void)win_free_mem(firstwin, &dummy, NULL);
@@ -3458,7 +3486,7 @@
     wp->w_topfill = 0;
 #endif
     wp->w_botline = 2;
-#ifdef FEAT_SYN_HL
+#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
     wp->w_s = &wp->w_buffer->b_s;
 #endif
 }
@@ -3484,19 +3512,41 @@
 }
 
 /*
- * Init "aucmd_win".  This can only be done after the first
- * window is fully initialized, thus it can't be in win_alloc_first().
+ * Allocate and init a window that is not a regular window.
+ * This can only be done after the first window is fully initialized, thus it
+ * can't be in win_alloc_first().
+ */
+    win_T *
+win_alloc_popup_win(void)
+{
+    win_T *wp;
+
+    wp = win_alloc(NULL, TRUE);
+    if (wp != NULL)
+    {
+	// We need to initialize options with something, using the current
+	// window makes most sense.
+	win_init_some(wp, curwin);
+
+	RESET_BINDING(wp);
+	new_frame(wp);
+    }
+    return wp;
+}
+
+/*
+ * Initialize window "wp" to display buffer "buf".
  */
     void
-win_alloc_aucmd_win(void)
+win_init_popup_win(win_T *wp, buf_T *buf)
 {
-    aucmd_win = win_alloc(NULL, TRUE);
-    if (aucmd_win != NULL)
-    {
-	win_init_some(aucmd_win, curwin);
-	RESET_BINDING(aucmd_win);
-	new_frame(aucmd_win);
-    }
+    wp->w_buffer = buf;
+    ++buf->b_nwindows;
+    win_init_empty(wp); // set cursor and topline to safe values
+
+    // Make sure w_localdir and globaldir are NULL to avoid a chdir() in
+    // win_enter_ext().
+    VIM_CLEAR(wp->w_localdir);
 }
 
 /*
@@ -3619,6 +3669,10 @@
 # ifdef FEAT_DIFF
     diff_clear(tp);
 # endif
+# ifdef FEAT_TEXT_PROP
+    while (tp->tp_first_popupwin != NULL)
+	popup_close(tp->tp_first_popupwin->w_id);
+#endif
     for (idx = 0; idx < SNAP_COUNT; ++idx)
 	clear_snapshot(tp, idx);
 #ifdef FEAT_EVAL
@@ -4782,7 +4836,7 @@
     vim_free(wp->w_p_cc_cols);
 #endif
 
-    if (wp != aucmd_win)
+    if (win_valid_any_tab(wp))
 	win_remove(wp, tp);
     if (autocmd_busy)
     {
@@ -4796,6 +4850,28 @@
 }
 
 /*
+ * Return TRUE if "wp" is not in the list of windows: the autocmd window or a
+ * popup window.
+ */
+    int
+win_unlisted(win_T *wp)
+{
+    return wp == aucmd_win || bt_popup(wp->w_buffer);
+}
+
+/*
+ * Free a popup window.  This does not take the window out of the window list
+ * and assumes there is only one toplevel frame, no split.
+ */
+    void
+win_free_popup(win_T *win)
+{
+    win_close_buffer(win, TRUE, FALSE);
+    vim_free(win->w_frame);
+    win_free(win, NULL);
+}
+
+/*
  * Append window "wp" in the window list after window "after".
  */
     void
@@ -6182,7 +6258,7 @@
 /*
  * Return TRUE if there is only one window (in the current tab page), not
  * counting a help or preview window, unless it is the current window.
- * Does not count "aucmd_win".
+ * Does not count unlisted windows.
  */
     int
 only_one_window(void)
@@ -6974,6 +7050,16 @@
     FOR_ALL_TAB_WINDOWS(tp, wp)
 	if (wp->w_id == id)
 	    return wp;
+#ifdef FEAT_TEXT_PROP
+    // popup windows are in a separate list
+     FOR_ALL_TABPAGES(tp)
+	 for (wp = tp->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+	     if (wp->w_id == id)
+		 return wp;
+    for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	if (wp->w_id == id)
+	    return wp;
+#endif
 
     return NULL;
 }