diff options
Diffstat (limited to '')
-rw-r--r-- | lib/tty/Makefile.am | 36 | ||||
-rw-r--r-- | lib/tty/Makefile.in | 801 | ||||
-rw-r--r-- | lib/tty/color-internal.c | 244 | ||||
-rw-r--r-- | lib/tty/color-internal.h | 61 | ||||
-rw-r--r-- | lib/tty/color-ncurses.c | 251 | ||||
-rw-r--r-- | lib/tty/color-slang.c | 260 | ||||
-rw-r--r-- | lib/tty/color-slang.h | 56 | ||||
-rw-r--r-- | lib/tty/color.c | 244 | ||||
-rw-r--r-- | lib/tty/color.h | 54 | ||||
-rw-r--r-- | lib/tty/key.c | 2252 | ||||
-rw-r--r-- | lib/tty/key.h | 121 | ||||
-rw-r--r-- | lib/tty/keyxdef.c | 455 | ||||
-rw-r--r-- | lib/tty/mouse.c | 216 | ||||
-rw-r--r-- | lib/tty/mouse.h | 117 | ||||
-rw-r--r-- | lib/tty/tty-internal.c | 110 | ||||
-rw-r--r-- | lib/tty/tty-internal.h | 49 | ||||
-rw-r--r-- | lib/tty/tty-ncurses.c | 772 | ||||
-rw-r--r-- | lib/tty/tty-ncurses.h | 50 | ||||
-rw-r--r-- | lib/tty/tty-slang.c | 781 | ||||
-rw-r--r-- | lib/tty/tty-slang.h | 48 | ||||
-rw-r--r-- | lib/tty/tty.c | 416 | ||||
-rw-r--r-- | lib/tty/tty.h | 146 | ||||
-rw-r--r-- | lib/tty/win.c | 168 | ||||
-rw-r--r-- | lib/tty/win.h | 24 | ||||
-rw-r--r-- | lib/tty/x11conn.c | 266 | ||||
-rw-r--r-- | lib/tty/x11conn.h | 40 |
26 files changed, 8038 insertions, 0 deletions
diff --git a/lib/tty/Makefile.am b/lib/tty/Makefile.am new file mode 100644 index 0000000..d4260fe --- /dev/null +++ b/lib/tty/Makefile.am @@ -0,0 +1,36 @@ + +noinst_LTLIBRARIES = libmctty.la + +if USE_SCREEN_SLANG + TTY_SCREEN_SRC = \ + color-slang.c color-slang.h \ + tty-slang.c tty-slang.h +else + TTY_SCREEN_SRC = \ + color-ncurses.c \ + tty-ncurses.c tty-ncurses.h +endif + +TTY_SRC = \ + color-internal.c color-internal.h \ + color.c color.h \ + key.c key.h keyxdef.c \ + mouse.c mouse.h \ + tty-internal.c tty-internal.h \ + tty.c tty.h \ + win.c win.h + +if HAVE_TEXTMODE_X11_SUPPORT +TTY_SRC += x11conn.c x11conn.h +endif + +libmctty_la_SOURCES = $(TTY_SRC) $(TTY_SCREEN_SRC) + +AM_CPPFLAGS = -I$(top_srcdir) + +if HAVE_GMODULE +AM_CPPFLAGS += $(GMODULE_CFLAGS) +else +AM_CPPFLAGS += $(GLIB_CFLAGS) +endif + diff --git a/lib/tty/Makefile.in b/lib/tty/Makefile.in new file mode 100644 index 0000000..0aa0af8 --- /dev/null +++ b/lib/tty/Makefile.in @@ -0,0 +1,801 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_TEXTMODE_X11_SUPPORT_TRUE@am__append_1 = x11conn.c x11conn.h +@HAVE_GMODULE_TRUE@am__append_2 = $(GMODULE_CFLAGS) +@HAVE_GMODULE_FALSE@am__append_3 = $(GLIB_CFLAGS) +subdir = lib/tty +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/m4.include/gnulib/mode_t.m4 \ + $(top_srcdir)/m4.include/gnulib/stat-size.m4 \ + $(top_srcdir)/m4.include/gnulib/fstypename.m4 \ + $(top_srcdir)/m4.include/gnulib/fsusage.m4 \ + $(top_srcdir)/m4.include/gnulib/mountlist.m4 \ + $(top_srcdir)/m4.include/gnulib/windows-stat-inodes.m4 \ + $(top_srcdir)/m4.include/gnulib/sys_types_h.m4 \ + $(top_srcdir)/m4.include/ax_path_lib_pcre.m4 \ + $(top_srcdir)/m4.include/ax_check_pcre2.m4 \ + $(top_srcdir)/m4.include/dx_doxygen.m4 \ + $(top_srcdir)/m4.include/ax_require_defined.m4 \ + $(top_srcdir)/m4.include/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_compile_flags.m4 \ + $(top_srcdir)/m4.include/mc-cflags.m4 \ + $(top_srcdir)/m4.include/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4.include/mc-check-search-type.m4 \ + $(top_srcdir)/m4.include/mc-get-fs-info.m4 \ + $(top_srcdir)/m4.include/mc-with-x.m4 \ + $(top_srcdir)/m4.include/mc-use-termcap.m4 \ + $(top_srcdir)/m4.include/mc-with-screen.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-ncurses.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-slang.m4 \ + $(top_srcdir)/m4.include/mc-with-internal-edit.m4 \ + $(top_srcdir)/m4.include/mc-subshell.m4 \ + $(top_srcdir)/m4.include/mc-background.m4 \ + $(top_srcdir)/m4.include/mc-ext2fs-attr.m4 \ + $(top_srcdir)/m4.include/mc-glib.m4 \ + $(top_srcdir)/m4.include/mc-vfs.m4 \ + $(top_srcdir)/m4.include/vfs/rpc.m4 \ + $(top_srcdir)/m4.include/vfs/socket.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-extfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-ftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-fish.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-undelfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-tarfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-cpiofs.m4 \ + $(top_srcdir)/m4.include/mc-version.m4 \ + $(top_srcdir)/m4.include/mc-tests.m4 \ + $(top_srcdir)/m4.include/mc-i18n.m4 \ + $(top_srcdir)/m4.include/mc-assert.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libmctty_la_LIBADD = +am__libmctty_la_SOURCES_DIST = color-internal.c color-internal.h \ + color.c color.h key.c key.h keyxdef.c mouse.c mouse.h \ + tty-internal.c tty-internal.h tty.c tty.h win.c win.h \ + x11conn.c x11conn.h color-ncurses.c tty-ncurses.c \ + tty-ncurses.h color-slang.c color-slang.h tty-slang.c \ + tty-slang.h +@HAVE_TEXTMODE_X11_SUPPORT_TRUE@am__objects_1 = x11conn.lo +am__objects_2 = color-internal.lo color.lo key.lo keyxdef.lo mouse.lo \ + tty-internal.lo tty.lo win.lo $(am__objects_1) +@USE_SCREEN_SLANG_FALSE@am__objects_3 = color-ncurses.lo \ +@USE_SCREEN_SLANG_FALSE@ tty-ncurses.lo +@USE_SCREEN_SLANG_TRUE@am__objects_3 = color-slang.lo tty-slang.lo +am_libmctty_la_OBJECTS = $(am__objects_2) $(am__objects_3) +libmctty_la_OBJECTS = $(am_libmctty_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/color-internal.Plo \ + ./$(DEPDIR)/color-ncurses.Plo ./$(DEPDIR)/color-slang.Plo \ + ./$(DEPDIR)/color.Plo ./$(DEPDIR)/key.Plo \ + ./$(DEPDIR)/keyxdef.Plo ./$(DEPDIR)/mouse.Plo \ + ./$(DEPDIR)/tty-internal.Plo ./$(DEPDIR)/tty-ncurses.Plo \ + ./$(DEPDIR)/tty-slang.Plo ./$(DEPDIR)/tty.Plo \ + ./$(DEPDIR)/win.Plo ./$(DEPDIR)/x11conn.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libmctty_la_SOURCES) +DIST_SOURCES = $(am__libmctty_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ +COM_ERR_CFLAGS = @COM_ERR_CFLAGS@ +COM_ERR_LIBS = @COM_ERR_LIBS@ +CP1251 = @CP1251@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_LINGUAS = @DOC_LINGUAS@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +E2P_CFLAGS = @E2P_CFLAGS@ +E2P_LIBS = @E2P_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +EXT2FS_CFLAGS = @EXT2FS_CFLAGS@ +EXT2FS_LIBS = @EXT2FS_LIBS@ +EXTHELPERSDIR = @EXTHELPERSDIR@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_FILECMD = @HAVE_FILECMD@ +HAVE_ZIPINFO = @HAVE_ZIPINFO@ +HAVE_nroff = @HAVE_nroff@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMC_RELEASE = @LIBMC_RELEASE@ +LIBMC_VERSION = @LIBMC_VERSION@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSSH_CFLAGS = @LIBSSH_CFLAGS@ +LIBSSH_LIBS = @LIBSSH_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANDOC = @MANDOC@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_DATE = @MAN_DATE@ +MAN_FLAGS = @MAN_FLAGS@ +MAN_VERSION = @MAN_VERSION@ +MCLIBS = @MCLIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRE_CFLAGS = @PCRE_CFLAGS@ +PCRE_LIBS = @PCRE_LIBS@ +PERL = @PERL@ +PERL_FOR_BUILD = @PERL_FOR_BUILD@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +RUBY = @RUBY@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SLANG_CFLAGS = @SLANG_CFLAGS@ +SLANG_LIBS = @SLANG_LIBS@ +STRIP = @STRIP@ +TESTS_LDFLAGS = @TESTS_LDFLAGS@ +UNZIP = @UNZIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +X11_WWW = @X11_WWW@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +ZIP = @ZIP@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libmctty.la +@USE_SCREEN_SLANG_FALSE@TTY_SCREEN_SRC = \ +@USE_SCREEN_SLANG_FALSE@ color-ncurses.c \ +@USE_SCREEN_SLANG_FALSE@ tty-ncurses.c tty-ncurses.h + +@USE_SCREEN_SLANG_TRUE@TTY_SCREEN_SRC = \ +@USE_SCREEN_SLANG_TRUE@ color-slang.c color-slang.h \ +@USE_SCREEN_SLANG_TRUE@ tty-slang.c tty-slang.h + +TTY_SRC = color-internal.c color-internal.h color.c color.h key.c \ + key.h keyxdef.c mouse.c mouse.h tty-internal.c tty-internal.h \ + tty.c tty.h win.c win.h $(am__append_1) +libmctty_la_SOURCES = $(TTY_SRC) $(TTY_SCREEN_SRC) +AM_CPPFLAGS = -I$(top_srcdir) $(am__append_2) $(am__append_3) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/tty/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/tty/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libmctty.la: $(libmctty_la_OBJECTS) $(libmctty_la_DEPENDENCIES) $(EXTRA_libmctty_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libmctty_la_OBJECTS) $(libmctty_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-internal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-ncurses.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-slang.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyxdef.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mouse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-internal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-ncurses.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-slang.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11conn.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/color-internal.Plo + -rm -f ./$(DEPDIR)/color-ncurses.Plo + -rm -f ./$(DEPDIR)/color-slang.Plo + -rm -f ./$(DEPDIR)/color.Plo + -rm -f ./$(DEPDIR)/key.Plo + -rm -f ./$(DEPDIR)/keyxdef.Plo + -rm -f ./$(DEPDIR)/mouse.Plo + -rm -f ./$(DEPDIR)/tty-internal.Plo + -rm -f ./$(DEPDIR)/tty-ncurses.Plo + -rm -f ./$(DEPDIR)/tty-slang.Plo + -rm -f ./$(DEPDIR)/tty.Plo + -rm -f ./$(DEPDIR)/win.Plo + -rm -f ./$(DEPDIR)/x11conn.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/color-internal.Plo + -rm -f ./$(DEPDIR)/color-ncurses.Plo + -rm -f ./$(DEPDIR)/color-slang.Plo + -rm -f ./$(DEPDIR)/color.Plo + -rm -f ./$(DEPDIR)/key.Plo + -rm -f ./$(DEPDIR)/keyxdef.Plo + -rm -f ./$(DEPDIR)/mouse.Plo + -rm -f ./$(DEPDIR)/tty-internal.Plo + -rm -f ./$(DEPDIR)/tty-ncurses.Plo + -rm -f ./$(DEPDIR)/tty-slang.Plo + -rm -f ./$(DEPDIR)/tty.Plo + -rm -f ./$(DEPDIR)/win.Plo + -rm -f ./$(DEPDIR)/x11conn.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/tty/color-internal.c b/lib/tty/color-internal.c new file mode 100644 index 0000000..8db2b6c --- /dev/null +++ b/lib/tty/color-internal.c @@ -0,0 +1,244 @@ +/* + Internal stuff of color setup + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009 + Slava Zanko <slavazanko@gmail.com>, 2009, 2013 + Egmont Koblinger <egmont@gmail.com>, 2010 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file color-internal.c + * \brief Source: Internal stuff of color setup + */ + +#include <config.h> + +#include <string.h> /* strcmp */ + +#include "color.h" /* colors and attributes */ +#include "color-internal.h" + +/*** global variables ****************************************************************************/ + +gboolean mc_tty_color_disable; + +/*** file scope macro definitions ****************************************************************/ + +#define COLOR_INTENSITY 8 + +/*** file scope type declarations ****************************************************************/ + +typedef struct mc_tty_color_table_struct +{ + const char *name; + int value; +} mc_tty_color_table_t; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static mc_tty_color_table_t const color_table[] = { + {"black", COLOR_BLACK}, + {"gray", COLOR_BLACK + COLOR_INTENSITY}, + {"red", COLOR_RED}, + {"brightred", COLOR_RED + COLOR_INTENSITY}, + {"green", COLOR_GREEN}, + {"brightgreen", COLOR_GREEN + COLOR_INTENSITY}, + {"brown", COLOR_YELLOW}, + {"yellow", COLOR_YELLOW + COLOR_INTENSITY}, + {"blue", COLOR_BLUE}, + {"brightblue", COLOR_BLUE + COLOR_INTENSITY}, + {"magenta", COLOR_MAGENTA}, + {"brightmagenta", COLOR_MAGENTA + COLOR_INTENSITY}, + {"cyan", COLOR_CYAN}, + {"brightcyan", COLOR_CYAN + COLOR_INTENSITY}, + {"lightgray", COLOR_WHITE}, + {"white", COLOR_WHITE + COLOR_INTENSITY}, + {"default", -1}, /* default color of the terminal */ + /* special colors */ + {"A_REVERSE", SPEC_A_REVERSE}, + {"A_BOLD", SPEC_A_BOLD}, + {"A_BOLD_REVERSE", SPEC_A_BOLD_REVERSE}, + {"A_UNDERLINE", SPEC_A_UNDERLINE}, + /* End of list */ + {NULL, 0} +}; + +static mc_tty_color_table_t const attributes_table[] = { + {"bold", A_BOLD}, +#ifdef A_ITALIC /* available since ncurses-5.9-20130831 / slang-pre2.3.0-107 */ + {"italic", A_ITALIC}, +#endif /* A_ITALIC */ + {"underline", A_UNDERLINE}, + {"reverse", A_REVERSE}, + {"blink", A_BLINK}, + /* End of list */ + {NULL, 0} +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static inline int +parse_hex_digit (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + c |= 0x20; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +parse_256_or_true_color_name (const char *color_name) +{ + int i; + char dummy; + + /* cppcheck-suppress invalidscanf */ + if (sscanf (color_name, "color%d%c", &i, &dummy) == 1 && i >= 0 && i < 256) + { + return i; + } + /* cppcheck-suppress invalidscanf */ + if (sscanf (color_name, "gray%d%c", &i, &dummy) == 1 && i >= 0 && i < 24) + { + return 232 + i; + } + if (strncmp (color_name, "rgb", 3) == 0 && + color_name[3] >= '0' && color_name[3] < '6' && + color_name[4] >= '0' && color_name[4] < '6' && + color_name[5] >= '0' && color_name[5] < '6' && color_name[6] == '\0') + { + return 16 + 36 * (color_name[3] - '0') + 6 * (color_name[4] - '0') + (color_name[5] - '0'); + } + if (color_name[0] == '#') + { + int len; + + color_name++; + len = (int) strlen (color_name); + if (len == 3 || len == 6) + { + int h[6]; + + for (i = 0; i < len; i++) + { + h[i] = parse_hex_digit (color_name[i]); + if (h[i] == -1) + return -1; + } + + if (i == 3) + i = (h[0] << 20) | (h[0] << 16) | (h[1] << 12) | (h[1] << 8) | (h[2] << 4) | h[2]; + else + i = (h[0] << 20) | (h[1] << 16) | (h[2] << 12) | (h[3] << 8) | (h[4] << 4) | h[5]; + return (1 << 24) | i; + } + } + + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +const char * +tty_color_get_name_by_index (int idx) +{ + int i; + + /* Find the real English name of the first 16 colors, */ + /* as well as the A_* special values. */ + for (i = 0; color_table[i].name != NULL; i++) + if (idx == color_table[i].value) + return color_table[i].name; + + /* Create and return the strings in "colorNNN" or "#rrggbb" format. */ + if ((idx >= 16 && idx < 256) || (idx & (1 << 24)) != 0) + { + char name[9]; + + if (idx < 256) + sprintf (name, "color%d", idx); + else + sprintf (name, "#%06X", (unsigned int) idx & 0xFFFFFF); + return g_intern_string (name); + } + return "default"; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_color_get_index_by_name (const char *color_name) +{ + if (color_name != NULL) + { + size_t i; + + for (i = 0; color_table[i].name != NULL; i++) + if (strcmp (color_name, color_table[i].name) == 0) + return color_table[i].value; + return parse_256_or_true_color_name (color_name); + } + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_attr_get_bits (const char *attrs) +{ + int attr_bits = 0; + + if (attrs != NULL) + { + gchar **attr_list; + int i; + + attr_list = g_strsplit (attrs, "+", -1); + + for (i = 0; attr_list[i] != NULL; i++) + { + int j; + + for (j = 0; attributes_table[j].name != NULL; j++) + { + if (strcmp (attr_list[i], attributes_table[j].name) == 0) + { + attr_bits |= attributes_table[j].value; + break; + } + } + } + g_strfreev (attr_list); + } + return attr_bits; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/color-internal.h b/lib/tty/color-internal.h new file mode 100644 index 0000000..dc85225 --- /dev/null +++ b/lib/tty/color-internal.h @@ -0,0 +1,61 @@ + +/** \file color-internal.h + * \brief Header: Internal stuff of color setup + */ + +#ifndef MC__COLOR_INTERNAL_H +#define MC__COLOR_INTERNAL_H + +#include <sys/types.h> /* size_t */ + +#include "lib/global.h" + +#ifdef HAVE_SLANG +#include "tty-slang.h" +#else +#include "tty-ncurses.h" +#endif /* HAVE_SLANG */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/* *INDENT-OFF* */ +typedef enum { + SPEC_A_REVERSE = -100, + SPEC_A_BOLD = -101, + SPEC_A_BOLD_REVERSE = -102, + SPEC_A_UNDERLINE = -103 +} tty_special_color_t; +/* *INDENT-ON* */ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +typedef struct mc_color_pair_struct +{ + int ifg; + int ibg; + int attr; + size_t pair_index; + gboolean is_temp; +} tty_color_pair_t; + +/*** global variables defined in .c file *********************************************************/ + +extern gboolean use_colors; +extern gboolean mc_tty_color_disable; + +/*** declarations of public functions ************************************************************/ + +const char *tty_color_get_name_by_index (int idx); +int tty_color_get_index_by_name (const char *color_name); +int tty_attr_get_bits (const char *attrs); + +void tty_color_init_lib (gboolean disable, gboolean force); +void tty_color_deinit_lib (void); + +void tty_color_try_alloc_pair_lib (tty_color_pair_t * mc_color_pair); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__COLOR_INTERNAL_H */ diff --git a/lib/tty/color-ncurses.c b/lib/tty/color-ncurses.c new file mode 100644 index 0000000..f01d697 --- /dev/null +++ b/lib/tty/color-ncurses.c @@ -0,0 +1,251 @@ +/* + Color setup for NCurses screen library + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009 + Slava Zanko <slavazanko@gmail.com>, 2010 + Egmont Koblinger <egmont@gmail.com>, 2010 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file color-ncurses.c + * \brief Source: NCUrses-specific color setup + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> /* size_t */ + +#include "lib/global.h" + +#include "tty-ncurses.h" +#include "color.h" /* variables */ +#include "color-internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static GHashTable *mc_tty_color_color_pair_attrs = NULL; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static inline void +mc_tty_color_attr_destroy_cb (gpointer data) +{ + g_free (data); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +mc_tty_color_save_attr (int color_pair, int color_attr) +{ + int *attr, *key; + + attr = g_try_new0 (int, 1); + if (attr == NULL) + return; + + key = g_try_new (int, 1); + if (key == NULL) + { + g_free (attr); + return; + } + + *key = color_pair; + *attr = color_attr; + + g_hash_table_replace (mc_tty_color_color_pair_attrs, (gpointer) key, (gpointer) attr); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +color_get_attr (int color_pair) +{ + int *fnd = NULL; + + if (mc_tty_color_color_pair_attrs != NULL) + fnd = (int *) g_hash_table_lookup (mc_tty_color_color_pair_attrs, (gpointer) & color_pair); + return (fnd != NULL) ? *fnd : 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +mc_tty_color_pair_init_special (tty_color_pair_t * mc_color_pair, + int fg1, int bg1, int fg2, int bg2, int attr) +{ + if (has_colors () && !mc_tty_color_disable) + init_pair (mc_color_pair->pair_index, fg1, bg1); + else + init_pair (mc_color_pair->pair_index, fg2, bg2); + mc_tty_color_save_attr (mc_color_pair->pair_index, attr); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_init_lib (gboolean disable, gboolean force) +{ + (void) force; + + if (has_colors () && !disable) + { + use_colors = TRUE; + start_color (); + use_default_colors (); + } + + mc_tty_color_color_pair_attrs = g_hash_table_new_full + (g_int_hash, g_int_equal, mc_tty_color_attr_destroy_cb, mc_tty_color_attr_destroy_cb); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_deinit_lib (void) +{ + g_hash_table_destroy (mc_tty_color_color_pair_attrs); + mc_tty_color_color_pair_attrs = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_try_alloc_pair_lib (tty_color_pair_t * mc_color_pair) +{ + if (mc_color_pair->ifg <= (int) SPEC_A_REVERSE) + { + switch (mc_color_pair->ifg) + { + case SPEC_A_REVERSE: + mc_tty_color_pair_init_special (mc_color_pair, + COLOR_BLACK, COLOR_WHITE, + COLOR_BLACK, COLOR_WHITE | A_BOLD, A_REVERSE); + break; + case SPEC_A_BOLD: + mc_tty_color_pair_init_special (mc_color_pair, + COLOR_WHITE, COLOR_BLACK, + COLOR_WHITE, COLOR_BLACK, A_BOLD); + break; + case SPEC_A_BOLD_REVERSE: + mc_tty_color_pair_init_special (mc_color_pair, + COLOR_WHITE, COLOR_WHITE, + COLOR_WHITE, COLOR_WHITE, A_BOLD | A_REVERSE); + break; + case SPEC_A_UNDERLINE: + mc_tty_color_pair_init_special (mc_color_pair, + COLOR_WHITE, COLOR_BLACK, + COLOR_WHITE, COLOR_BLACK, A_UNDERLINE); + break; + default: + break; + } + } + else + { + int ifg, ibg, attr; + + ifg = mc_color_pair->ifg; + ibg = mc_color_pair->ibg; + attr = mc_color_pair->attr; + + /* In legacy color mode, change bright colors into bold */ + if (!tty_use_256colors (NULL) && !tty_use_truecolors (NULL)) + { + if (ifg >= 8 && ifg < 16) + { + ifg &= 0x07; + attr |= A_BOLD; + } + + if (ibg >= 8 && ibg < 16) + { + ibg &= 0x07; + /* attr | = A_BOLD | A_REVERSE ; */ + } + } + + init_pair (mc_color_pair->pair_index, ifg, ibg); + mc_tty_color_save_attr (mc_color_pair->pair_index, attr); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_setcolor (int color) +{ + attrset (COLOR_PAIR (color) | color_get_attr (color)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_lowlevel_setcolor (int color) +{ + tty_setcolor (color); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_set_normal_attrs (void) +{ + standend (); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_use_256colors (GError ** error) +{ + (void) error; + + return (COLORS == 256); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_use_truecolors (GError ** error) +{ + /* Not yet supported in ncurses */ + g_set_error (error, MC_ERROR, -1, _("True color not supported with ncurses.")); + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/color-slang.c b/lib/tty/color-slang.c new file mode 100644 index 0000000..5dd2663 --- /dev/null +++ b/lib/tty/color-slang.c @@ -0,0 +1,260 @@ +/* + Color setup for S_Lang screen library + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009 + Egmont Koblinger <egmont@gmail.com>, 2010 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file color-slang.c + * \brief Source: S-Lang-specific color setup + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> /* size_t */ + +#include "lib/global.h" +#include "lib/util.h" /* whitespace() */ + +#include "tty-slang.h" +#include "color.h" /* variables */ +#include "color-internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +has_colors (gboolean disable, gboolean force) +{ + mc_tty_color_disable = disable; + + if (force || (getenv ("COLORTERM") != NULL)) + SLtt_Use_Ansi_Colors = 1; + + if (!mc_tty_color_disable) + { + const char *terminal = getenv ("TERM"); + const size_t len = strlen (terminal); + char *cts = mc_global.tty.color_terminal_string; + + /* check mc_global.tty.color_terminal_string */ + while (*cts != '\0') + { + char *s; + size_t i = 0; + + while (whitespace (*cts)) + cts++; + s = cts; + + while (*cts != '\0' && *cts != ',') + { + cts++; + i++; + } + + if ((i != 0) && (i == len) && (strncmp (s, terminal, i) == 0)) + SLtt_Use_Ansi_Colors = 1; + + if (*cts == ',') + cts++; + } + } + return SLtt_Use_Ansi_Colors; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +mc_tty_color_pair_init_special (tty_color_pair_t * mc_color_pair, + const char *fg1, const char *bg1, + const char *fg2, const char *bg2, SLtt_Char_Type mask) +{ + if (SLtt_Use_Ansi_Colors != 0) + { + if (!mc_tty_color_disable) + { + SLtt_set_color (mc_color_pair->pair_index, (char *) "", (char *) fg1, (char *) bg1); + } + else + { + SLtt_set_color (mc_color_pair->pair_index, (char *) "", (char *) fg2, (char *) bg2); + } + } + else + { + SLtt_set_mono (mc_color_pair->pair_index, NULL, mask); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_init_lib (gboolean disable, gboolean force) +{ + /* FIXME: if S-Lang is used, has_colors() must be called regardless + of whether we are interested in its result */ + if (has_colors (disable, force) && !disable) + { + use_colors = TRUE; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_deinit_lib (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_try_alloc_pair_lib (tty_color_pair_t * mc_color_pair) +{ + if (mc_color_pair->ifg <= (int) SPEC_A_REVERSE) + { + switch (mc_color_pair->ifg) + { + case SPEC_A_REVERSE: + mc_tty_color_pair_init_special (mc_color_pair, + "black", "white", "black", "lightgray", SLTT_REV_MASK); + break; + case SPEC_A_BOLD: + mc_tty_color_pair_init_special (mc_color_pair, + "white", "black", "white", "black", SLTT_BOLD_MASK); + break; + case SPEC_A_BOLD_REVERSE: + mc_tty_color_pair_init_special (mc_color_pair, + "white", "white", + "white", "white", SLTT_BOLD_MASK | SLTT_REV_MASK); + break; + case SPEC_A_UNDERLINE: + mc_tty_color_pair_init_special (mc_color_pair, + "white", "black", "white", "black", SLTT_ULINE_MASK); + break; + default: + break; + } + } + else + { + const char *fg, *bg; + + fg = tty_color_get_name_by_index (mc_color_pair->ifg); + bg = tty_color_get_name_by_index (mc_color_pair->ibg); + SLtt_set_color (mc_color_pair->pair_index, (char *) "", (char *) fg, (char *) bg); + SLtt_add_color_attribute (mc_color_pair->pair_index, mc_color_pair->attr); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_setcolor (int color) +{ + SLsmg_set_color (color); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Set colorpair by index, don't interpret S-Lang "emulated attributes" + */ + +void +tty_lowlevel_setcolor (int color) +{ + SLsmg_set_color (color & 0x7F); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_set_normal_attrs (void) +{ + SLsmg_normal_video (); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_use_256colors (GError ** error) +{ + gboolean ret; + + ret = (SLtt_Use_Ansi_Colors && SLtt_tgetnum ((char *) "Co") == 256); + + if (!ret) + g_set_error (error, MC_ERROR, -1, + _("Your terminal doesn't even seem to support 256 colors.")); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_use_truecolors (GError ** error) +{ + char *colorterm; + + /* True color is supported since slang-2.3.1 on 64-bit machines, + and expected to be supported from slang-3 on 32-bit machines: + http://lists.jedsoft.org/lists/slang-users/2016/0000014.html. + Check for sizeof (long) being 8, exactly as slang does. */ + if (SLang_Version < 20301 || (sizeof (long) != 8 && SLang_Version < 30000)) + { + g_set_error (error, MC_ERROR, -1, _("True color not supported in this slang version.")); + return FALSE; + } + + /* Duplicate slang's check so that we can pop up an error message + rather than silently use wrong colors. */ + colorterm = getenv ("COLORTERM"); + if (colorterm == NULL + || (strcmp (colorterm, "truecolor") != 0 && strcmp (colorterm, "24bit") != 0)) + { + g_set_error (error, MC_ERROR, -1, + _("Set COLORTERM=truecolor if your terminal really supports true colors.")); + return FALSE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/color-slang.h b/lib/tty/color-slang.h new file mode 100644 index 0000000..a1a8d55 --- /dev/null +++ b/lib/tty/color-slang.h @@ -0,0 +1,56 @@ + +/** \file color-slang.h + * \brief Header: S-Lang-specific color setup + */ + +#ifndef MC__COLOR_SLANG_H +#define MC__COLOR_SLANG_H + +#include "tty-slang.h" /* S-Lang headers */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/* When using Slang with color, we have all the indexes free but + * those defined here (A_BOLD, A_ITALIC, A_UNDERLINE, A_REVERSE, A_BLINK) + */ + +#ifndef A_BOLD +#define A_BOLD SLTT_BOLD_MASK +#endif /* A_BOLD */ +#ifdef SLTT_ITALIC_MASK /* available since slang-pre2.3.0-107 */ +#ifndef A_ITALIC +#define A_ITALIC SLTT_ITALIC_MASK +#endif /* A_ITALIC */ +#endif /* SLTT_ITALIC_MASK */ +#ifndef A_UNDERLINE +#define A_UNDERLINE SLTT_ULINE_MASK +#endif /* A_UNDERLINE */ +#ifndef A_REVERSE +#define A_REVERSE SLTT_REV_MASK +#endif /* A_REVERSE */ +#ifndef A_BLINK +#define A_BLINK SLTT_BLINK_MASK +#endif /* A_BLINK */ + +/*** enums ***************************************************************************************/ + +enum +{ + COLOR_BLACK = 0, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, + COLOR_MAGENTA, + COLOR_CYAN, + COLOR_WHITE +}; + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +/*** inline functions ****************************************************************************/ +#endif /* MC_COLOR_SLANG_H */ diff --git a/lib/tty/color.c b/lib/tty/color.c new file mode 100644 index 0000000..c79e13a --- /dev/null +++ b/lib/tty/color.c @@ -0,0 +1,244 @@ +/* + Color setup. + Interface functions. + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009 + Slava Zanko <slavazanko@gmail.com>, 2009 + Egmont Koblinger <egmont@gmail.com>, 2010 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file color.c + * \brief Source: color setup + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> /* size_t */ + +#include "lib/global.h" + +#include "tty.h" +#include "color.h" + +#include "color-internal.h" + +/*** global variables ****************************************************************************/ + +static char *tty_color_defaults__fg = NULL; +static char *tty_color_defaults__bg = NULL; +static char *tty_color_defaults__attrs = NULL; + +/* Set if we are actually using colors */ +gboolean use_colors = FALSE; + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static GHashTable *mc_tty_color__hashtable = NULL; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +tty_color_free_condition_cb (gpointer key, gpointer value, gpointer user_data) +{ + tty_color_pair_t *mc_color_pair = (tty_color_pair_t *) value; + gboolean is_temp_color; + + (void) key; + + is_temp_color = user_data != NULL; + return (mc_color_pair->is_temp == is_temp_color); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tty_color_free_all (gboolean is_temp_color) +{ + g_hash_table_foreach_remove (mc_tty_color__hashtable, tty_color_free_condition_cb, + is_temp_color ? GSIZE_TO_POINTER (1) : NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +tty_color_get_next_cpn_cb (gpointer key, gpointer value, gpointer user_data) +{ + tty_color_pair_t *mc_color_pair = (tty_color_pair_t *) value; + size_t cp = GPOINTER_TO_SIZE (user_data); + + (void) key; + + return (cp == mc_color_pair->pair_index); +} + +/* --------------------------------------------------------------------------------------------- */ + +static size_t +tty_color_get_next__color_pair_number (void) +{ + size_t cp_count, cp; + + cp_count = g_hash_table_size (mc_tty_color__hashtable); + for (cp = 0; cp < cp_count; cp++) + if (g_hash_table_find (mc_tty_color__hashtable, tty_color_get_next_cpn_cb, + GSIZE_TO_POINTER (cp)) == NULL) + break; + + return cp; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +tty_init_colors (gboolean disable, gboolean force) +{ + tty_color_init_lib (disable, force); + mc_tty_color__hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_colors_done (void) +{ + tty_color_deinit_lib (); + g_free (tty_color_defaults__fg); + g_free (tty_color_defaults__bg); + g_free (tty_color_defaults__attrs); + + g_hash_table_destroy (mc_tty_color__hashtable); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_use_colors (void) +{ + return use_colors; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_try_alloc_color_pair2 (const char *fg, const char *bg, const char *attrs, + gboolean is_temp_color) +{ + gchar *color_pair; + tty_color_pair_t *mc_color_pair; + int ifg, ibg, attr; + + if (fg == NULL || strcmp (fg, "base") == 0) + fg = tty_color_defaults__fg; + if (bg == NULL || strcmp (bg, "base") == 0) + bg = tty_color_defaults__bg; + if (attrs == NULL || strcmp (attrs, "base") == 0) + attrs = tty_color_defaults__attrs; + + ifg = tty_color_get_index_by_name (fg); + ibg = tty_color_get_index_by_name (bg); + attr = tty_attr_get_bits (attrs); + + color_pair = g_strdup_printf ("%d.%d.%d", ifg, ibg, attr); + if (color_pair == NULL) + return 0; + + mc_color_pair = + (tty_color_pair_t *) g_hash_table_lookup (mc_tty_color__hashtable, (gpointer) color_pair); + + if (mc_color_pair != NULL) + { + g_free (color_pair); + return mc_color_pair->pair_index; + } + + mc_color_pair = g_try_new0 (tty_color_pair_t, 1); + if (mc_color_pair == NULL) + { + g_free (color_pair); + return 0; + } + + mc_color_pair->is_temp = is_temp_color; + mc_color_pair->ifg = ifg; + mc_color_pair->ibg = ibg; + mc_color_pair->attr = attr; + mc_color_pair->pair_index = tty_color_get_next__color_pair_number (); + + tty_color_try_alloc_pair_lib (mc_color_pair); + + g_hash_table_insert (mc_tty_color__hashtable, (gpointer) color_pair, (gpointer) mc_color_pair); + + return mc_color_pair->pair_index; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs) +{ + return tty_try_alloc_color_pair2 (fg, bg, attrs, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_free_all_tmp (void) +{ + tty_color_free_all (TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_free_all_non_tmp (void) +{ + tty_color_free_all (FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_color_set_defaults (const char *fgcolor, const char *bgcolor, const char *attrs) +{ + g_free (tty_color_defaults__fg); + g_free (tty_color_defaults__bg); + g_free (tty_color_defaults__attrs); + + tty_color_defaults__fg = g_strdup (fgcolor); + tty_color_defaults__bg = g_strdup (bgcolor); + tty_color_defaults__attrs = g_strdup (attrs); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/color.h b/lib/tty/color.h new file mode 100644 index 0000000..583cce3 --- /dev/null +++ b/lib/tty/color.h @@ -0,0 +1,54 @@ +/** \file color.h + * \brief Header: color setup + * + * PLEASE FORGOT ABOUT tty/color.h! + * Use skin engine for getting needed color pairs. + * + * edit/syntax.c may use this file directly, I'm agree. :) + * + */ + +#ifndef MC__COLOR_H +#define MC__COLOR_H + +#include "lib/global.h" /* glib.h */ + +#ifdef HAVE_SLANG +#include "color-slang.h" +#else +#include "tty-ncurses.h" +#endif + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +void tty_init_colors (gboolean disable, gboolean force); +void tty_colors_done (void); + +gboolean tty_use_colors (void); +int tty_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs); +int tty_try_alloc_color_pair2 (const char *fg, const char *bg, const char *attrs, + gboolean is_temp_color); + +void tty_color_free_all_tmp (void); +void tty_color_free_all_non_tmp (void); + +void tty_setcolor (int color); +void tty_lowlevel_setcolor (int color); +void tty_set_normal_attrs (void); + +void tty_color_set_defaults (const char *fgcolor, const char *bgcolor, const char *attrs); + +extern gboolean tty_use_256colors (GError ** error); +extern gboolean tty_use_truecolors (GError ** error); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__COLOR_H */ diff --git a/lib/tty/key.c b/lib/tty/key.c new file mode 100644 index 0000000..5671666 --- /dev/null +++ b/lib/tty/key.c @@ -0,0 +1,2252 @@ +/* + Keyboard support routines. + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Miguel de Icaza, 1994, 1995 + Janne Kukonlehto, 1994, 1995 + Jakub Jelinek, 1995 + Norbert Warmuth, 1997 + Denys Vlasenko <vda.linux@googlemail.com>, 2013 + Slava Zanko <slavazanko@gmail.com>, 2013 + Egmont Koblinger <egmont@gmail.com>, 2013 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file key.c + * \brief Source: keyboard support routines + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "lib/global.h" + +#include "lib/vfs/vfs.h" + +#include "tty.h" +#include "tty-internal.h" /* mouse_enabled */ +#include "mouse.h" +#include "key.h" + +#include "lib/widget.h" /* mc_refresh() */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +#include "x11conn.h" +#endif + +#ifdef __linux__ +#if defined(__GLIBC__) && (__GLIBC__ < 2) +#include <linux/termios.h> /* TIOCLINUX */ +#else +#include <termios.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#endif /* __linux__ */ + +#ifdef __CYGWIN__ +#include <termios.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#endif /* __CYGWIN__ */ + +#ifdef __QNXNTO__ +#include <dlfcn.h> +#include <Ph.h> +#include <sys/dcmd_chr.h> +#endif /* __QNXNTO__ */ + +/*** global variables ****************************************************************************/ + +int mou_auto_repeat = 100; /* ms */ +int double_click_speed = 250; /* ms */ +gboolean old_esc_mode = TRUE; +/* timeout for old_esc_mode in usec */ +int old_esc_mode_timeout = G_USEC_PER_SEC; /* us, settable via env */ +gboolean use_8th_bit_as_meta = FALSE; + +gboolean bracketed_pasting_in_progress = FALSE; + +/* This table is a mapping between names and the constants we use + * We use this to allow users to define alternate definitions for + * certain keys that may be missing from the terminal database + */ +const key_code_name_t key_name_conv_tab[] = { + {ESC_CHAR, "escape", N_("Escape"), "Esc"}, + /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason + to define f0 as well. Also, it makes Learn keys a bunch of problems :( */ + {KEY_F (1), "f1", N_("Function key 1"), "F1"}, + {KEY_F (2), "f2", N_("Function key 2"), "F2"}, + {KEY_F (3), "f3", N_("Function key 3"), "F3"}, + {KEY_F (4), "f4", N_("Function key 4"), "F4"}, + {KEY_F (5), "f5", N_("Function key 5"), "F5"}, + {KEY_F (6), "f6", N_("Function key 6"), "F6"}, + {KEY_F (7), "f7", N_("Function key 7"), "F7"}, + {KEY_F (8), "f8", N_("Function key 8"), "F8"}, + {KEY_F (9), "f9", N_("Function key 9"), "F9"}, + {KEY_F (10), "f10", N_("Function key 10"), "F10"}, + {KEY_F (11), "f11", N_("Function key 11"), "F11"}, + {KEY_F (12), "f12", N_("Function key 12"), "F12"}, + {KEY_F (13), "f13", N_("Function key 13"), "F13"}, + {KEY_F (14), "f14", N_("Function key 14"), "F14"}, + {KEY_F (15), "f15", N_("Function key 15"), "F15"}, + {KEY_F (16), "f16", N_("Function key 16"), "F16"}, + {KEY_F (17), "f17", N_("Function key 17"), "F17"}, + {KEY_F (18), "f18", N_("Function key 18"), "F18"}, + {KEY_F (19), "f19", N_("Function key 19"), "F19"}, + {KEY_F (20), "f20", N_("Function key 20"), "F20"}, + {ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab"}, + {KEY_BTAB, "backtab", N_("BackTab/S-tab"), "Shift-Tab"}, + {KEY_BACKSPACE, "backspace", N_("Backspace"), "Backspace"}, + {KEY_UP, "up", N_("Up arrow"), "Up"}, + {KEY_DOWN, "down", N_("Down arrow"), "Down"}, + {KEY_LEFT, "left", N_("Left arrow"), "Left"}, + {KEY_RIGHT, "right", N_("Right arrow"), "Right"}, + {KEY_IC, "insert", N_("Insert"), "Ins"}, + {KEY_DC, "delete", N_("Delete"), "Del"}, + {KEY_HOME, "home", N_("Home"), "Home"}, + {KEY_END, "end", N_("End key"), "End"}, + {KEY_PPAGE, "pgup", N_("Page Up"), "PgUp"}, + {KEY_NPAGE, "pgdn", N_("Page Down"), "PgDn"}, + {(int) '/', "kpslash", N_("/ on keypad"), "/"}, + {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*"}, + {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-"}, + {KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+"}, + + /* From here on, these won't be shown in Learn keys (no space) */ + {KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left"}, + {KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right"}, + {KEY_UP, "kpup", N_("Up arrow keypad"), "Up"}, + {KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down"}, + {KEY_HOME, "kphome", N_("Home on keypad"), "Home"}, + {KEY_END, "kpend", N_("End on keypad"), "End"}, + {KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn"}, + {KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp"}, + {KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins"}, + {KEY_DC, "kpdelete", N_("Delete on keypad"), "Del"}, + {(int) '\n', "kpenter", N_("Enter on keypad"), "Enter"}, + {KEY_F (21), "f21", N_("Function key 21"), "F21"}, + {KEY_F (22), "f22", N_("Function key 22"), "F22"}, + {KEY_F (23), "f23", N_("Function key 23"), "F23"}, + {KEY_F (24), "f24", N_("Function key 24"), "F24"}, + {KEY_A1, "a1", N_("A1 key"), "A1"}, + {KEY_C1, "c1", N_("C1 key"), "C1"}, + + /* Alternative label */ + {ESC_CHAR, "esc", N_("Escape"), "Esc"}, + {KEY_BACKSPACE, "bs", N_("Backspace"), "Bakspace"}, + {KEY_IC, "ins", N_("Insert"), "Ins"}, + {KEY_DC, "del", N_("Delete"), "Del"}, + {(int) '*', "asterisk", N_("Asterisk"), "*"}, + {(int) '-', "minus", N_("Minus"), "-"}, + {(int) '+', "plus", N_("Plus"), "+"}, + {(int) '.', "dot", N_("Dot"), "."}, + {(int) '<', "lt", N_("Less than"), "<"}, + {(int) '>', "gt", N_("Great than"), ">"}, + {(int) '=', "equal", N_("Equal"), "="}, + {(int) ',', "comma", N_("Comma"), ","}, + {(int) '\'', "apostrophe", N_("Apostrophe"), "\'"}, + {(int) ':', "colon", N_("Colon"), ":"}, + {(int) ';', "semicolon", N_("Semicolon"), ";"}, + {(int) '!', "exclamation", N_("Exclamation mark"), "!"}, + {(int) '?', "question", N_("Question mark"), "?"}, + {(int) '&', "ampersand", N_("Ampersand"), "&"}, + {(int) '$', "dollar", N_("Dollar sign"), "$"}, + {(int) '"', "quota", N_("Quotation mark"), "\""}, + {(int) '%', "percent", N_("Percent sign"), "%"}, + {(int) '^', "caret", N_("Caret"), "^"}, + {(int) '~', "tilda", N_("Tilda"), "~"}, + {(int) '`', "prime", N_("Prime"), "`"}, + {(int) '_', "underline", N_("Underline"), "_"}, + {(int) '_', "understrike", N_("Understrike"), "_"}, + {(int) '|', "pipe", N_("Pipe"), "|"}, + {(int) '(', "lparenthesis", N_("Left parenthesis"), "("}, + {(int) ')', "rparenthesis", N_("Right parenthesis"), ")"}, + {(int) '[', "lbracket", N_("Left bracket"), "["}, + {(int) ']', "rbracket", N_("Right bracket"), "]"}, + {(int) '{', "lbrace", N_("Left brace"), "{"}, + {(int) '}', "rbrace", N_("Right brace"), "}"}, + {(int) '\n', "enter", N_("Enter"), "Enter"}, + {(int) '\t', "tab", N_("Tab key"), "Tab"}, + {(int) ' ', "space", N_("Space key"), "Space"}, + {(int) '/', "slash", N_("Slash key"), "/"}, + {(int) '\\', "backslash", N_("Backslash key"), "\\"}, + {(int) '#', "number", N_("Number sign #"), "#"}, + {(int) '#', "hash", N_("Number sign #"), "#"}, + /* TRANSLATORS: Please translate as in "at sign" (@). */ + {(int) '@', "at", N_("At sign"), "@"}, + + /* meta keys */ + {KEY_M_CTRL, "control", N_("Ctrl"), "C"}, + {KEY_M_CTRL, "ctrl", N_("Ctrl"), "C"}, + {KEY_M_ALT, "meta", N_("Alt"), "M"}, + {KEY_M_ALT, "alt", N_("Alt"), "M"}, + {KEY_M_ALT, "ralt", N_("Alt"), "M"}, + {KEY_M_SHIFT, "shift", N_("Shift"), "S"}, + + {0, NULL, NULL, NULL} +}; + +/*** file scope macro definitions ****************************************************************/ + +#define MC_USEC_PER_MSEC 1000 + +/* The maximum sequence length (32 + null terminator) */ +#define SEQ_BUFFER_LEN 33 + +/*** file scope type declarations ****************************************************************/ + +/* Linux console keyboard modifiers */ +typedef enum +{ + SHIFT_PRESSED = (1 << 0), + ALTR_PRESSED = (1 << 1), + CONTROL_PRESSED = (1 << 2), + ALTL_PRESSED = (1 << 3) +} mod_pressed_t; + +typedef struct key_def +{ + char ch; /* Holds the matching char code */ + int code; /* The code returned, valid if child == NULL */ + struct key_def *next; + struct key_def *child; /* sequence continuation */ + int action; /* optional action to be done. Now used only + to mark that we are just after the first + Escape */ +} key_def; + +typedef struct +{ + int code; + const char *seq; + int action; +} key_define_t; + +/* File descriptor monitoring add/remove routines */ +typedef struct +{ + int fd; + select_fn callback; + void *info; +} select_t; + +typedef enum KeySortType +{ + KEY_NOSORT = 0, + KEY_SORTBYNAME, + KEY_SORTBYCODE +} KeySortType; + +#ifdef __QNXNTO__ +typedef int (*ph_dv_f) (void *, void *); +typedef int (*ph_ov_f) (void *); +typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *); +#endif + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static key_define_t mc_default_keys[] = { + {ESC_CHAR, ESC_STR, MCKEY_ESCAPE}, + {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION}, + {MCKEY_BRACKETED_PASTING_START, ESC_STR "[200~", MCKEY_NOACTION}, + {MCKEY_BRACKETED_PASTING_END, ESC_STR "[201~", MCKEY_NOACTION}, + {0, NULL, MCKEY_NOACTION}, +}; + +/* Broken terminfo and termcap databases on xterminals */ +static key_define_t xterm_key_defines[] = { + {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION}, + {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION}, + {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION}, + {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION}, + {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION}, + {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION}, + {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION}, + {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION}, + {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION}, + {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION}, + {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION}, + {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION}, + {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION}, + {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION}, + + /* old xterm Shift-arrows */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION}, + + /* new xterm Shift-arrows */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION}, + + /* more xterm keys with modifiers */ + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION}, + {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, + + /* putty */ + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION}, + + /* putty alt-arrow keys */ + /* removed as source esc esc esc trouble */ + /* + { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION }, + + { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION }, + + { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION }, + */ + /* xterm alt-arrow keys */ + {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION}, + + {KEY_M_SHIFT | KEY_M_ALT | KEY_UP, ESC_STR "[1;4A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;4B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;4C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;4D", MCKEY_NOACTION}, + + /* rxvt keys with modifiers */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION}, + + /* konsole keys with modifiers */ + {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION}, + + /* gnome-terminal */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION}, + + /* gnome-terminal - application mode */ + {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION}, + + /* iTerm */ + {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION}, + + /* putty */ + {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION}, + + /* keypad keys */ + {KEY_IC, ESC_STR "Op", MCKEY_NOACTION}, + {KEY_DC, ESC_STR "On", MCKEY_NOACTION}, + {'/', ESC_STR "Oo", MCKEY_NOACTION}, + {'\n', ESC_STR "OM", MCKEY_NOACTION}, + + {0, NULL, MCKEY_NOACTION}, +}; + +/* qansi-m terminals have a much more key combinations, + which are undefined in termcap/terminfo */ +static key_define_t qansi_key_defines[] = { + /* qansi-m terminal */ + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */ + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */ + {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */ + {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */ + {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */ + {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */ + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */ + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */ + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */ + {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */ + {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */ + {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */ + {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */ + {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */ + {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */ + {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */ + {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */ + {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */ + {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */ + {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */ + {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */ + {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */ + {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */ + {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */ + {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */ + {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */ + {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */ + {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */ + {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */ + {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */ + {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */ + {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */ + {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */ + {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */ + {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */ + {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */ + {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */ + {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */ + {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */ + {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */ + {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */ + {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */ + {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */ + {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */ + {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */ + {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */ + {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */ + {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */ + {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */ + {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */ + {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */ + {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */ + {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */ + {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */ + {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */ + {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */ + {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */ + {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */ + {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */ + {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */ + {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */ + {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */ + {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */ + {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */ + {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */ + {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */ + {0, NULL, MCKEY_NOACTION}, +}; + +/* This holds all the key definitions */ +static key_def *keys = NULL; + +static int input_fd; +static int disabled_channels = 0; /* Disable channels checking */ + +static GSList *select_list = NULL; + +static int seq_buffer[SEQ_BUFFER_LEN]; +static int *seq_append = NULL; + +static int *pending_keys = NULL; + +#ifdef __QNXNTO__ +ph_dv_f ph_attach; +ph_ov_f ph_input_group; +ph_pqc_f ph_query_cursor; +#endif + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +static Display *x11_display; +static Window x11_window; +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +static KeySortType has_been_sorted = KEY_NOSORT; + +/* *INDENT-OFF* */ +static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1; +/* *INDENT-ON* */ + +static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1]; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +select_cmp_by_fd_set (gconstpointer a, gconstpointer b) +{ + const select_t *s = (const select_t *) a; + const fd_set *f = (const fd_set *) b; + + return (FD_ISSET (s->fd, f) ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +select_cmp_by_fd (gconstpointer a, gconstpointer b) +{ + const select_t *s = (const select_t *) a; + const int fd = GPOINTER_TO_INT (b); + + return (s->fd == fd ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +add_selects (fd_set * select_set) +{ + int top_fd = 0; + + if (disabled_channels == 0) + { + GSList *s; + + for (s = select_list; s != NULL; s = g_slist_next (s)) + { + select_t *p = (select_t *) s->data; + + FD_SET (p->fd, select_set); + if (p->fd > top_fd) + top_fd = p->fd; + } + } + + return top_fd; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +check_selects (fd_set * select_set) +{ + while (disabled_channels == 0) + { + GSList *s; + select_t *p; + + s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set); + if (s == NULL) + break; + + p = (select_t *) s->data; + FD_CLR (p->fd, select_set); + p->callback (p->fd, p->info); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/* If set timeout is set, then we wait 0.1 seconds, else, we block */ + +static void +try_channels (gboolean set_timeout) +{ + struct timeval time_out; + static fd_set select_set; + + while (TRUE) + { + struct timeval *timeptr = NULL; + int maxfdp, v; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); /* Add stdin */ + maxfdp = MAX (add_selects (&select_set), input_fd); + + if (set_timeout) + { + time_out.tv_sec = 0; + time_out.tv_usec = 100 * MC_USEC_PER_MSEC; + timeptr = &time_out; + } + + v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr); + if (v > 0) + { + check_selects (&select_set); + if (FD_ISSET (input_fd, &select_set)) + break; + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static key_def * +create_sequence (const char *seq, int code, int action) +{ + key_def *base, *p, *attach; + + for (base = attach = NULL; *seq != '\0'; seq++) + { + p = g_new (key_def, 1); + if (base == NULL) + base = p; + if (attach != NULL) + attach->child = p; + + p->ch = *seq; + p->code = code; + p->child = p->next = NULL; + if (seq[1] == '\0') + p->action = action; + else + p->action = MCKEY_NOACTION; + attach = p; + } + return base; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +define_sequences (const key_define_t * kd) +{ + int i; + + for (i = 0; kd[i].code != 0; i++) + define_sequence (kd[i].code, kd[i].seq, kd[i].action); +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +static void +init_key_x11 (void) +{ + if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11) + { + x11_display = mc_XOpenDisplay (0); + + if (x11_display != NULL) + x11_window = DefaultRootWindow (x11_display); + } +} +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +/* --------------------------------------------------------------------------------------------- */ +/* Workaround for System V Curses vt100 bug */ + +static int +getch_with_delay (void) +{ + int c; + + /* This routine could be used on systems without mouse support, + so we need to do the select check :-( */ + while (TRUE) + { + if (pending_keys == NULL) + try_channels (FALSE); + + /* Try to get a character */ + c = get_key_code (0); + if (c != -1) + break; + + /* Failed -> wait 0.1 secs and try again */ + try_channels (TRUE); + } + + /* Success -> return the character */ + return c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +xmouse_get_event (Gpm_Event * ev, gboolean extended) +{ + static gint64 tv1 = 0; /* Force first click as single */ + static int clicks = 0; + static int last_btn = 0; + int btn; + + /* Decode Xterm mouse information to a GPM style event */ + + if (!extended) + { + /* Variable btn has following meaning: */ + /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */ + btn = tty_lowlevel_getch () - 32; + /* Coordinates are 33-based */ + /* Transform them to 1-based */ + ev->x = tty_lowlevel_getch () - 32; + ev->y = tty_lowlevel_getch () - 32; + } + else + { + /* SGR 1006 extension (e.g. "\e[<0;12;300M"): + - Numbers are encoded in decimal to make it ASCII-safe + and to overcome the limit of 223 columns/rows. + - Mouse release is encoded by trailing 'm' rather than 'M' + so that the released button can be reported. + - Numbers are no longer offset by 32. */ + char c; + + btn = ev->x = ev->y = 0; + ev->type = 0; /* In case we return on an invalid sequence */ + + while ((c = tty_lowlevel_getch ()) != ';') + { + if (c < '0' || c > '9') + return; + btn = 10 * btn + (c - '0'); + } + while ((c = tty_lowlevel_getch ()) != ';') + { + if (c < '0' || c > '9') + return; + ev->x = 10 * ev->x + (c - '0'); + } + while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm') + { + if (c < '0' || c > '9') + return; + ev->y = 10 * ev->y + (c - '0'); + } + /* Legacy mouse protocol doesn't tell which button was released, + conveniently all of mc's widgets are written not to rely on this + information. With the SGR extension the released button becomes + known, but for the sake of simplicity we just ignore it. */ + if (c == 'm') + btn = 3; + } + + /* There seems to be no way of knowing which button was released */ + /* So we assume all the buttons were released */ + + if (btn == 3) + { + if (last_btn != 0) + { + if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0) + { + /* FIXME: DIRTY HACK */ + /* don't generate GPM_UP after mouse wheel */ + /* need for menu event handling */ + ev->type = 0; + tv1 = 0; + } + else + { + ev->type = GPM_UP | (GPM_SINGLE << clicks); + tv1 = g_get_monotonic_time (); + } + ev->buttons = 0; + last_btn = 0; + clicks = 0; + } + else + { + /* Bogus event, maybe mouse wheel */ + ev->type = 0; + } + } + else + { + gint64 tv2; + + if (btn >= 32 && btn <= 34) + { + btn -= 32; + ev->type = GPM_DRAG; + } + else + ev->type = GPM_DOWN; + + tv2 = g_get_monotonic_time (); + if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC) + { + clicks++; + clicks %= 3; + } + else + clicks = 0; + + switch (btn) + { + case 0: + ev->buttons = GPM_B_LEFT; + break; + case 1: + ev->buttons = GPM_B_MIDDLE; + break; + case 2: + ev->buttons = GPM_B_RIGHT; + break; + case 64: + ev->buttons = GPM_B_UP; + clicks = 0; + break; + case 65: + ev->buttons = GPM_B_DOWN; + clicks = 0; + break; + default: + /* Nothing */ + ev->type = 0; + ev->buttons = 0; + break; + } + last_btn = ev->buttons; + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Get modifier state (shift, alt, ctrl) for the last key pressed. + * We are assuming that the state didn't change since the key press. + * This is only correct if get_modifier() is called very fast after + * the input was received, so that the user didn't release the + * modifier keys yet. + */ + +static int +get_modifier (void) +{ + int result = 0; +#ifdef __QNXNTO__ + static int in_photon = 0; + static int ph_ig = 0; +#endif /* __QNXNTO__ */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + if (x11_window != 0) + { + Window root, child; + int root_x, root_y; + int win_x, win_y; + unsigned int mask; + + mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x, + &root_y, &win_x, &win_y, &mask); + + if ((mask & ShiftMask) != 0) + result |= KEY_M_SHIFT; + if ((mask & ControlMask) != 0) + result |= KEY_M_CTRL; + return result; + } +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +#ifdef __QNXNTO__ + if (in_photon == 0) + { + /* First time here, let's load Photon library and attach to Photon */ + in_photon = -1; + + if (getenv ("PHOTON2_PATH") != NULL) + { + /* QNX 6.x has no support for RTLD_LAZY */ + void *ph_handle; + + ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW); + if (ph_handle != NULL) + { + ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach"); + ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup"); + ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor"); + if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL) + && (*ph_attach) (0, 0) != NULL) + { + /* Attached */ + ph_ig = (*ph_input_group) (0); + in_photon = 1; + } + } + } + } + /* We do not have Photon running. Assume we are in text console or xterm */ + if (in_photon == -1) + { + int mod_status; + int shift_ext_status; + + if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) == + -1) + return 0; + + shift_ext_status = mod_status & 0xffffff00UL; + mod_status &= 0x7f; + if ((mod_status & _LINESTATUS_CON_ALT) != 0) + result |= KEY_M_ALT; + if ((mod_status & _LINESTATUS_CON_CTRL) != 0) + result |= KEY_M_CTRL; + if ((mod_status & _LINESTATUS_CON_SHIFT) != 0 || (shift_ext_status & 0x00000800UL) != 0) + result |= KEY_M_SHIFT; + } + else + { + PhCursorInfo_t cursor_info; + + (*ph_query_cursor) (ph_ig, &cursor_info); + if ((cursor_info.key_mods & 0x04) != 0) + result |= KEY_M_ALT; + if ((cursor_info.key_mods & 0x02) != 0) + result |= KEY_M_CTRL; + if ((cursor_info.key_mods & 0x01) != 0) + result |= KEY_M_SHIFT; + } +#endif /* __QNXNTO__ */ + +#if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX) + { + unsigned char modifiers = 6; + + if (ioctl (0, TIOCLINUX, &modifiers) < 0) + return 0; + + /* Translate Linux modifiers into mc modifiers */ + if ((modifiers & SHIFT_PRESSED) != 0) + result |= KEY_M_SHIFT; + if ((modifiers & (ALTL_PRESSED | ALTR_PRESSED)) != 0) + result |= KEY_M_ALT; + if ((modifiers & CONTROL_PRESSED) != 0) + result |= KEY_M_CTRL; + } +#endif /* !__linux__ */ + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +push_char (int c) +{ + gboolean ret = FALSE; + + if (seq_append == NULL) + seq_append = seq_buffer; + + if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2])) + { + *(seq_append++) = c; + *seq_append = '\0'; + ret = TRUE; + } + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Apply corrections for the keycode generated in get_key_code() */ + +static int +correct_key_code (int code) +{ + unsigned int c = code & ~KEY_M_MASK; /* code without modifier */ + unsigned int mod = code & KEY_M_MASK; /* modifier */ +#ifdef __QNXNTO__ + unsigned int qmod; /* bunch of the QNX console + modifiers needs unchanged */ +#endif /* __QNXNTO__ */ + + /* + * Add key modifiers directly from X11 or OS. + * Ordinary characters only get modifiers from sequences. + */ + if (c < 32 || c >= 256) + mod |= get_modifier (); + + /* This is needed if the newline is reported as carriage return */ + if (c == '\r') + c = '\n'; + + /* This is reported to be useful on AIX */ + if (c == KEY_SCANCEL) + c = '\t'; + + /* Convert Back Tab to Shift+Tab */ + if (c == KEY_BTAB) + { + c = '\t'; + mod = KEY_M_SHIFT; + } + + /* F0 is the same as F10 for out purposes */ + if (c == KEY_F (0)) + c = KEY_F (10); + + /* + * We are not interested if Ctrl was pressed when entering control + * characters, so assume that it was. When checking for such keys, + * XCTRL macro should be used. In some cases, we are interested, + * e.g. to distinguish Ctrl-Enter from Enter. + */ + if (c == '\b') + { + /* Special case for backspase ('\b' < 32) */ + c = KEY_BACKSPACE; + mod &= ~KEY_M_CTRL; + } + else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') + mod |= KEY_M_CTRL; + +#ifdef __QNXNTO__ + qmod = get_modifier (); + + if (c == 127 && mod == 0) + { + /* Add Ctrl/Alt/Shift-BackSpace */ + mod |= get_modifier (); + c = KEY_BACKSPACE; + } + + if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT) + { + /* Add Shift-Insert on key pad */ + mod = KEY_M_SHIFT; + c = KEY_IC; + } + + if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT) + { + /* Add Shift-Del on key pad */ + mod = KEY_M_SHIFT; + c = KEY_DC; + } +#endif /* __QNXNTO__ */ + + /* Unrecognized 0177 is delete (preserve Ctrl) */ + if (c == 0177) + c = KEY_BACKSPACE; + +#if 0 + /* Unrecognized Ctrl-d is delete */ + if (c == 'd' & 31) + { + c = KEY_DC; + mod &= ~KEY_M_CTRL; + } + + /* Unrecognized Ctrl-h is backspace */ + if (c == 'h' & 31) + { + c = KEY_BACKSPACE; + mod &= ~KEY_M_CTRL; + } +#endif + + /* Shift+BackSpace is backspace */ + if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0) + mod &= ~KEY_M_SHIFT; + + /* Convert Shift+Fn to F(n+10) */ + if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT) != 0) + c += 10; + + /* Remove Shift information from function keys */ + if (c >= KEY_F (1) && c <= KEY_F (20)) + mod &= ~KEY_M_SHIFT; + + if (!mc_global.tty.alternate_plus_minus) + switch (c) + { + case KEY_KP_ADD: + c = '+'; + break; + case KEY_KP_SUBTRACT: + c = '-'; + break; + case KEY_KP_MULTIPLY: + c = '*'; + break; + default: + break; + } + + return (mod | c); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +getch_with_timeout (unsigned int delay_us) +{ + fd_set Read_FD_Set; + int c; + struct timeval time_out; + + time_out.tv_sec = delay_us / G_USEC_PER_SEC; + time_out.tv_usec = delay_us % G_USEC_PER_SEC; + tty_nodelay (TRUE); + FD_ZERO (&Read_FD_Set); + FD_SET (input_fd, &Read_FD_Set); + select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out); + c = tty_lowlevel_getch (); + tty_nodelay (FALSE); + return c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +learn_store_key (char *buffer, char **p, int c) +{ + if (*p - buffer > 253) + return; + + if (c == ESC_CHAR) + { + *(*p)++ = '\\'; + *(*p)++ = 'e'; + } + else if (c < ' ') + { + *(*p)++ = '^'; + *(*p)++ = c + 'a' - 1; + } + else if (c == '^') + { + *(*p)++ = '^'; + *(*p)++ = '^'; + } + else + *(*p)++ = (char) c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +k_dispose (key_def * k) +{ + if (k != NULL) + { + k_dispose (k->child); + k_dispose (k->next); + g_free (k); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +key_code_comparator_by_name (const void *p1, const void *p2) +{ + const key_code_name_t *n1 = *(const key_code_name_t * const *) p1; + const key_code_name_t *n2 = *(const key_code_name_t * const *) p2; + + return g_ascii_strcasecmp (n1->name, n2->name); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +key_code_comparator_by_code (const void *p1, const void *p2) +{ + const key_code_name_t *n1 = *(const key_code_name_t * const *) p1; + const key_code_name_t *n2 = *(const key_code_name_t * const *) p2; + + return n1->code - n2->code; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +sort_key_conv_tab (enum KeySortType type_sort) +{ + if (has_been_sorted != type_sort) + { + size_t i; + + for (i = 0; i < key_conv_tab_size; i++) + key_conv_tab_sorted[i] = &key_name_conv_tab[i]; + + if (type_sort == KEY_SORTBYNAME) + qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]), + &key_code_comparator_by_name); + else if (type_sort == KEY_SORTBYCODE) + qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]), + &key_code_comparator_by_code); + + has_been_sorted = type_sort; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +lookup_keyname (const char *name, int *idx) +{ + if (name[0] != '\0') + { + const key_code_name_t key = { 0, name, NULL, NULL }; + const key_code_name_t *keyp = &key; + const key_code_name_t **res; + + if (name[1] == '\0') + { + *idx = -1; + return (int) name[0]; + } + + sort_key_conv_tab (KEY_SORTBYNAME); + + res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size, + sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name); + + if (res != NULL) + { + *idx = (int) (res - key_conv_tab_sorted); + return (*res)->code; + } + } + + *idx = -1; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +lookup_keycode (const long code, int *idx) +{ + if (code != 0) + { + const key_code_name_t key = { code, NULL, NULL, NULL }; + const key_code_name_t *keyp = &key; + const key_code_name_t **res; + + sort_key_conv_tab (KEY_SORTBYCODE); + + res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size, + sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code); + + if (res != NULL) + { + *idx = (int) (res - key_conv_tab_sorted); + return TRUE; + } + } + + *idx = -1; + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/* This has to be called before init_slang or whatever routine + calls any define_sequence */ + +void +init_key (void) +{ + const char *term; + + term = getenv ("TERM"); + + /* This has to be the first define_sequence */ + /* So, we can assume that the first keys member has ESC */ + define_sequences (mc_default_keys); + + /* Terminfo on irix does not have some keys */ + if (mc_global.tty.xterm_flag + || (term != NULL + && (strncmp (term, "iris-ansi", 9) == 0 + || strncmp (term, "xterm", 5) == 0 + || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0))) + define_sequences (xterm_key_defines); + + /* load some additional keys (e.g. direct Alt-? support) */ + load_xtra_key_defines (); + +#ifdef __QNX__ + if ((term != NULL) && (strncmp (term, "qnx", 3) == 0)) + { + /* Modify the default value of use_8th_bit_as_meta: we would + * like to provide a working mc for a newbie who knows nothing + * about [Options|Display bits|Full 8 bits input]... + * + * Don't use 'meta'-bit, when we are dealing with a + * 'qnx*'-type terminal: clear the default value! + * These terminal types use 0xFF as an escape character, + * so use_8th_bit_as_meta==1 must not be enabled! + * + * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff + * is not used now (doesn't even depend on use_8th_bit_as_meta + * as in mc-3.1.2)...GREAT!...no additional code is required!] + */ + use_8th_bit_as_meta = FALSE; + } +#endif /* __QNX__ */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + init_key_x11 (); +#endif + + /* Load the qansi-m key definitions + if we are running under the qansi-m terminal */ + if (term != NULL && (strncmp (term, "qansi-m", 7) == 0)) + define_sequences (qansi_key_defines); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * This has to be called after SLang_init_tty/slint_init + */ + +void +init_key_input_fd (void) +{ +#ifdef HAVE_SLANG + input_fd = SLang_TT_Read_FD; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +done_key (void) +{ + k_dispose (keys); + g_slist_free_full (select_list, g_free); + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + if (x11_display) + mc_XCloseDisplay (x11_display); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +add_select_channel (int fd, select_fn callback, void *info) +{ + select_t *new; + + new = g_new (select_t, 1); + new->fd = fd; + new->callback = callback; + new->info = info; + + select_list = g_slist_prepend (select_list, new); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +delete_select_channel (int fd) +{ + GSList *p; + + p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd); + if (p != NULL) + select_list = g_slist_delete_link (select_list, p); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +channels_up (void) +{ + if (disabled_channels == 0) + fputs ("Error: channels_up called with disabled_channels = 0\n", stderr); + disabled_channels--; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +channels_down (void) +{ + disabled_channels++; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Return the code associated with the symbolic name keyname + */ + +long +tty_keyname_to_keycode (const char *name, char **label) +{ + char **lc_keys, **p; + char *cname; + int k = -1; + int key = 0; + int lc_index = -1; + + int use_meta = -1; + int use_ctrl = -1; + int use_shift = -1; + + if (name == NULL) + return 0; + + cname = g_strstrip (g_strdup (name)); + lc_keys = g_strsplit_set (cname, "-+ ", -1); + g_free (cname); + + for (p = lc_keys; p != NULL && *p != NULL; p++) + { + if ((*p)[0] != '\0') + { + int idx; + + key = lookup_keyname (g_strstrip (*p), &idx); + + if (key == KEY_M_ALT) + use_meta = idx; + else if (key == KEY_M_CTRL) + use_ctrl = idx; + else if (key == KEY_M_SHIFT) + use_shift = idx; + else + { + k = key; + lc_index = idx; + break; + } + } + } + + g_strfreev (lc_keys); + + /* output */ + if (k <= 0) + return 0; + + if (label != NULL) + { + GString *s; + + s = g_string_new (""); + + if (use_meta != -1) + { + g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut); + g_string_append_c (s, '-'); + } + if (use_ctrl != -1) + { + g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut); + g_string_append_c (s, '-'); + } + if (use_shift != -1) + { + if (k < 127) + g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k)); + else + { + g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut); + g_string_append_c (s, '-'); + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + } + } + else if (k < 128) + { + if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL)) + g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k)); + else + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + } + else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL)) + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + else + g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key)); + + *label = g_string_free (s, FALSE); + } + + if (use_shift != -1) + { + if (k < 127 && k > 31) + k = g_ascii_toupper ((gchar) k); + else + k |= KEY_M_SHIFT; + } + + if (use_ctrl != -1) + { + if (k < 256) + k = XCTRL (k); + else + k |= KEY_M_CTRL; + } + + if (use_meta != -1) + k = ALT (k); + + return (long) k; +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +tty_keycode_to_keyname (const int keycode) +{ + /* code without modifier */ + unsigned int k = keycode & ~KEY_M_MASK; + /* modifier */ + unsigned int mod = keycode & KEY_M_MASK; + + int key_idx = -1; + + GString *s; + int idx; + + s = g_string_sized_new (8); + + if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256)) + { + if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx)) + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + } + + if ((mod & KEY_M_CTRL) != 0) + { + /* non printeble chars like a CTRL-[A..Z] */ + if (k < 32) + k += 64; + + if (lookup_keycode (KEY_M_CTRL, &idx)) + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + } + } + + if ((mod & KEY_M_SHIFT) != 0) + { + if (lookup_keycode (KEY_M_ALT, &idx)) + { + if (k < 127) + g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k)); + else + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + } + } + } + else if (k < 128) + { + if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL)) + g_string_append_c (s, (gchar) k); + else + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + } + else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL)) + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + else + g_string_append_c (s, (gchar) keycode); + } + + return g_string_free (s, s->len == 0); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Return TRUE on success, FALSE on error. + * An error happens if SEQ is a beginning of an existing longer sequence. + */ + +gboolean +define_sequence (int code, const char *seq, int action) +{ + key_def *base; + + if (strlen (seq) > SEQ_BUFFER_LEN - 1) + return FALSE; + + for (base = keys; (base != NULL) && (*seq != '\0');) + if (*seq == base->ch) + { + if (base->child == NULL) + { + if (*(seq + 1) != '\0') + base->child = create_sequence (seq + 1, code, action); + else + { + /* The sequence matches an existing one. */ + base->code = code; + base->action = action; + } + return TRUE; + } + + base = base->child; + seq++; + } + else + { + if (base->next != NULL) + base = base->next; + else + { + base->next = create_sequence (seq, code, action); + return TRUE; + } + } + + if (*seq == '\0') + { + /* Attempt to redefine a sequence with a shorter sequence. */ + return FALSE; + } + + keys = create_sequence (seq, code, action); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Check if we are idle, i.e. there are no pending keyboard or mouse + * events. Return 1 is idle, 0 is there are pending events. + */ +gboolean +is_idle (void) +{ + int nfd; + fd_set select_set; + struct timeval time_out; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); + nfd = MAX (0, input_fd) + 1; + time_out.tv_sec = 0; + time_out.tv_usec = 0; +#ifdef HAVE_LIBGPM + if (mouse_enabled && use_mouse_p == MOUSE_GPM) + { + if (gpm_fd >= 0) + { + FD_SET (gpm_fd, &select_set); + nfd = MAX (nfd, gpm_fd + 1); + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + } + } +#endif + return (select (nfd, &select_set, 0, 0, &time_out) <= 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +get_key_code (int no_delay) +{ + int c; + static key_def *this = NULL, *parent; + static gint64 esc_time = -1; + static int lastnodelay = -1; + + if (no_delay != lastnodelay) + { + this = NULL; + lastnodelay = no_delay; + } + + pend_send: + if (pending_keys != NULL) + { + gboolean bad_seq; + + c = *pending_keys++; + while (c == ESC_CHAR) + c = ALT (*pending_keys++); + + bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0'); + if (*pending_keys == '\0' || bad_seq) + pending_keys = seq_append = NULL; + + if (bad_seq) + { + /* This is an unknown ESC sequence. + * To prevent interpreting its tail as a random garbage, + * eat and discard all buffered and quickly following chars. + * Small, but non-zero timeout is needed to reconnect + * escape sequence split up by e.g. a serial line. + */ + int paranoia = 20; + + while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0) + ; + } + else + { + if (c > 127 && c < 256 && use_8th_bit_as_meta) + c = ALT (c & 0x7f); + + goto done; + } + } + + nodelay_try_again: + if (no_delay != 0) + tty_nodelay (TRUE); + + c = tty_lowlevel_getch (); +#if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE) + if (c == KEY_RESIZE) + goto nodelay_try_again; +#endif + + if (no_delay != 0) + { + tty_nodelay (FALSE); + if (c == -1) + { + if (this == NULL || parent == NULL || parent->action != MCKEY_ESCAPE || !old_esc_mode || + esc_time == -1 || g_get_monotonic_time () < esc_time + old_esc_mode_timeout) + return -1; + + this = NULL; + pending_keys = seq_append = NULL; + return ESC_CHAR; + } + } + else if (c == -1) + { + /* Maybe we got an incomplete match. + This we do only in delay mode, since otherwise + tty_lowlevel_getch can return -1 at any time. */ + if (seq_append == NULL) + { + this = NULL; + return -1; + } + + pending_keys = seq_buffer; + goto pend_send; + } + + /* Search the key on the root */ + if (no_delay == 0 || this == NULL) + { + this = keys; + parent = NULL; + + if (c > 127 && c < 256 && use_8th_bit_as_meta) + { + c &= 0x7f; + + /* The first sequence defined starts with esc */ + parent = keys; + this = keys->child; + } + } + + while (this != NULL) + { + if (c == this->ch) + { + if (this->child == NULL) + { + /* We got a complete match, return and reset search */ + pending_keys = seq_append = NULL; + c = this->code; + goto done; + } + + /* No match yet, but it may be a prefix for a valid seq */ + if (!push_char (c)) + { + pending_keys = seq_buffer; + goto pend_send; + } + + parent = this; + this = this->child; + if (parent->action == MCKEY_ESCAPE && old_esc_mode) + { + if (no_delay != 0) + { + esc_time = g_get_monotonic_time (); + goto nodelay_try_again; + } + + esc_time = -1; + c = getch_with_timeout (old_esc_mode_timeout); + if (c != -1) + continue; + + pending_keys = seq_append = NULL; + this = NULL; + return ESC_CHAR; + } + + if (no_delay != 0) + goto nodelay_try_again; + c = tty_lowlevel_getch (); + continue; + } + + /* c != this->ch. Try other keys with this prefix */ + if (this->next != NULL) + { + this = this->next; + continue; + } + + /* No match found. Is it one of our ESC <key> specials? */ + if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) + { + /* Convert escape-digits to F-keys */ + if (g_ascii_isdigit (c)) + c = KEY_F (c - '0'); + else if (c == ' ') + c = ESC_CHAR; + else + c = ALT (c); + + pending_keys = seq_append = NULL; + goto done; + } + + /* Unknown sequence. Maybe a prefix of a longer one. Save it. */ + push_char (c); + pending_keys = seq_buffer; + goto pend_send; + } /* while (this != NULL) */ + + done: + this = NULL; + return correct_key_code (c); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a character read from stdin with appropriate interpretation */ +/* Also takes care of generated mouse events */ +/* Returns EV_MOUSE if it is a mouse event */ +/* Returns EV_NONE if non-blocking or interrupt set and nothing was done */ + +int +tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) +{ + int c; + int flag = 0; /* Return value from select */ +#ifdef HAVE_LIBGPM + static struct Gpm_Event ev; /* Mouse event */ +#endif + struct timeval time_out; + struct timeval *time_addr = NULL; + static int dirty = 3; + + if ((dirty == 3) || is_idle ()) + { + mc_refresh (); + dirty = 1; + } + else + dirty++; + + vfs_timeout_handler (); + + /* Ok, we use (event->x < 0) to signal that the event does not contain + a suitable position for the mouse, so we can't use show_mouse_pointer + on it. + */ + if (event->x > 0) + { + show_mouse_pointer (event->x, event->y); + if (!redo_event) + event->x = -1; + } + + /* Repeat if using mouse */ + while (pending_keys == NULL) + { + int nfd; + fd_set select_set; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); + nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1; + +#ifdef HAVE_LIBGPM + if (mouse_enabled && (use_mouse_p == MOUSE_GPM)) + { + if (gpm_fd >= 0) + { + FD_SET (gpm_fd, &select_set); + nfd = MAX (nfd, gpm_fd + 1); + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + break; + } + } +#endif + + if (redo_event) + { + time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC; + time_out.tv_sec = 0; + + time_addr = &time_out; + } + else + { + int seconds; + + seconds = vfs_timeouts (); + time_addr = NULL; + + if (seconds != 0) + { + /* the timeout could be improved and actually be + * the number of seconds until the next vfs entry + * timeouts in the stamp list. + */ + + time_out.tv_sec = seconds; + time_out.tv_usec = 0; + time_addr = &time_out; + } + } + + if (!block || tty_got_winch ()) + { + time_addr = &time_out; + time_out.tv_sec = 0; + time_out.tv_usec = 0; + } + + tty_enable_interrupt_key (); + flag = select (nfd, &select_set, NULL, NULL, time_addr); + tty_disable_interrupt_key (); + + /* select timed out: it could be for any of the following reasons: + * redo_event -> it was because of the MOU_REPEAT handler + * !block -> we did not block in the select call + * else -> 10 second timeout to check the vfs status. + */ + if (flag == 0) + { + if (redo_event) + return EV_MOUSE; + if (!block || tty_got_winch ()) + return EV_NONE; + vfs_timeout_handler (); + } + if (flag == -1 && errno == EINTR) + return EV_NONE; + + check_selects (&select_set); + + if (FD_ISSET (input_fd, &select_set)) + break; + +#ifdef HAVE_LIBGPM + if (mouse_enabled && use_mouse_p == MOUSE_GPM) + { + if (gpm_fd >= 0) + { + if (FD_ISSET (gpm_fd, &select_set)) + { + int status; + + status = Gpm_GetEvent (&ev); + if (status == 1) /* success */ + { + Gpm_FitEvent (&ev); + *event = ev; + return EV_MOUSE; + } + if (status <= 0) /* connection closed; -1 == error */ + { + if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + + disable_mouse (); + return EV_NONE; + } + } + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + break; + } + } +#endif /* !HAVE_LIBGPM */ + } + +#ifndef HAVE_SLANG + flag = is_wintouched (stdscr); + untouchwin (stdscr); +#endif /* !HAVE_SLANG */ + c = block ? getch_with_delay () : get_key_code (1); + +#ifndef HAVE_SLANG + if (flag > 0) + tty_touch_screen (); +#endif /* !HAVE_SLANG */ + + if (mouse_enabled && (c == MCKEY_MOUSE +#ifdef KEY_MOUSE + || c == KEY_MOUSE +#endif /* KEY_MOUSE */ + || c == MCKEY_EXTENDED_MOUSE)) + { + /* Mouse event. See tickets 2956 and 3954 for extended mode detection. */ + gboolean extended = c == MCKEY_EXTENDED_MOUSE; + +#ifdef KEY_MOUSE + extended = extended || (c == KEY_MOUSE && xmouse_seq == NULL + && xmouse_extended_seq != NULL); +#endif /* KEY_MOUSE */ + + xmouse_get_event (event, extended); + c = (event->type != 0) ? EV_MOUSE : EV_NONE; + } + else if (c == MCKEY_BRACKETED_PASTING_START) + { + bracketed_pasting_in_progress = TRUE; + c = EV_NONE; + } + else if (c == MCKEY_BRACKETED_PASTING_END) + { + bracketed_pasting_in_progress = FALSE; + c = EV_NONE; + } + + return c; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a key press, mouse events are discarded */ + +int +tty_getch (void) +{ + Gpm_Event ev; + int key; + + ev.x = -1; + while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE) + ; + return key; +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +learn_key (void) +{ + /* LEARN_TIMEOUT in ms */ +#define LEARN_TIMEOUT 200 + + fd_set Read_FD_Set; + gint64 end_time; + int c; + char buffer[256]; + char *p = buffer; + + tty_keypad (FALSE); /* disable interpreting keys by ncurses */ + c = tty_lowlevel_getch (); + while (c == -1) + c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */ + learn_store_key (buffer, &p, c); + + end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC; + + tty_nodelay (TRUE); + while (TRUE) + { + while ((c = tty_lowlevel_getch ()) == -1) + { + gint64 time_out; + struct timeval tv; + + time_out = end_time - g_get_monotonic_time (); + if (time_out <= 0) + break; + + tv.tv_sec = time_out / G_USEC_PER_SEC; + tv.tv_usec = time_out % G_USEC_PER_SEC; + FD_ZERO (&Read_FD_Set); + FD_SET (input_fd, &Read_FD_Set); + select (input_fd + 1, &Read_FD_Set, NULL, NULL, &tv); + } + if (c == -1) + break; + learn_store_key (buffer, &p, c); + } + tty_keypad (TRUE); + tty_nodelay (FALSE); + *p = '\0'; + return (buffer[0] != '\0' ? g_strdup (buffer) : NULL); +#undef LEARN_TIMEOUT +} + +/* --------------------------------------------------------------------------------------------- */ +/* xterm and linux console only: set keypad to numeric or application + mode. Only in application keypad mode it's possible to distinguish + the '+' key and the '+' on the keypad ('*' and '-' ditto) */ + +void +numeric_keypad_mode (void) +{ + if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag) + { + fputs (ESC_STR ">", stdout); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +application_keypad_mode (void) +{ + if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag) + { + fputs (ESC_STR "=", stdout); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +enable_bracketed_paste (void) +{ + printf (ESC_STR "[?2004h"); + fflush (stdout); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +disable_bracketed_paste (void) +{ + printf (ESC_STR "[?2004l"); + fflush (stdout); + bracketed_pasting_in_progress = FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/key.h b/lib/tty/key.h new file mode 100644 index 0000000..6dd2cee --- /dev/null +++ b/lib/tty/key.h @@ -0,0 +1,121 @@ +/** \file key.h + * \brief Header: keyboard support routines + */ + +#ifndef MC__KEY_H +#define MC__KEY_H + +#include "lib/global.h" /* <glib.h> */ +#include "tty.h" /* KEY_F macro */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/* Possible return values from tty_get_event: */ +#define EV_MOUSE -2 +#define EV_NONE -1 + +/* + * Internal representation of the key modifiers. It is used in the + * sequence tables and the keycodes in the mc sources. + */ +#define KEY_M_SHIFT 0x1000 +#define KEY_M_ALT 0x2000 +#define KEY_M_CTRL 0x4000 +#define KEY_M_MASK 0x7000 + +#define XCTRL(x) (KEY_M_CTRL | ((x) & 0x1F)) +#define ALT(x) (KEY_M_ALT | (unsigned int)(x)) + +/* To define sequences and return codes */ +#define MCKEY_NOACTION 0 +#define MCKEY_ESCAPE 1 + +/* Return code for the mouse sequence */ +#define MCKEY_MOUSE -2 + +/* Return code for the extended mouse sequence */ +#define MCKEY_EXTENDED_MOUSE -3 + +/* Return code for brackets of bracketed paste mode */ +#define MCKEY_BRACKETED_PASTING_START -4 +#define MCKEY_BRACKETED_PASTING_END -5 + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +typedef struct +{ + int code; + const char *name; + const char *longname; + const char *shortcut; +} key_code_name_t; + +struct Gpm_Event; + +/*** global variables defined in .c file *********************************************************/ + +extern const key_code_name_t key_name_conv_tab[]; + +extern int old_esc_mode_timeout; + +extern int double_click_speed; +extern gboolean old_esc_mode; +extern gboolean use_8th_bit_as_meta; +extern int mou_auto_repeat; + +extern gboolean bracketed_pasting_in_progress; + +/*** declarations of public functions ************************************************************/ + +gboolean define_sequence (int code, const char *seq, int action); + +void init_key (void); +void init_key_input_fd (void); +void done_key (void); + +long tty_keyname_to_keycode (const char *name, char **label); +char *tty_keycode_to_keyname (const int keycode); +/* mouse support */ +int tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block); +gboolean is_idle (void); +int tty_getch (void); + +/* While waiting for input, the program can select on more than one file */ +typedef int (*select_fn) (int fd, void *info); + +/* Channel manipulation */ +void add_select_channel (int fd, select_fn callback, void *info); +void delete_select_channel (int fd); + +/* Activate/deactivate the channel checking */ +void channels_up (void); +void channels_down (void); + +/* internally used in key.c, defined in keyxtra.c */ +void load_xtra_key_defines (void); + +/* Learn a single key */ +char *learn_key (void); + +/* Returns a key code (interpreted) */ +int get_key_code (int nodelay); + +/* Set keypad mode (xterm and linux console only) */ +void numeric_keypad_mode (void); +void application_keypad_mode (void); + +/* Bracketed paste mode */ +void enable_bracketed_paste (void); +void disable_bracketed_paste (void); + +/*** inline functions ****************************************************************************/ + +static inline gboolean +is_abort_char (int c) +{ + return ((c == (int) ESC_CHAR) || (c == (int) KEY_F (10))); +} + +#endif /* MC_KEY_H */ diff --git a/lib/tty/keyxdef.c b/lib/tty/keyxdef.c new file mode 100644 index 0000000..a496f67 --- /dev/null +++ b/lib/tty/keyxdef.c @@ -0,0 +1,455 @@ +/* {{{ Copyright */ + +/* + Additional keyboard support routines. + + Copyright (C) 1998-2023 + Free Software Foundation, Inc. + + Written by: + Gyorgy Tamasi, 1998 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* }}} */ + +/** \file keyxdef.c + * \brief Source: additional keyboard support routines + * + * PURPOSE: + * We would like to support the direct ALT-?/META-? and some other 'extra' + * keyboard functionality provided by some terminals under some OSes (and + * not supported by the 'learn keys...' facility of 'mc'. + * (First target platform: QNX.) + * + * REMARK: + * + * Implementation strategy: we don't want to rely on a specific terminal + * information database management API (termcap,terminfo,SLang,...), so we + * try to define a superset of the possible key identifiers here. + * + */ + +#include <config.h> + +#include "lib/global.h" + +#include "tty.h" +#include "mouse.h" /* required before key.h */ +#include "key.h" + +#if defined (__QNX__) && !defined (__QNXNTO__) +#define HAVE_QNX_KEYS +#endif + +#ifdef HAVE_QNX_KEYS + +/* select implementation: use QNX/term interface */ +#define __USE_QNX_TI + +/* implementation specific _TE() definition */ +#ifdef __USE_QNX_TI + +/* include QNX/term.h (not NCURSES/term.h!) */ +#if __WATCOMC__ > 1000 +#include <sys/term.h> +#else +#include <term.h> +#endif +#include <stdlib.h> /* getenv() */ + +/* fieldname -> index conversion */ +#define __QTISX(_qtisn) \ + (((int)(&((struct _strs*)0)->_qtisn))/sizeof(charoffset)) + +/* define the OS/implementation-specific __TK() format */ +#define __TK(_tis,_tcs,_tisx,_qtisn) __QTISX(_qtisn) + +#endif /* __USE_QNX_TI */ + +#endif /* HAVE_QNX_KEYS */ + + +/* {{{ */ + +/* general key definitions: + * + * format: + * + * terminfo name, + * termcap name, + * index in the terminfo string table (ncurses), + * field name in the QNX terminfo strings struct + */ + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + + +#define Key_backspace __TK("kbs", "kb", 55, _ky_backspace ) +#define Key_catab __TK("ktbc", "ka", 56, _ky_catab ) +#define Key_clear __TK("kclr", "kC", 57, _ky_clear ) +#define Key_ctab __TK("kctab", "kt", 58, _ky_ctab ) +#define Key_dc __TK("kdch1", "kD", 59, _ky_dc ) +#define Key_dl __TK("kdl1", "kL", 60, _ky_dl ) +#define Key_down __TK("kcud1", "kd", 61, _ky_down ) +#define Key_eic __TK("krmir", "kM", 62, _ky_eic ) +#define Key_eol __TK("kel", "kE", 63, _ky_eol ) +#define Key_eos __TK("ked", "kS", 64, _ky_eos ) +#define Key_f0 __TK("kf0", "k0", 65, _ky_f0 ) +#define Key_f1 __TK("kf1", "k1", 66, _ky_f1 ) +#define Key_f10 __TK("kf10", "k;", 67, _ky_f10 ) +#define Key_f2 __TK("kf2", "k2", 68, _ky_f2 ) +#define Key_f3 __TK("kf3", "k3", 69, _ky_f3 ) +#define Key_f4 __TK("kf4", "k4", 70, _ky_f4 ) +#define Key_f5 __TK("kf5", "k5", 71, _ky_f5 ) +#define Key_f6 __TK("kf6", "k6", 72, _ky_f6 ) +#define Key_f7 __TK("kf7", "k7", 73, _ky_f7 ) +#define Key_f8 __TK("kf8", "k8", 74, _ky_f8 ) +#define Key_f9 __TK("kf9", "k9", 75, _ky_f9 ) +#define Key_home __TK("khome", "kh", 76, _ky_home ) +#define Key_ic __TK("kich1", "kI", 77, _ky_ic ) +#define Key_il __TK("kil1", "kA", 78, _ky_il ) +#define Key_left __TK("kcub1", "kl", 79, _ky_left ) +#define Key_ll __TK("kll", "kH", 80, _ky_ll ) +#define Key_npage __TK("knp", "kN", 81, _ky_npage ) +#define Key_ppage __TK("kpp", "kP", 82, _ky_ppage ) +#define Key_right __TK("kcuf1", "kr", 83, _ky_right ) +#define Key_sf __TK("kind", "kF", 84, _ky_sf ) +#define Key_sr __TK("kri", "kR", 85, _ky_sr ) +#define Key_stab __TK("khts", "kT", 86, _ky_stab ) +#define Key_up __TK("kcuu1", "ku", 87, _ky_up ) +#define Key_a1 __TK("ka1", "K1", 139, _ky_a1 ) +#define Key_a3 __TK("ka3", "K3", 140, _ky_a3 ) +#define Key_b2 __TK("kb2", "K2", 141, _ky_b2 ) +#define Key_c1 __TK("kc1", "K4", 142, _ky_c1 ) +#define Key_c3 __TK("kc3", "K5", 143, _ky_c3 ) +#define Key_btab __TK("kcbt", "kB", 148, _ky_btab ) +#define Key_beg __TK("kbeg", "@1", 158, _ky_beg ) +#define Key_cancel __TK("kcan", "@2", 159, _ky_cancel ) +#define Key_close __TK("kclo", "@3", 160, _ky_close ) +#define Key_command __TK("kcmd", "@4", 161, _ky_command ) +#define Key_copy __TK("kcpy", "@5", 162, _ky_copy ) +#define Key_create __TK("kcrt", "@6", 163, _ky_create ) +#define Key_end __TK("kend", "@7", 164, _ky_end ) +#define Key_enter __TK("kent", "@8", 165, _ky_enter ) +#define Key_exit __TK("kext", "@9", 166, _ky_exit ) +#define Key_find __TK("kfnd", "@0", 167, _ky_find ) +#define Key_help __TK("khlp", "%1", 168, _ky_help ) +#define Key_mark __TK("kmrk", "%2", 169, _ky_mark ) +#define Key_message __TK("kmsg", "%3", 170, _ky_message ) +#define Key_move __TK("kmov", "%4", 171, _ky_move ) +#define Key_next __TK("knxt", "%5", 172, _ky_next ) +#define Key_open __TK("kopn", "%6", 173, _ky_open ) +#define Key_options __TK("kopt", "%7", 174, _ky_options ) +#define Key_previous __TK("kprv", "%8", 175, _ky_previous ) +#define Key_print __TK("kprt", "%9", 176, _ky_print ) +#define Key_redo __TK("krdo", "%0", 177, _ky_redo ) +#define Key_reference __TK("kref", "&1", 178, _ky_reference ) +#define Key_refresh __TK("krfr", "&2", 179, _ky_refresh ) +#define Key_replace __TK("krpl", "&3", 180, _ky_replace ) +#define Key_restart __TK("krst", "&4", 181, _ky_restart ) +#define Key_resume __TK("kres", "&5", 182, _ky_resume ) +#define Key_save __TK("ksav", "&6", 183, _ky_save ) +#define Key_suspend __TK("kspd", "&7", 184, _ky_suspend ) +#define Key_undo __TK("kund", "&8", 185, _ky_undo ) +#define Key_sbeg __TK("kBEG", "&9", 186, _ky_sbeg ) +#define Key_scancel __TK("kCAN", "&0", 187, _ky_scancel ) +#define Key_scommand __TK("kCMD", "*1", 188, _ky_scommand ) +#define Key_scopy __TK("kCPY", "*2", 189, _ky_scopy ) +#define Key_screate __TK("kCRT", "*3", 190, _ky_screate ) +#define Key_sdc __TK("kDC", "*4", 191, _ky_sdc ) +#define Key_sdl __TK("kDL", "*5", 192, _ky_sdl ) +#define Key_select __TK("kslt", "*6", 193, _ky_select ) +#define Key_send __TK("kEND", "*7", 194, _ky_send ) +#define Key_seol __TK("kEOL", "*8", 195, _ky_seol ) +#define Key_sexit __TK("kEXT", "*9", 196, _ky_sexit ) +#define Key_sfind __TK("kFND", "*0", 197, _ky_sfind ) +#define Key_shelp __TK("kHLP", "#1", 198, _ky_shelp ) +#define Key_shome __TK("kHOM", "#2", 199, _ky_shome ) +#define Key_sic __TK("kIC", "#3", 200, _ky_sic ) +#define Key_sleft __TK("kLFT", "#4", 201, _ky_sleft ) +#define Key_smessage __TK("kMSG", "%a", 202, _ky_smessage ) +#define Key_smove __TK("kMOV", "%b", 203, _ky_smove ) +#define Key_snext __TK("kNXT", "%c", 204, _ky_snext ) +#define Key_soptions __TK("kOPT", "%d", 205, _ky_soptions ) +#define Key_sprevious __TK("kPRV", "%e", 206, _ky_sprevious ) +#define Key_sprint __TK("kPRT", "%f", 207, _ky_sprint ) +#define Key_sredo __TK("kRDO", "%g", 208, _ky_sredo ) +#define Key_sreplace __TK("kRPL", "%h", 209, _ky_sreplace ) +#define Key_sright __TK("kRIT", "%i", 210, _ky_sright ) +#define Key_srsume __TK("kRES", "%j", 211, _ky_srsume ) +#define Key_ssave __TK("kSAV", "!1", 212, _ky_ssave ) +#define Key_ssuspend __TK("kSPD", "!2", 213, _ky_ssuspend ) +#define Key_sundo __TK("kUND", "!3", 214, _ky_sundo ) +#define Key_f11 __TK("kf11", "F1", 216, _ky_f11 ) +#define Key_f12 __TK("kf12", "F2", 217, _ky_f12 ) +#define Key_f13 __TK("kf13", "F3", 218, _ky_f13 ) +#define Key_f14 __TK("kf14", "F4", 219, _ky_f14 ) +#define Key_f15 __TK("kf15", "F5", 220, _ky_f15 ) +#define Key_f16 __TK("kf16", "F6", 221, _ky_f16 ) +#define Key_f17 __TK("kf17", "F7", 222, _ky_f17 ) +#define Key_f18 __TK("kf18", "F8", 223, _ky_f18 ) +#define Key_f19 __TK("kf19", "F9", 224, _ky_f19 ) +#define Key_f20 __TK("kf20", "FA", 225, _ky_f20 ) +#define Key_f21 __TK("kf21", "FB", 226, _ky_f21 ) +#define Key_f22 __TK("kf22", "FC", 227, _ky_f22 ) +#define Key_f23 __TK("kf23", "FD", 228, _ky_f23 ) +#define Key_f24 __TK("kf24", "FE", 229, _ky_f24 ) +#define Key_f25 __TK("kf25", "FF", 230, _ky_f25 ) +#define Key_f26 __TK("kf26", "FG", 231, _ky_f26 ) +#define Key_f27 __TK("kf27", "FH", 232, _ky_f27 ) +#define Key_f28 __TK("kf28", "FI", 233, _ky_f28 ) +#define Key_f29 __TK("kf29", "FJ", 234, _ky_f29 ) +#define Key_f30 __TK("kf30", "FK", 235, _ky_f30 ) +#define Key_f31 __TK("kf31", "FL", 236, _ky_f31 ) +#define Key_f32 __TK("kf32", "FM", 237, _ky_f32 ) +#define Key_f33 __TK("kf33", "FN", 238, _ky_f33 ) +#define Key_f34 __TK("kf34", "FO", 239, _ky_f34 ) +#define Key_f35 __TK("kf35", "FP", 240, _ky_f35 ) +#define Key_f36 __TK("kf36", "FQ", 241, _ky_f36 ) +#define Key_f37 __TK("kf37", "FR", 242, _ky_f37 ) +#define Key_f38 __TK("kf38", "FS", 243, _ky_f38 ) +#define Key_f39 __TK("kf39", "FT", 244, _ky_f39 ) +#define Key_f40 __TK("kf40", "FU", 245, _ky_f40 ) +#define Key_f41 __TK("kf41", "FV", 246, _ky_f41 ) +#define Key_f42 __TK("kf42", "FW", 247, _ky_f42 ) +#define Key_f43 __TK("kf43", "FX", 248, _ky_f43 ) +#define Key_f44 __TK("kf44", "FY", 249, _ky_f44 ) +#define Key_f45 __TK("kf45", "FZ", 250, _ky_f45 ) +#define Key_f46 __TK("kf46", "Fa", 251, _ky_f46 ) +#define Key_f47 __TK("kf47", "Fb", 252, _ky_f47 ) +#define Key_f48 __TK("kf48", "Fc", 253, _ky_f48 ) +#define Key_f49 __TK("kf49", "Fd", 254, _ky_f49 ) +#define Key_f50 __TK("kf50", "Fe", 255, _ky_f50 ) +#define Key_f51 __TK("kf51", "Ff", 256, _ky_f51 ) +#define Key_f52 __TK("kf52", "Fg", 257, _ky_f52 ) +#define Key_f53 __TK("kf53", "Fh", 258, _ky_f53 ) +#define Key_f54 __TK("kf54", "Fi", 259, _ky_f54 ) +#define Key_f55 __TK("kf55", "Fj", 260, _ky_f55 ) +#define Key_f56 __TK("kf56", "Fk", 261, _ky_f56 ) +#define Key_f57 __TK("kf57", "Fl", 262, _ky_f57 ) +#define Key_f58 __TK("kf58", "Fm", 263, _ky_f58 ) +#define Key_f59 __TK("kf59", "Fn", 264, _ky_f59 ) +#define Key_f60 __TK("kf60", "Fo", 265, _ky_f60 ) +#define Key_f61 __TK("kf61", "Fp", 266, _ky_f61 ) +#define Key_f62 __TK("kf62", "Fq", 267, _ky_f62 ) +#define Key_f63 __TK("kf63", "Fr", 268, _ky_f63 ) + +/* }}} */ + +#ifdef HAVE_QNX_KEYS + +/* don't force pre-defining of base keys under QNX */ +#define FORCE_BASE_KEY_DEFS 0 + +/* OS specific key aliases */ +#define Key_alt_a Key_clear +#define Key_alt_b Key_stab +#define Key_alt_c Key_close +#define Key_alt_d Key_catab +#define Key_alt_e Key_message +#define Key_alt_f Key_find +#define Key_alt_g Key_refresh +#define Key_alt_h Key_help +#define Key_alt_i Key_move +#define Key_alt_j Key_restart +#define Key_alt_k Key_options +#define Key_alt_l Key_reference +#define Key_alt_m Key_mark +#define Key_alt_n Key_sbeg +#define Key_alt_o Key_open +#define Key_alt_p Key_resume +#define Key_alt_q Key_save +#define Key_alt_r Key_replace +#define Key_alt_s Key_scopy +#define Key_alt_t Key_screate +#define Key_alt_u Key_undo +#define Key_alt_v Key_sdl +#define Key_alt_w Key_sexit +#define Key_alt_x Key_sfind +#define Key_alt_y Key_shelp +#define Key_alt_z Key_soptions + +#define Key_ctl_enter Key_enter +#define Key_ctl_tab Key_ctab + +#define Key_alt_tab Key_ctl_tab /* map ALT-TAB to CTRL-TAB */ +#define Key_alt_enter Key_ctl_enter /* map ALT-ENTER to CTRL-ENTER */ + +#ifdef __USE_QNX_TI +/* define current xtra_key_define_t (enable OS/implementation) */ +#define xtra_key_define_t qnx_key_define_t +#endif /* __USE_QNX_TI */ +#endif /* HAVE_QNX_KEYS */ + + +#ifdef xtra_key_define_t +#ifndef FORCE_BASE_KEY_DEFS +#define FORCE_BASE_KEY_DEFS 0 +#endif +#endif /* xtra_key_define_t */ + +#ifdef HAVE_QNX_KEYS +#ifdef __USE_QNX_TI +#define __CT (__cur_term) +#define __QTISOFFS(_qtisx) (((charoffset*)(&__CT->_strs))[_qtisx]) +#define __QTISSTR(_qtisx) (&__CT->_strtab[0]+__QTISOFFS(_qtisx)) +#endif /* __USE_QNX_TI */ +#endif /* HAVE_QNX_KEYS */ + +/*** file scope type declarations ****************************************************************/ + +#ifdef HAVE_QNX_KEYS +#ifdef __USE_QNX_TI +/* OS/implementation specific key-define struct */ +typedef const struct qnx_key_define_s +{ + int mc_code; + int str_idx; +} qnx_key_define_t; +#endif /* __USE_QNX_TI */ +#endif /* HAVE_QNX_KEYS */ + +/*** file scope variables ************************************************************************/ + + +#ifdef xtra_key_define_t + +/* general key define table */ +xtra_key_define_t xtra_key_defines[] = { +#if FORCE_BASE_KEY_DEFS + {KEY_BACKSPACE, Key_backspace}, + {KEY_LEFT, Key_left}, + {KEY_RIGHT, Key_right}, + {KEY_UP, Key_up}, + {KEY_DOWN, Key_down}, + {KEY_NPAGE, Key_npage}, + {KEY_PPAGE, Key_ppage}, + {KEY_HOME, Key_home}, + {KEY_END, Key_end}, + {KEY_DC, Key_dc}, + {KEY_IC, Key_ic}, + {KEY_F (1), Key_f1}, + {KEY_F (2), Key_f2}, + {KEY_F (3), Key_f3}, + {KEY_F (4), Key_f4}, + {KEY_F (5), Key_f5}, + {KEY_F (6), Key_f6}, + {KEY_F (7), Key_f7}, + {KEY_F (8), Key_f8}, + {KEY_F (9), Key_f9}, + {KEY_F (10), Key_f10}, + {KEY_F (11), Key_f11}, + {KEY_F (12), Key_f12}, + {KEY_F (13), Key_f13}, + {KEY_F (14), Key_f14}, + {KEY_F (15), Key_f15}, + {KEY_F (16), Key_f16}, + {KEY_F (17), Key_f17}, + {KEY_F (18), Key_f18}, + {KEY_F (19), Key_f19}, + {KEY_F (20), Key_f20}, +#endif + {ALT ('a'), Key_alt_a}, + {ALT ('b'), Key_alt_b}, + {ALT ('c'), Key_alt_c}, + {ALT ('d'), Key_alt_d}, + {ALT ('e'), Key_alt_e}, + {ALT ('f'), Key_alt_f}, + {ALT ('g'), Key_alt_g}, + {ALT ('h'), Key_alt_h}, + {ALT ('i'), Key_alt_i}, + {ALT ('j'), Key_alt_j}, + {ALT ('k'), Key_alt_k}, + {ALT ('l'), Key_alt_l}, + {ALT ('m'), Key_alt_m}, + {ALT ('n'), Key_alt_n}, + {ALT ('o'), Key_alt_o}, + {ALT ('p'), Key_alt_p}, + {ALT ('q'), Key_alt_q}, + {ALT ('r'), Key_alt_r}, + {ALT ('s'), Key_alt_s}, + {ALT ('t'), Key_alt_t}, + {ALT ('u'), Key_alt_u}, + {ALT ('v'), Key_alt_v}, + {ALT ('w'), Key_alt_w}, + {ALT ('x'), Key_alt_x}, + {ALT ('y'), Key_alt_y}, + {ALT ('z'), Key_alt_z}, + + {ALT ('\n'), Key_alt_enter}, + {ALT ('\t'), Key_alt_tab} +}; + +#endif /* xtra_key_define_t */ + +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_QNX_KEYS +#ifdef __USE_QNX_TI +void +load_qnx_key_defines (void) +{ + static int _qnx_keys_defined = 0; + + if (!_qnx_keys_defined) + { + int idx; + int term_setup_ok; + + __setupterm (NULL, fileno (stdout), &term_setup_ok); + if (term_setup_ok != 1) + return; + + for (idx = 0; idx < sizeof (xtra_key_defines) / sizeof (xtra_key_defines[0]); idx++) + { + int str_idx = xtra_key_defines[idx].str_idx; + + if (__QTISOFFS (str_idx)) + { + if (*__QTISSTR (str_idx)) + { + define_sequence (xtra_key_defines[idx].mc_code, + __QTISSTR (str_idx), MCKEY_NOACTION); + } + } + } + _qnx_keys_defined = 1; + } +} +#endif /* __USE_QNX_TI */ +#endif /* HAVE_QNX_KEYS */ + +/* --------------------------------------------------------------------------------------------- */ +/* called from key.c/init_key() */ + +void +load_xtra_key_defines (void) +{ +#ifdef HAVE_QNX_KEYS + load_qnx_key_defines (); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/mouse.c b/lib/tty/mouse.c new file mode 100644 index 0000000..cf42287 --- /dev/null +++ b/lib/tty/mouse.c @@ -0,0 +1,216 @@ +/* + Mouse managing + + Copyright (C) 1994-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file mouse.c + * \brief Source: mouse managing + * + * Events received by clients of this library have their coordinates 0 based + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lib/global.h" + +#include "tty.h" +#include "tty-internal.h" /* mouse_enabled */ +#include "mouse.h" +#include "key.h" /* define sequence */ + +/*** global variables ****************************************************************************/ + +Mouse_Type use_mouse_p = MOUSE_NONE; +gboolean mouse_enabled = FALSE; +int mouse_fd = -1; /* for when gpm_fd changes to < 0 and the old one must be cleared from select_set */ +const char *xmouse_seq; +const char *xmouse_extended_seq; + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +show_mouse_pointer (int x, int y) +{ +#ifdef HAVE_LIBGPM + if (use_mouse_p == MOUSE_GPM) + Gpm_DrawPointer (x, y, gpm_consolefd); +#else + (void) x; + (void) y; +#endif /* HAVE_LIBGPM */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +init_mouse (void) +{ + switch (use_mouse_p) + { +#ifdef HAVE_LIBGPM + case MOUSE_NONE: + use_mouse_p = MOUSE_GPM; + break; +#endif /* HAVE_LIBGPM */ + + case MOUSE_XTERM_NORMAL_TRACKING: + case MOUSE_XTERM_BUTTON_EVENT_TRACKING: + if (xmouse_seq != NULL) + define_sequence (MCKEY_MOUSE, xmouse_seq, MCKEY_NOACTION); + if (xmouse_extended_seq != NULL) + define_sequence (MCKEY_EXTENDED_MOUSE, xmouse_extended_seq, MCKEY_NOACTION); + break; + + default: + break; + } + + enable_mouse (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +enable_mouse (void) +{ + if (mouse_enabled) + return; + + switch (use_mouse_p) + { +#ifdef HAVE_LIBGPM + case MOUSE_GPM: + { + Gpm_Connect conn; + + conn.eventMask = ~GPM_MOVE; + conn.defaultMask = GPM_MOVE; + conn.minMod = 0; + conn.maxMod = 0; + + mouse_fd = Gpm_Open (&conn, 0); + if (mouse_fd == -1) + { + use_mouse_p = MOUSE_NONE; + return; + } + mouse_enabled = TRUE; + } + break; +#endif /* HAVE_LIBGPM */ + + case MOUSE_XTERM_NORMAL_TRACKING: + /* save old highlight mouse tracking */ + printf (ESC_STR "[?1001s"); + + /* enable mouse tracking */ + printf (ESC_STR "[?1000h"); + + /* enable SGR extended mouse reporting */ + printf (ESC_STR "[?1006h"); + + fflush (stdout); + mouse_enabled = TRUE; + break; + + case MOUSE_XTERM_BUTTON_EVENT_TRACKING: + /* save old highlight mouse tracking */ + printf (ESC_STR "[?1001s"); + + /* enable mouse tracking */ + printf (ESC_STR "[?1002h"); + + /* enable SGR extended mouse reporting */ + printf (ESC_STR "[?1006h"); + + fflush (stdout); + mouse_enabled = TRUE; + break; + + default: + break; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +disable_mouse (void) +{ + if (!mouse_enabled) + return; + + mouse_enabled = FALSE; + + switch (use_mouse_p) + { +#ifdef HAVE_LIBGPM + case MOUSE_GPM: + Gpm_Close (); + break; +#endif + case MOUSE_XTERM_NORMAL_TRACKING: + /* disable SGR extended mouse reporting */ + printf (ESC_STR "[?1006l"); + + /* disable mouse tracking */ + printf (ESC_STR "[?1000l"); + + /* restore old highlight mouse tracking */ + printf (ESC_STR "[?1001r"); + + fflush (stdout); + break; + case MOUSE_XTERM_BUTTON_EVENT_TRACKING: + /* disable SGR extended mouse reporting */ + printf (ESC_STR "[?1006l"); + + /* disable mouse tracking */ + printf (ESC_STR "[?1002l"); + + /* restore old highlight mouse tracking */ + printf (ESC_STR "[?1001r"); + + fflush (stdout); + break; + default: + break; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/mouse.h b/lib/tty/mouse.h new file mode 100644 index 0000000..99d0a69 --- /dev/null +++ b/lib/tty/mouse.h @@ -0,0 +1,117 @@ + +/** \file mouse.h + * \brief Header: mouse managing + * + * Events received by clients of this library have their coordinates 0 based + */ + +#ifndef MC__MOUSE_H +#define MC__MOUSE_H + +#ifdef HAVE_LIBGPM +/* GPM mouse support include file */ +#include <gpm.h> +#endif /* !HAVE_LIBGPM */ + + +/*** typedefs(not structures) and defined constants **********************************************/ + +#ifndef HAVE_LIBGPM +/* Equivalent definitions for non-GPM mouse support */ +/* These lines are modified version from the lines appearing in the */ +/* gpm.h include file of the Linux General Purpose Mouse server */ + +#define GPM_B_LEFT (1 << 2) +#define GPM_B_MIDDLE (1 << 1) +#define GPM_B_RIGHT (1 << 0) + +#define GPM_BARE_EVENTS(ev) ((ev)&0xF) +#endif /* !HAVE_LIBGPM */ + +/* Mouse wheel events */ +#ifndef GPM_B_DOWN +#define GPM_B_DOWN (1 << 5) +#endif + +#ifndef GPM_B_UP +#define GPM_B_UP (1 << 4) +#endif + +/*** enums ***************************************************************************************/ + +#ifndef HAVE_LIBGPM +/* Xterm mouse support supports only GPM_DOWN and GPM_UP */ +/* If you use others make sure your code also works without them */ +enum Gpm_Etype +{ + GPM_MOVE = 1, + GPM_DRAG = 2, /* exactly one in four is active at a time */ + GPM_DOWN = 4, + GPM_UP = 8, + + + GPM_SINGLE = 16, /* at most one in three is set */ + GPM_DOUBLE = 32, + GPM_TRIPLE = 64, + + GPM_MFLAG = 128, /* motion during click? */ + GPM_HARD = 256 /* if set in the defaultMask, force an already + used event to pass over to another handler */ +}; +#endif /* !HAVE_LIBGPM */ + +/* Constants returned from the mouse callback */ +enum +{ + MOU_UNHANDLED = 0, + MOU_NORMAL, + MOU_REPEAT +}; + +/* Type of mouse support */ +typedef enum +{ + MOUSE_NONE, /* Not detected yet */ + MOUSE_DISABLED, /* Explicitly disabled by -d */ + MOUSE_GPM, /* Support using GPM on Linux */ + MOUSE_XTERM, /* Support using xterm-style mouse reporting */ + MOUSE_XTERM_NORMAL_TRACKING = MOUSE_XTERM, + MOUSE_XTERM_BUTTON_EVENT_TRACKING +} Mouse_Type; + +/*** structures declarations (and typedefs of structures)*****************************************/ + +#ifndef HAVE_LIBGPM +typedef struct Gpm_Event +{ + int buttons, x, y; + enum Gpm_Etype type; +} Gpm_Event; +#endif /* !HAVE_LIBGPM */ + +/*** global variables defined in .c file *********************************************************/ + +/* Type of the currently used mouse */ +extern Mouse_Type use_mouse_p; + +/* To be used when gpm_fd were initially >= 0 */ +extern int mouse_fd; + +/* String indicating that a mouse event has occurred, usually "\E[M" */ +extern const char *xmouse_seq; + +/* String indicating that an SGR extended mouse event has occurred, namely "\E[<" */ +extern const char *xmouse_extended_seq; + +/*** declarations of public functions ************************************************************/ + +/* General (i.e. both for xterm and gpm) mouse support definitions */ + +void init_mouse (void); +void enable_mouse (void); +void disable_mouse (void); + +void show_mouse_pointer (int x, int y); + +/*** inline functions ****************************************************************************/ +#endif /* MC_MOUSE_H */ diff --git a/lib/tty/tty-internal.c b/lib/tty/tty-internal.c new file mode 100644 index 0000000..c79301d --- /dev/null +++ b/lib/tty/tty-internal.c @@ -0,0 +1,110 @@ +/* + Internal stuff of the terminal controlling library. + + Copyright (C) 2019-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2019. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: internal stuff of the terminal controlling library. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include "lib/global.h" + +#include <glib-unix.h> + +#include "tty-internal.h" + +/*** global variables ****************************************************************************/ + +/* pipe to handle SIGWINCH */ +int sigwinch_pipe[2]; + +/*** file scope macro definitions ****************************************************************/ + +/*** global variables ****************************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +tty_create_winch_pipe (void) +{ + GError *mcerror = NULL; + + if (!g_unix_open_pipe (sigwinch_pipe, FD_CLOEXEC, &mcerror)) + { + fprintf (stderr, _("\nCannot create pipe for SIGWINCH: %s (%d)\n"), + mcerror->message, mcerror->code); + g_error_free (mcerror); + exit (EXIT_FAILURE); + } + + /* If we read from an empty pipe, then read(2) will block until data is available. + * If we write to a full pipe, then write(2) blocks until sufficient data has been read + * from the pipe to allow the write to complete.. + * Therefore, use nonblocking I/O. + */ + if (!g_unix_set_fd_nonblocking (sigwinch_pipe[0], TRUE, &mcerror)) + { + fprintf (stderr, _("\nCannot configure write end of SIGWINCH pipe: %s (%d)\n"), + mcerror->message, mcerror->code); + g_error_free (mcerror); + tty_destroy_winch_pipe (); + exit (EXIT_FAILURE); + } + + if (!g_unix_set_fd_nonblocking (sigwinch_pipe[1], TRUE, &mcerror)) + { + fprintf (stderr, _("\nCannot configure read end of SIGWINCH pipe: %s (%d)\n"), + mcerror->message, mcerror->code); + g_error_free (mcerror); + tty_destroy_winch_pipe (); + exit (EXIT_FAILURE); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_destroy_winch_pipe (void) +{ + (void) close (sigwinch_pipe[0]); + (void) close (sigwinch_pipe[1]); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/tty-internal.h b/lib/tty/tty-internal.h new file mode 100644 index 0000000..a2cdfa7 --- /dev/null +++ b/lib/tty/tty-internal.h @@ -0,0 +1,49 @@ + +/** \file tty-internal.h + * \brief Header: internal stuff of the terminal controlling library + */ + +#ifndef MC__TTY_INTERNAL_H +#define MC__TTY_INTERNAL_H + +#include "lib/global.h" /* include <glib.h> */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/* Taken from S-Lang's slutty.c */ +#ifdef _POSIX_VDISABLE +#define NULL_VALUE _POSIX_VDISABLE +#else +#define NULL_VALUE 255 +#endif + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/* The mouse is currently: TRUE - enabled, FALSE - disabled */ +extern gboolean mouse_enabled; + +/* terminal ca capabilities */ +extern char *smcup; +extern char *rmcup; + +/* pipe to handle SIGWINCH */ +extern int sigwinch_pipe[2]; + +/*** declarations of public functions ************************************************************/ + +void tty_create_winch_pipe (void); +void tty_destroy_winch_pipe (void); + +char *mc_tty_normalize_from_utf8 (const char *str); +void tty_init_xterm_support (gboolean is_xterm); +int tty_lowlevel_getch (void); + +void tty_colorize_area (int y, int x, int rows, int cols, int color); + +/*** inline functions ****************************************************************************/ + +#endif /* MC_TTY_INTERNAL_H */ diff --git a/lib/tty/tty-ncurses.c b/lib/tty/tty-ncurses.c new file mode 100644 index 0000000..08f663d --- /dev/null +++ b/lib/tty/tty-ncurses.c @@ -0,0 +1,772 @@ +/* + Interface to the terminal controlling library. + Ncurses wrapper. + + Copyright (C) 2005-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009. + Ilia Maslakov <il.smind@gmail.com>, 2009. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: NCurses-based tty layer of Midnight-commander + */ + +#include <config.h> + +#include <stdlib.h> +#include <stdarg.h> +#include <signal.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#include <termios.h> + +#include "lib/global.h" +#include "lib/strutil.h" /* str_term_form */ + +#ifndef WANT_TERM_H +#define WANT_TERM_H +#endif + +#include "tty-internal.h" /* mc_tty_normalize_from_utf8() */ +#include "tty.h" +#include "color.h" /* tty_setcolor */ +#include "color-internal.h" +#include "key.h" +#include "mouse.h" +#include "win.h" + +/* include at last !!! */ +#ifdef WANT_TERM_H +#ifdef HAVE_NCURSES_TERM_H +#include <ncurses/term.h> +#else +#include <term.h> +#endif /* HAVE_NCURSES_TERM_H */ +#endif /* WANT_TERM_H */ + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#if !defined(CTRL) +#define CTRL(x) ((x) & 0x1f) +#endif + +#define yx_in_screen(y, x) \ + (y >= 0 && y < LINES && x >= 0 && x < COLS) + +/*** global variables ****************************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* ncurses supports cursor positions only within window */ +/* We use our own cursor coordinates to support partially visible widgets */ +static int mc_curs_row, mc_curs_col; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +tty_setup_sigwinch (void (*handler) (int)) +{ +#if (NCURSES_VERSION_MAJOR >= 4) && defined (SIGWINCH) + struct sigaction act, oact; + + memset (&act, 0, sizeof (act)); + act.sa_handler = handler; + sigemptyset (&act.sa_mask); +#ifdef SA_RESTART + act.sa_flags = SA_RESTART; +#endif /* SA_RESTART */ + sigaction (SIGWINCH, &act, &oact); +#endif /* SIGWINCH */ + + tty_create_winch_pipe (); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sigwinch_handler (int dummy) +{ + ssize_t n = 0; + + (void) dummy; + + n = write (sigwinch_pipe[1], "", 1); + (void) n; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Get visible part of area. + * + * @returns TRUE if any part of area is in screen bounds, FALSE otherwise. + */ +static gboolean +tty_clip (int *y, int *x, int *rows, int *cols) +{ + if (*y < 0) + { + *rows += *y; + + if (*rows <= 0) + return FALSE; + + *y = 0; + } + + if (*x < 0) + { + *cols += *x; + + if (*cols <= 0) + return FALSE; + + *x = 0; + } + + if (*y + *rows > LINES) + *rows = LINES - *y; + + if (*rows <= 0) + return FALSE; + + if (*x + *cols > COLS) + *cols = COLS - *x; + + if (*cols <= 0) + return FALSE; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +int +mc_tty_normalize_lines_char (const char *ch) +{ + char *str2; + int res; + + struct mc_tty_lines_struct + { + const char *line; + int line_code; + } const lines_codes[] = { + {"\342\224\230", ACS_LRCORNER}, /* ┌ */ + {"\342\224\224", ACS_LLCORNER}, /* └ */ + {"\342\224\220", ACS_URCORNER}, /* ┐ */ + {"\342\224\214", ACS_ULCORNER}, /* ┘ */ + {"\342\224\234", ACS_LTEE}, /* ├ */ + {"\342\224\244", ACS_RTEE}, /* ┤ */ + {"\342\224\254", ACS_TTEE}, /* ┬ */ + {"\342\224\264", ACS_BTEE}, /* ┴ */ + {"\342\224\200", ACS_HLINE}, /* ─ */ + {"\342\224\202", ACS_VLINE}, /* │ */ + {"\342\224\274", ACS_PLUS}, /* ┼ */ + + {"\342\225\235", ACS_LRCORNER | A_BOLD}, /* ╔ */ + {"\342\225\232", ACS_LLCORNER | A_BOLD}, /* ╚ */ + {"\342\225\227", ACS_URCORNER | A_BOLD}, /* ╗ */ + {"\342\225\224", ACS_ULCORNER | A_BOLD}, /* ╝ */ + {"\342\225\237", ACS_LTEE | A_BOLD}, /* ╟ */ + {"\342\225\242", ACS_RTEE | A_BOLD}, /* ╢ */ + {"\342\225\244", ACS_TTEE | A_BOLD}, /* ╤ */ + {"\342\225\247", ACS_BTEE | A_BOLD}, /* ╧ */ + {"\342\225\220", ACS_HLINE | A_BOLD}, /* ═ */ + {"\342\225\221", ACS_VLINE | A_BOLD}, /* ║ */ + + {NULL, 0} + }; + + if (ch == NULL) + return (int) ' '; + + for (res = 0; lines_codes[res].line; res++) + { + if (strcmp (ch, lines_codes[res].line) == 0) + return lines_codes[res].line_code; + } + + str2 = mc_tty_normalize_from_utf8 (ch); + res = g_utf8_get_char_validated (str2, -1); + + if (res < 0) + res = (unsigned char) str2[0]; + g_free (str2); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_init (gboolean mouse_enable, gboolean is_xterm) +{ + struct termios mode; + + initscr (); + +#ifdef HAVE_ESCDELAY + /* + * If ncurses exports the ESCDELAY variable, it should be set to + * a low value, or you'll experience a delay in processing escape + * sequences that are recognized by mc (e.g. Esc-Esc). On the other + * hand, making ESCDELAY too small can result in some sequences + * (e.g. cursor arrows) being reported as separate keys under heavy + * processor load, and this can be a problem if mc hasn't learned + * them in the "Learn Keys" dialog. The value is in milliseconds. + */ + ESCDELAY = 200; +#endif /* HAVE_ESCDELAY */ + + tcgetattr (STDIN_FILENO, &mode); + /* use Ctrl-g to generate SIGINT */ + mode.c_cc[VINTR] = CTRL ('g'); /* ^g */ + /* disable SIGQUIT to allow use Ctrl-\ key */ + mode.c_cc[VQUIT] = NULL_VALUE; + tcsetattr (STDIN_FILENO, TCSANOW, &mode); + + /* curses remembers the "in-program" modes after this call */ + def_prog_mode (); + + tty_start_interrupt_key (); + + if (!mouse_enable) + use_mouse_p = MOUSE_DISABLED; + tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */ + tty_enter_ca_mode (); + tty_raw_mode (); + noecho (); + keypad (stdscr, TRUE); + nodelay (stdscr, FALSE); + + tty_setup_sigwinch (sigwinch_handler); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_shutdown (void) +{ + tty_destroy_winch_pipe (); + tty_reset_shell_mode (); + tty_noraw_mode (); + tty_keypad (FALSE); + tty_reset_screen (); + tty_exit_ca_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_enter_ca_mode (void) +{ + if (mc_global.tty.xterm_flag && smcup != NULL) + { + fprintf (stdout, /* ESC_STR ")0" */ ESC_STR "7" ESC_STR "[?47h"); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_exit_ca_mode (void) +{ + if (mc_global.tty.xterm_flag && rmcup != NULL) + { + fprintf (stdout, ESC_STR "[?47l" ESC_STR "8" ESC_STR "[m"); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_change_screen_size (void) +{ +#if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4 + struct winsize winsz; + + winsz.ws_col = winsz.ws_row = 0; + +#ifndef NCURSES_VERSION + tty_noraw_mode (); + tty_reset_screen (); +#endif + + /* Ioctl on the STDIN_FILENO */ + ioctl (fileno (stdout), TIOCGWINSZ, &winsz); + if (winsz.ws_col != 0 && winsz.ws_row != 0) + { +#if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM) + resizeterm (winsz.ws_row, winsz.ws_col); + clearok (stdscr, TRUE); /* sigwinch's should use a semaphore! */ +#else + COLS = winsz.ws_col; + LINES = winsz.ws_row; +#endif + } +#endif /* defined(TIOCGWINSZ) || NCURSES_VERSION_MAJOR >= 4 */ + +#ifdef ENABLE_SUBSHELL + if (mc_global.tty.use_subshell) + tty_resize (mc_global.tty.subshell_pty); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_reset_prog_mode (void) +{ + reset_prog_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_reset_shell_mode (void) +{ + reset_shell_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_raw_mode (void) +{ + raw (); /* FIXME: unneeded? */ + cbreak (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noraw_mode (void) +{ + nocbreak (); /* FIXME: unneeded? */ + noraw (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noecho (void) +{ + noecho (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_flush_input (void) +{ + return flushinp (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_keypad (gboolean set) +{ + keypad (stdscr, (bool) set); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_nodelay (gboolean set) +{ + nodelay (stdscr, (bool) set); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_baudrate (void) +{ + return baudrate (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_lowlevel_getch (void) +{ + return getch (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_reset_screen (void) +{ + return endwin (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_touch_screen (void) +{ + touchwin (stdscr); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_gotoyx (int y, int x) +{ + mc_curs_row = y; + mc_curs_col = x; + + if (y < 0) + y = 0; + if (y >= LINES) + y = LINES - 1; + + if (x < 0) + x = 0; + if (x >= COLS) + x = COLS - 1; + + move (y, x); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_getyx (int *py, int *px) +{ + *py = mc_curs_row; + *px = mc_curs_col; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_hline (int y, int x, int ch, int len) +{ + int x1; + + if (y < 0 || y >= LINES || x >= COLS) + return; + + x1 = x; + + if (x < 0) + { + len += x; + if (len <= 0) + return; + x = 0; + } + + if ((chtype) ch == ACS_HLINE) + ch = mc_tty_frm[MC_TTY_FRM_HORIZ]; + + move (y, x); + hline (ch, len); + move (y, x1); + + mc_curs_row = y; + mc_curs_col = x1; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_vline (int y, int x, int ch, int len) +{ + int y1; + + if (x < 0 || x >= COLS || y >= LINES) + return; + + y1 = y; + + if (y < 0) + { + len += y; + if (len <= 0) + return; + y = 0; + } + + if ((chtype) ch == ACS_VLINE) + ch = mc_tty_frm[MC_TTY_FRM_VERT]; + + move (y, x); + vline (ch, len); + move (y1, x); + + mc_curs_row = y1; + mc_curs_col = x; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_fill_region (int y, int x, int rows, int cols, unsigned char ch) +{ + int i; + + if (!tty_clip (&y, &x, &rows, &cols)) + return; + + for (i = 0; i < rows; i++) + { + move (y + i, x); + hline (ch, cols); + } + + move (y, x); + + mc_curs_row = y; + mc_curs_col = x; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_colorize_area (int y, int x, int rows, int cols, int color) +{ +#ifdef ENABLE_SHADOWS + cchar_t *ctext; + wchar_t wch[10]; /* TODO not sure if the length is correct */ + attr_t attrs; + short color_pair; + + if (!use_colors || !tty_clip (&y, &x, &rows, &cols)) + return; + + tty_setcolor (color); + ctext = g_malloc (sizeof (cchar_t) * (cols + 1)); + + for (int row = 0; row < rows; row++) + { + mvin_wchnstr (y + row, x, ctext, cols); + + for (int col = 0; col < cols; col++) + { + getcchar (&ctext[col], wch, &attrs, &color_pair, NULL); + setcchar (&ctext[col], wch, attrs, color, NULL); + } + + mvadd_wchnstr (y + row, x, ctext, cols); + } + + g_free (ctext); +#else + (void) y; + (void) x; + (void) rows; + (void) cols; + (void) color; +#endif /* ENABLE_SHADOWS */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_set_alt_charset (gboolean alt_charset) +{ + (void) alt_charset; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_display_8bit (gboolean what) +{ + meta (stdscr, (int) what); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_char (int c) +{ + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch (c); + mc_curs_col++; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_anychar (int c) +{ + if (mc_global.utf8_display || c > 255) + { + int res; + unsigned char str[UTF8_CHAR_LEN + 1]; + + res = g_unichar_to_utf8 (c, (char *) str); + if (res == 0) + { + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch ('.'); + mc_curs_col++; + } + else + { + const char *s; + + str[res] = '\0'; + s = str_term_form ((char *) str); + + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addstr (s); + + if (g_unichar_iswide (c)) + mc_curs_col += 2; + else if (!g_unichar_iszerowidth (c)) + mc_curs_col++; + } + } + else + { + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch (c); + mc_curs_col++; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_alt_char (int c, gboolean single) +{ + if (yx_in_screen (mc_curs_row, mc_curs_col)) + { + if ((chtype) c == ACS_VLINE) + c = mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]; + else if ((chtype) c == ACS_HLINE) + c = mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]; + else if ((chtype) c == ACS_LTEE) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]; + else if ((chtype) c == ACS_RTEE) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]; + else if ((chtype) c == ACS_ULCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]; + else if ((chtype) c == ACS_LLCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]; + else if ((chtype) c == ACS_URCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]; + else if ((chtype) c == ACS_LRCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]; + else if ((chtype) c == ACS_PLUS) + c = mc_tty_frm[MC_TTY_FRM_CROSS]; + + addch (c); + } + + mc_curs_col++; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_string (const char *s) +{ + int len; + int start = 0; + + s = str_term_form (s); + len = str_term_width1 (s); + + /* line is upper or below the screen or entire line is before or after screen */ + if (mc_curs_row < 0 || mc_curs_row >= LINES || mc_curs_col + len <= 0 || mc_curs_col >= COLS) + { + mc_curs_col += len; + return; + } + + /* skip invisible left part */ + if (mc_curs_col < 0) + { + start = -mc_curs_col; + len += mc_curs_col; + mc_curs_col = 0; + } + + mc_curs_col += len; + if (mc_curs_col >= COLS) + len = COLS - (mc_curs_col - len); + + addstr (str_term_substring (s, start, len)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_printf (const char *fmt, ...) +{ + va_list args; + char buf[BUF_1K]; /* FIXME: is it enough? */ + + va_start (args, fmt); + g_vsnprintf (buf, sizeof (buf), fmt, args); + va_end (args); + tty_print_string (buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +tty_tgetstr (const char *cap) +{ + char *unused = NULL; + + return tgetstr ((NCURSES_CONST char *) cap, &unused); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_refresh (void) +{ + refresh (); + doupdate (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_beep (void) +{ + beep (); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/tty-ncurses.h b/lib/tty/tty-ncurses.h new file mode 100644 index 0000000..8feb17c --- /dev/null +++ b/lib/tty/tty-ncurses.h @@ -0,0 +1,50 @@ + +#ifndef MC__TTY_NCURSES_H +#define MC__TTY_NCURSES_H + +/* for cchar_t, getcchar(), setcchar() */ +#ifndef _XOPEN_SOURCE_EXTENDED +#define _XOPEN_SOURCE_EXTENDED +#endif + +#ifdef USE_NCURSES +#ifdef HAVE_NCURSES_CURSES_H +#include <ncurses/curses.h> +#elif defined (HAVE_NCURSES_NCURSES_H) +#include <ncurses/ncurses.h> +#elif defined (HAVE_NCURSESW_CURSES_H) +#include <ncursesw/curses.h> +#elif defined (HAVE_NCURSES_HCURSES_H) || defined (HAVE_NCURSES_H) +#include <ncurses.h> +#else +#include <curses.h> +#endif +#endif /* USE_NCURSES */ + +#ifdef USE_NCURSESW +#include <ncursesw/curses.h> +#endif /* USE_NCURSESW */ + +/* netbsd-libcurses doesn't define NCURSES_CONST */ +#ifndef NCURSES_CONST +#define NCURSES_CONST const +#endif + +/* do not draw shadows if NCurses is built with --disable-widec */ +#if defined(NCURSES_WIDECHAR) && NCURSES_WIDECHAR +#define ENABLE_SHADOWS 1 +#endif + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +/*** inline functions ****************************************************************************/ + +#endif /* MC_TTY_NCURSES_H */ diff --git a/lib/tty/tty-slang.c b/lib/tty/tty-slang.c new file mode 100644 index 0000000..3aa74de --- /dev/null +++ b/lib/tty/tty-slang.c @@ -0,0 +1,781 @@ +/* + Interface to the terminal controlling library. + Slang wrapper. + + Copyright (C) 2005-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009 + Egmont Koblinger <egmont@gmail.com>, 2010 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: S-Lang-based tty layer of Midnight Commander + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> /* size_t */ +#include <unistd.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#include <termios.h> + +#include "lib/global.h" +#include "lib/strutil.h" /* str_term_form */ +#include "lib/util.h" /* is_printable() */ + +#include "tty-internal.h" /* mc_tty_normalize_from_utf8() */ +#include "tty.h" +#include "color.h" +#include "color-slang.h" +#include "color-internal.h" +#include "mouse.h" /* Gpm_Event is required in key.h */ +#include "key.h" /* define_sequence */ +#include "win.h" + + +/*** global variables ****************************************************************************/ + +/* If true program softkeys (HP terminals only) on startup and after every + command ran in the subshell to the description found in the termcap/terminfo + database */ +int reset_hp_softkeys = 0; + +/*** file scope macro definitions ****************************************************************/ + +#ifndef SLTT_MAX_SCREEN_COLS +#define SLTT_MAX_SCREEN_COLS 512 +#endif + +#ifndef SLTT_MAX_SCREEN_ROWS +#define SLTT_MAX_SCREEN_ROWS 512 +#endif + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* Various saved termios settings that we control here */ +static struct termios boot_mode; +static struct termios new_mode; + +/* Controls whether we should wait for input in tty_lowlevel_getch */ +static gboolean no_slang_delay; + +static gboolean slsmg_active = FALSE; + +/* This table describes which capabilities we want and which values we + * assign to them. + */ +static const struct +{ + int key_code; + const char *key_name; +} key_table[] = +{ + /* *INDENT-OFF* */ + { KEY_F (0), "k0" }, + { KEY_F (1), "k1" }, + { KEY_F (2), "k2" }, + { KEY_F (3), "k3" }, + { KEY_F (4), "k4" }, + { KEY_F (5), "k5" }, + { KEY_F (6), "k6" }, + { KEY_F (7), "k7" }, + { KEY_F (8), "k8" }, + { KEY_F (9), "k9" }, + { KEY_F (10), "k;" }, + { KEY_F (11), "F1" }, + { KEY_F (12), "F2" }, + { KEY_F (13), "F3" }, + { KEY_F (14), "F4" }, + { KEY_F (15), "F5" }, + { KEY_F (16), "F6" }, + { KEY_F (17), "F7" }, + { KEY_F (18), "F8" }, + { KEY_F (19), "F9" }, + { KEY_F (20), "FA" }, + { KEY_IC, "kI" }, + { KEY_NPAGE, "kN" }, + { KEY_PPAGE, "kP" }, + { KEY_LEFT, "kl" }, + { KEY_RIGHT, "kr" }, + { KEY_UP, "ku" }, + { KEY_DOWN, "kd" }, + { KEY_DC, "kD" }, + { KEY_BACKSPACE, "kb" }, + { KEY_HOME, "kh" }, + { KEY_END, "@7" }, + { 0, NULL } + /* *INDENT-ON* */ +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +tty_setup_sigwinch (void (*handler) (int)) +{ + (void) SLsignal (SIGWINCH, handler); + tty_create_winch_pipe (); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sigwinch_handler (int dummy) +{ + ssize_t n = 0; + + (void) dummy; + + n = write (sigwinch_pipe[1], "", 1); + (void) n; + + (void) SLsignal (SIGWINCH, sigwinch_handler); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys. + elm 2.4pl15 invoked with the -K option utilizes these softkeys and the + consequence is that function keys don't work in MC sometimes... + Unfortunately I don't now the one and only escape sequence to turn off. + softkeys (elm uses three different capabilities to turn on softkeys and two. + capabilities to turn them off).. + Among other things elm uses the pair we already use in slang_keypad. That's. + the reason why I call slang_reset_softkeys from slang_keypad. In lack of + something better the softkeys are programmed to their defaults from the + termcap/terminfo database. + The escape sequence to program the softkeys is taken from elm and it is. + hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this. + sequence. -- Norbert + */ + +static void +slang_reset_softkeys (void) +{ + int key; + static const char display[] = " "; + char tmp[BUF_SMALL]; + + for (key = 1; key < 9; key++) + { + char *send; + + g_snprintf (tmp, sizeof (tmp), "k%d", key); + send = SLtt_tgetstr (tmp); + if (send != NULL) + { + g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key, + (int) (sizeof (display) - 1), (int) strlen (send), display, send); + SLtt_write_string (tmp); + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +do_define_key (int code, const char *strcap) +{ + char *seq; + + seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap); + if (seq != NULL) + define_sequence (code, seq, MCKEY_NOACTION); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +load_terminfo_keys (void) +{ + int i; + + for (i = 0; key_table[i].key_code; i++) + do_define_key (key_table[i].key_code, key_table[i].key_name); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +int +mc_tty_normalize_lines_char (const char *str) +{ + char *str2; + int res; + + struct mc_tty_lines_struct + { + const char *line; + int line_code; + } const lines_codes[] = { + {"\342\224\214", SLSMG_ULCORN_CHAR}, + {"\342\224\220", SLSMG_URCORN_CHAR}, + {"\342\224\224", SLSMG_LLCORN_CHAR}, + {"\342\224\230", SLSMG_LRCORN_CHAR}, + {"\342\224\234", SLSMG_LTEE_CHAR}, + {"\342\224\244", SLSMG_RTEE_CHAR}, + {"\342\224\254", SLSMG_UTEE_CHAR}, + {"\342\224\264", SLSMG_DTEE_CHAR}, + {"\342\224\200", SLSMG_HLINE_CHAR}, + {"\342\224\202", SLSMG_VLINE_CHAR}, + {"\342\224\274", SLSMG_PLUS_CHAR}, + + {NULL, 0} + }; + + if (!str) + return (int) ' '; + + for (res = 0; lines_codes[res].line; res++) + { + if (strcmp (str, lines_codes[res].line) == 0) + return lines_codes[res].line_code; + } + + str2 = mc_tty_normalize_from_utf8 (str); + res = g_utf8_get_char_validated (str2, -1); + + if (res < 0) + res = (unsigned char) str2[0]; + g_free (str2); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_init (gboolean mouse_enable, gboolean is_xterm) +{ + SLtt_Ignore_Beep = 1; + + SLutf8_enable (-1); /* has to be called first before any of the other functions. */ + SLtt_get_terminfo (); + /* + * If the terminal in not in terminfo but begins with a well-known + * string such as "linux" or "xterm" S-Lang will go on, but the + * terminal size and several other variables won't be initialized + * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely + * small screen dimensions. + */ + if ((COLS < 10) || (LINES < 5) +#if SLANG_VERSION < 20303 + /* Beginning from pre2.3.3-8 (55f58798c267d76a1b93d0d916027b71a10ac1ee), + these limitations were eliminated. */ + || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS) +#endif + ) + { + fprintf (stderr, + _("Screen size %dx%d is not supported.\n" + "Check the TERM environment variable.\n"), COLS, LINES); + exit (EXIT_FAILURE); + } + + tcgetattr (fileno (stdin), &boot_mode); + /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */ + SLang_init_tty (XCTRL ('g'), 1, 0); + + if (mc_global.tty.ugly_line_drawing) + SLtt_Has_Alt_Charset = 0; + + tcgetattr (SLang_TT_Read_FD, &new_mode); + + tty_reset_prog_mode (); + load_terminfo_keys (); + + SLtt_Blink_Mode = (tty_use_256colors (NULL) || tty_use_truecolors (NULL)) ? 1 : 0; + + tty_start_interrupt_key (); + + /* It's the small part from the previous init_key() */ + init_key_input_fd (); + + /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang + * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display + * detected - but checking TERM would fail under screen, OR running xterm + * with allowC1Printable). + */ + tty_display_8bit (FALSE); + + SLsmg_init_smg (); + slsmg_active = TRUE; + if (!mouse_enable) + use_mouse_p = MOUSE_DISABLED; + tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */ + tty_enter_ca_mode (); + tty_keypad (TRUE); + tty_nodelay (FALSE); + + tty_setup_sigwinch (sigwinch_handler); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_shutdown (void) +{ + char *op_cap; + + tty_destroy_winch_pipe (); + tty_reset_shell_mode (); + tty_noraw_mode (); + tty_keypad (FALSE); + tty_reset_screen (); + tty_exit_ca_mode (); + SLang_reset_tty (); + slsmg_active = FALSE; + + /* Load the op capability to reset the colors to those that were + * active when the program was started up + */ + op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op"); + if (op_cap != NULL) + { + fputs (op_cap, stdout); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_enter_ca_mode (void) +{ + /* S-Lang handles alternate screen switching and cursor position saving */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_exit_ca_mode (void) +{ + /* S-Lang handles alternate screen switching and cursor position restoring */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_change_screen_size (void) +{ + SLtt_get_screen_size (); + if (slsmg_active) + SLsmg_reinit_smg (); + +#ifdef ENABLE_SUBSHELL + if (mc_global.tty.use_subshell) + tty_resize (mc_global.tty.subshell_pty); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ +/* Done each time we come back from done mode */ + +void +tty_reset_prog_mode (void) +{ + tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode); + SLsmg_init_smg (); + slsmg_active = TRUE; + SLsmg_touch_lines (0, LINES); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Called each time we want to shutdown slang screen manager */ + +void +tty_reset_shell_mode (void) +{ + tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_raw_mode (void) +{ + tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noraw_mode (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noecho (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_flush_input (void) +{ + return 0; /* OK */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_keypad (gboolean set) +{ + char *keypad_string; + + keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke")); + if (keypad_string != NULL) + SLtt_write_string (keypad_string); + if (set && reset_hp_softkeys) + slang_reset_softkeys (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_nodelay (gboolean set) +{ + no_slang_delay = set; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_baudrate (void) +{ + return SLang_TT_Baud_Rate; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_lowlevel_getch (void) +{ + int c; + + if (no_slang_delay && (SLang_input_pending (0) == 0)) + return -1; + + c = SLang_getkey (); + if (c == SLANG_GETKEY_ERROR) + { + fprintf (stderr, + "SLang_getkey returned SLANG_GETKEY_ERROR\n" + "Assuming EOF on stdin and exiting\n"); + exit (EXIT_FAILURE); + } + + return c; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_reset_screen (void) +{ + SLsmg_reset_smg (); + slsmg_active = FALSE; + return 0; /* OK */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_touch_screen (void) +{ + SLsmg_touch_lines (0, LINES); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_gotoyx (int y, int x) +{ + SLsmg_gotorc (y, x); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_getyx (int *py, int *px) +{ + *py = SLsmg_get_row (); + *px = SLsmg_get_column (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_hline (int y, int x, int ch, int len) +{ + int x1; + + if (y < 0 || y >= LINES || x >= COLS) + return; + + x1 = x; + + if (x < 0) + { + len += x; + if (len <= 0) + return; + x = 0; + } + + if (ch == ACS_HLINE) + ch = mc_tty_frm[MC_TTY_FRM_HORIZ]; + if (ch == 0) + ch = ACS_HLINE; + + SLsmg_gotorc (y, x); + + if (ch == ACS_HLINE) + SLsmg_draw_hline (len); + else + while (len-- != 0) + tty_print_char (ch); + + SLsmg_gotorc (y, x1); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_vline (int y, int x, int ch, int len) +{ + int y1; + + if (x < 0 || x >= COLS || y >= LINES) + return; + + y1 = y; + + if (y < 0) + { + len += y; + if (len <= 0) + return; + y = 0; + } + + if (ch == ACS_VLINE) + ch = mc_tty_frm[MC_TTY_FRM_VERT]; + if (ch == 0) + ch = ACS_VLINE; + + SLsmg_gotorc (y, x); + + if (ch == ACS_VLINE) + SLsmg_draw_vline (len); + else + { + int pos = 0; + + while (len-- != 0) + { + SLsmg_gotorc (y + pos, x); + tty_print_char (ch); + pos++; + } + } + + SLsmg_gotorc (y1, x); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_fill_region (int y, int x, int rows, int cols, unsigned char ch) +{ + SLsmg_fill_region (y, x, rows, cols, ch); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_colorize_area (int y, int x, int rows, int cols, int color) +{ + if (use_colors) + SLsmg_set_color_in_region (color, y, x, rows, cols); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_set_alt_charset (gboolean alt_charset) +{ + SLsmg_set_char_set ((int) alt_charset); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_display_8bit (gboolean what) +{ + SLsmg_Display_Eight_Bit = what ? 128 : 160; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_char (int c) +{ + SLsmg_write_char ((SLwchar_Type) ((unsigned int) c)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_alt_char (int c, gboolean single) +{ +#define DRAW(x, y) (x == y) \ + ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \ + : SLsmg_write_char ((unsigned int) y) + switch (c) + { + case ACS_VLINE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]); + break; + case ACS_HLINE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]); + break; + case ACS_LTEE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]); + break; + case ACS_RTEE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]); + break; + case ACS_TTEE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]); + break; + case ACS_BTEE: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]); + break; + case ACS_ULCORNER: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]); + break; + case ACS_LLCORNER: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]); + break; + case ACS_URCORNER: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]); + break; + case ACS_LRCORNER: + DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]); + break; + case ACS_PLUS: + DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]); + break; + default: + SLsmg_write_char ((unsigned int) c); + } +#undef DRAW +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_anychar (int c) +{ + if (c > 255) + { + char str[UTF8_CHAR_LEN + 1]; + int res; + + res = g_unichar_to_utf8 (c, str); + if (res == 0) + { + str[0] = '.'; + str[1] = '\0'; + } + else + { + str[res] = '\0'; + } + SLsmg_write_string ((char *) str_term_form (str)); + } + else + { + if (!is_printable (c)) + c = '.'; + SLsmg_write_char ((SLwchar_Type) ((unsigned int) c)); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_string (const char *s) +{ + SLsmg_write_string ((char *) str_term_form (s)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_printf (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + SLsmg_vprintf ((char *) fmt, args); + va_end (args); +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +tty_tgetstr (const char *cap) +{ + return SLtt_tgetstr ((SLFUTURE_CONST char *) cap); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_refresh (void) +{ + SLsmg_refresh (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_beep (void) +{ + SLtt_beep (); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/tty-slang.h b/lib/tty/tty-slang.h new file mode 100644 index 0000000..eeaade3 --- /dev/null +++ b/lib/tty/tty-slang.h @@ -0,0 +1,48 @@ + +#ifndef MC__TTY_SLANG_H +#define MC__TTY_SLANG_H + +#include <slang.h> + +/*** typedefs(not structures) and defined constants **********************************************/ + +#define KEY_F(x) (1000 + x) + +#define ACS_VLINE SLSMG_VLINE_CHAR +#define ACS_HLINE SLSMG_HLINE_CHAR +#define ACS_LTEE SLSMG_LTEE_CHAR +#define ACS_RTEE SLSMG_RTEE_CHAR +#define ACS_TTEE SLSMG_UTEE_CHAR +#define ACS_BTEE SLSMG_DTEE_CHAR +#define ACS_ULCORNER SLSMG_ULCORN_CHAR +#define ACS_LLCORNER SLSMG_LLCORN_CHAR +#define ACS_URCORNER SLSMG_URCORN_CHAR +#define ACS_LRCORNER SLSMG_LRCORN_CHAR +#define ACS_PLUS SLSMG_PLUS_CHAR + +#define COLS SLtt_Screen_Cols +#define LINES SLtt_Screen_Rows + +#define ENABLE_SHADOWS 1 + +/*** enums ***************************************************************************************/ + +enum +{ + KEY_BACKSPACE = 400, + KEY_END, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, + KEY_HOME, KEY_A1, KEY_C1, KEY_NPAGE, KEY_PPAGE, KEY_IC, + KEY_ENTER, KEY_DC, KEY_SCANCEL, KEY_BTAB +}; + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +extern int reset_hp_softkeys; + +/*** declarations of public functions ************************************************************/ + +/*** inline functions ****************************************************************************/ + +#endif /* MC_TTY_SLANG_H */ diff --git a/lib/tty/tty.c b/lib/tty/tty.c new file mode 100644 index 0000000..cae0a05 --- /dev/null +++ b/lib/tty/tty.c @@ -0,0 +1,416 @@ +/* + Interface to the terminal controlling library. + + Copyright (C) 2005-2023 + Free Software Foundation, Inc. + + Written by: + Roland Illig <roland.illig@gmx.de>, 2005. + Andrew Borodin <aborodin@vmail.ru>, 2009. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file tty.c + * \brief Source: %interface to the terminal controlling library + */ + +#include <config.h> + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> /* memset() */ + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#endif +#include <unistd.h> /* exit() */ + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +/* In some systems (like Solaris 11.4 SPARC), TIOCSWINSZ is defined in termios.h */ +#include <termios.h> + +#include "lib/global.h" +#include "lib/strutil.h" + +#include "tty.h" +#include "tty-internal.h" +#include "color.h" /* tty_set_normal_attrs() */ +#include "mouse.h" /* use_mouse_p */ +#include "win.h" + +/*** global variables ****************************************************************************/ + +int mc_tty_frm[MC_TTY_FRM_MAX]; + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static SIG_ATOMIC_VOLATILE_T got_interrupt = 0; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +sigintr_handler (int signo) +{ + (void) &signo; + got_interrupt = 1; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/** + * Check terminal type. If $TERM is not set or value is empty, mc finishes with EXIT_FAILURE. + * + * @param force_xterm Set forced the XTerm type + * + * @return true if @param force_xterm is true or value of $TERM is one of following: + * term* + * konsole* + * rxvt* + * Eterm + * dtterm + * alacritty* + * foot* + * screen* + * tmux* + * contour* + */ +gboolean +tty_check_term (gboolean force_xterm) +{ + const char *termvalue; + + termvalue = getenv ("TERM"); + if (termvalue == NULL || *termvalue == '\0') + { + fputs (_("The TERM environment variable is unset!\n"), stderr); + exit (EXIT_FAILURE); + } + + /* *INDENT-OFF* */ + return force_xterm + || strncmp (termvalue, "xterm", 5) == 0 + || strncmp (termvalue, "konsole", 7) == 0 + || strncmp (termvalue, "rxvt", 4) == 0 + || strcmp (termvalue, "Eterm") == 0 + || strcmp (termvalue, "dtterm") == 0 + || strncmp (termvalue, "alacritty", 9) == 0 + || strncmp (termvalue, "foot", 4) == 0 + || strncmp (termvalue, "screen", 6) == 0 + || strncmp (termvalue, "tmux", 4) == 0 + || strncmp (termvalue, "contour", 7) == 0; + /* *INDENT-ON* */ +} + +/* --------------------------------------------------------------------------------------------- */ + +extern void +tty_start_interrupt_key (void) +{ + struct sigaction act; + + memset (&act, 0, sizeof (act)); + act.sa_handler = sigintr_handler; + sigemptyset (&act.sa_mask); +#ifdef SA_RESTART + act.sa_flags = SA_RESTART; +#endif /* SA_RESTART */ + sigaction (SIGINT, &act, NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +extern void +tty_enable_interrupt_key (void) +{ + struct sigaction act; + + memset (&act, 0, sizeof (act)); + act.sa_handler = sigintr_handler; + sigemptyset (&act.sa_mask); + sigaction (SIGINT, &act, NULL); + got_interrupt = 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +extern void +tty_disable_interrupt_key (void) +{ + struct sigaction act; + + memset (&act, 0, sizeof (act)); + act.sa_handler = SIG_IGN; + sigemptyset (&act.sa_mask); + sigaction (SIGINT, &act, NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +extern gboolean +tty_got_interrupt (void) +{ + gboolean rv; + + rv = (got_interrupt != 0); + got_interrupt = 0; + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tty_got_winch (void) +{ + fd_set fdset; + /* *INDENT-OFF* */ + /* instant timeout */ + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + /* *INDENT-ON* */ + int ok; + + FD_ZERO (&fdset); + FD_SET (sigwinch_pipe[0], &fdset); + + while ((ok = select (sigwinch_pipe[0] + 1, &fdset, NULL, NULL, &timeout)) < 0) + if (errno != EINTR) + { + perror (_("Cannot check SIGWINCH pipe")); + exit (EXIT_FAILURE); + } + + return (ok != 0 && FD_ISSET (sigwinch_pipe[0], &fdset)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_flush_winch (void) +{ + ssize_t n; + + /* merge all SIGWINCH events raised to this moment */ + do + { + char x[16]; + + /* read multiple events at a time */ + n = read (sigwinch_pipe[0], &x, sizeof (x)); + } + while (n > 0 || (n == -1 && errno == EINTR)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_one_hline (gboolean single) +{ + tty_print_alt_char (ACS_HLINE, single); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_one_vline (gboolean single) +{ + tty_print_alt_char (ACS_VLINE, single); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_box (int y, int x, int ys, int xs, gboolean single) +{ + int y2, x2; + + if (ys <= 0 || xs <= 0) + return; + + ys--; + xs--; + + y2 = y + ys; + x2 = x + xs; + + tty_draw_vline (y, x, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys); + tty_draw_vline (y, x2, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys); + tty_draw_hline (y, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs); + tty_draw_hline (y2, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs); + tty_gotoyx (y, x); + tty_print_alt_char (ACS_ULCORNER, single); + tty_gotoyx (y2, x); + tty_print_alt_char (ACS_LLCORNER, single); + tty_gotoyx (y, x2); + tty_print_alt_char (ACS_URCORNER, single); + tty_gotoyx (y2, x2); + tty_print_alt_char (ACS_LRCORNER, single); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_box_shadow (int y, int x, int rows, int cols, int shadow_color) +{ + /* draw right shadow */ + tty_colorize_area (y + 1, x + cols, rows - 1, 2, shadow_color); + /* draw bottom shadow */ + tty_colorize_area (y + rows, x + 2, 1, cols, shadow_color); +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +mc_tty_normalize_from_utf8 (const char *str) +{ + GIConv conv; + GString *buffer; + const char *_system_codepage = str_detect_termencoding (); + + if (str_isutf8 (_system_codepage)) + return g_strdup (str); + + conv = g_iconv_open (_system_codepage, "UTF-8"); + if (conv == INVALID_CONV) + return g_strdup (str); + + buffer = g_string_new (""); + + if (str_convert (conv, str, buffer) == ESTR_FAILURE) + { + g_string_free (buffer, TRUE); + str_close_conv (conv); + return g_strdup (str); + } + str_close_conv (conv); + + return g_string_free (buffer, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** Resize given terminal using TIOCSWINSZ, return ioctl() result */ +int +tty_resize (int fd) +{ +#if defined TIOCSWINSZ + struct winsize tty_size; + + tty_size.ws_row = LINES; + tty_size.ws_col = COLS; + tty_size.ws_xpixel = tty_size.ws_ypixel = 0; + + return ioctl (fd, TIOCSWINSZ, &tty_size); +#else + return 0; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +/** Clear screen */ +void +tty_clear_screen (void) +{ + tty_set_normal_attrs (); + tty_fill_region (0, 0, LINES, COLS, ' '); + tty_refresh (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_init_xterm_support (gboolean is_xterm) +{ + const char *termvalue; + + termvalue = getenv ("TERM"); + + /* Check mouse and ca capabilities */ + /* terminfo/termcap structures have been already initialized, + in slang_init() or/and init_curses() */ + /* Check terminfo at first, then check termcap */ + xmouse_seq = tty_tgetstr ("kmous"); + if (xmouse_seq == NULL) + xmouse_seq = tty_tgetstr ("Km"); + smcup = tty_tgetstr ("smcup"); + if (smcup == NULL) + smcup = tty_tgetstr ("ti"); + rmcup = tty_tgetstr ("rmcup"); + if (rmcup == NULL) + rmcup = tty_tgetstr ("te"); + + if (strcmp (termvalue, "cygwin") == 0) + { + is_xterm = TRUE; + use_mouse_p = MOUSE_DISABLED; + } + + if (is_xterm) + { + /* Default to the standard xterm sequence */ + if (xmouse_seq == NULL) + xmouse_seq = ESC_STR "[M"; + + /* Enable mouse unless explicitly disabled by --nomouse */ + if (use_mouse_p != MOUSE_DISABLED) + { + if (mc_global.tty.old_mouse) + use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING; + else + { + /* FIXME: this dirty hack to set supported type of tracking the mouse */ + const char *color_term = getenv ("COLORTERM"); + if (strncmp (termvalue, "rxvt", 4) == 0 || + (color_term != NULL && strncmp (color_term, "rxvt", 4) == 0) || + strcmp (termvalue, "Eterm") == 0) + use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING; + else + use_mouse_p = MOUSE_XTERM_BUTTON_EVENT_TRACKING; + } + } + } + + /* There's only one termcap entry "kmous", typically containing "\E[M" or "\E[<". + * We need the former in xmouse_seq, the latter in xmouse_extended_seq. + * See tickets 2956, 3954, and 4063 for details. */ + if (xmouse_seq != NULL) + { + if (strcmp (xmouse_seq, ESC_STR "[<") == 0) + xmouse_seq = ESC_STR "[M"; + + xmouse_extended_seq = ESC_STR "[<"; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/tty.h b/lib/tty/tty.h new file mode 100644 index 0000000..90cbbc6 --- /dev/null +++ b/lib/tty/tty.h @@ -0,0 +1,146 @@ + +/** \file tty.h + * \brief Header: %interface to the terminal controlling library + * + * This file is the %interface to the terminal controlling library: + * slang or ncurses. It provides an additional layer of abstraction + * above the "real" libraries to keep the number of ifdefs in the other + * files small. + */ + +#ifndef MC__TTY_H +#define MC__TTY_H + +#include "lib/global.h" /* include <glib.h> */ + +#ifdef HAVE_SLANG +#include "tty-slang.h" +#else +#include "tty-ncurses.h" +#endif + +/*** typedefs(not structures) and defined constants **********************************************/ + +#define KEY_KP_ADD 4001 +#define KEY_KP_SUBTRACT 4002 +#define KEY_KP_MULTIPLY 4003 + +/*** enums ***************************************************************************************/ + +typedef enum +{ + /* single lines */ + MC_TTY_FRM_VERT, + MC_TTY_FRM_HORIZ, + MC_TTY_FRM_LEFTTOP, + MC_TTY_FRM_RIGHTTOP, + MC_TTY_FRM_LEFTBOTTOM, + MC_TTY_FRM_RIGHTBOTTOM, + MC_TTY_FRM_TOPMIDDLE, + MC_TTY_FRM_BOTTOMMIDDLE, + MC_TTY_FRM_LEFTMIDDLE, + MC_TTY_FRM_RIGHTMIDDLE, + MC_TTY_FRM_CROSS, + + /* double lines */ + MC_TTY_FRM_DVERT, + MC_TTY_FRM_DHORIZ, + MC_TTY_FRM_DLEFTTOP, + MC_TTY_FRM_DRIGHTTOP, + MC_TTY_FRM_DLEFTBOTTOM, + MC_TTY_FRM_DRIGHTBOTTOM, + MC_TTY_FRM_DTOPMIDDLE, + MC_TTY_FRM_DBOTTOMMIDDLE, + MC_TTY_FRM_DLEFTMIDDLE, + MC_TTY_FRM_DRIGHTMIDDLE, + + MC_TTY_FRM_MAX +} mc_tty_frm_t; + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +extern int mc_tty_frm[]; + +extern char *tty_tgetstr (const char *name); + +/*** declarations of public functions ************************************************************/ + +extern void tty_beep (void); + +/* {{{ Input }}} */ + +extern gboolean tty_check_term (gboolean force_xterm); +extern void tty_init (gboolean mouse_enable, gboolean is_xterm); +extern void tty_shutdown (void); + +extern void tty_start_interrupt_key (void); +extern void tty_enable_interrupt_key (void); +extern void tty_disable_interrupt_key (void); +extern gboolean tty_got_interrupt (void); + +extern gboolean tty_got_winch (void); +extern void tty_flush_winch (void); + +extern void tty_reset_prog_mode (void); +extern void tty_reset_shell_mode (void); + +extern void tty_raw_mode (void); +extern void tty_noraw_mode (void); + +extern void tty_noecho (void); +extern int tty_flush_input (void); + +extern void tty_keypad (gboolean set); +extern void tty_nodelay (gboolean set); +extern int tty_baudrate (void); + +/* {{{ Output }}} */ + +/* + The output functions do not check themselves for screen overflows, + so make sure that you never write more than what fits on the screen. + While SLang provides such a feature, ncurses does not. + */ + +extern int tty_reset_screen (void); +extern void tty_touch_screen (void); + +extern void tty_gotoyx (int y, int x); +extern void tty_getyx (int *py, int *px); + +extern void tty_set_alt_charset (gboolean alt_charset); + +extern void tty_display_8bit (gboolean what); +extern void tty_print_char (int c); +extern void tty_print_alt_char (int c, gboolean single); +extern void tty_print_anychar (int c); +extern void tty_print_string (const char *s); +/* *INDENT-OFF* */ +extern void tty_printf (const char *s, ...) G_GNUC_PRINTF (1, 2); +/* *INDENT-ON* */ + +extern void tty_print_one_vline (gboolean single); +extern void tty_print_one_hline (gboolean single); +extern void tty_draw_hline (int y, int x, int ch, int len); +extern void tty_draw_vline (int y, int x, int ch, int len); +extern void tty_draw_box (int y, int x, int rows, int cols, gboolean single); +extern void tty_draw_box_shadow (int y, int x, int rows, int cols, int shadow_color); +extern void tty_fill_region (int y, int x, int rows, int cols, unsigned char ch); + +extern int tty_resize (int fd); +extern void tty_refresh (void); +extern void tty_change_screen_size (void); + +/* Clear screen */ +extern void tty_clear_screen (void); + +extern int mc_tty_normalize_lines_char (const char *str); + +extern void tty_enter_ca_mode (void); +extern void tty_exit_ca_mode (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__TTY_H */ diff --git a/lib/tty/win.c b/lib/tty/win.c new file mode 100644 index 0000000..45451a4 --- /dev/null +++ b/lib/tty/win.c @@ -0,0 +1,168 @@ +/* + Terminal management xterm and rxvt support + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file win.c + * \brief Source: Terminal management xterm and rxvt support + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "lib/global.h" +#include "lib/util.h" /* is_printable() */ +#include "tty-internal.h" +#include "tty.h" /* tty_gotoyx, tty_print_char */ +#include "win.h" + +/*** global variables ****************************************************************************/ + +char *smcup = NULL; +char *rmcup = NULL; + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static gboolean rxvt_extensions = FALSE; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* my own weird protocol base 16 - paul */ +static int +rxvt_getc (void) +{ + int r; + unsigned char c; + + while (read (0, &c, 1) != 1); + if (c == '\n') + return -1; + r = (c - 'A') * 16; + while (read (0, &c, 1) != 1); + r += (c - 'A'); + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +anything_ready (void) +{ + fd_set fds; + struct timeval tv; + + FD_ZERO (&fds); + FD_SET (0, &fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + return select (1, &fds, 0, 0, &tv); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +show_rxvt_contents (int starty, unsigned char y1, unsigned char y2) +{ + unsigned char *k; + int bytes, i, j, cols = 0; + + y1 += mc_global.keybar_visible != 0 ? 1 : 0; /* i don't know why we need this - paul */ + y2 += mc_global.keybar_visible != 0 ? 1 : 0; + while (anything_ready ()) + tty_lowlevel_getch (); + + /* my own weird protocol base 26 - paul */ + printf (ESC_STR "CL%c%c%c%c\n", (y1 / 26) + 'A', (y1 % 26) + 'A', (y2 / 26) + 'A', + (y2 % 26) + 'A'); + + bytes = (y2 - y1) * (COLS + 1) + 1; /* *should* be the number of bytes read */ + j = 0; + k = g_malloc (bytes); + while (TRUE) + { + int c; + + c = rxvt_getc (); + if (c < 0) + break; + if (j < bytes) + k[j++] = c; + for (cols = 1;; cols++) + { + c = rxvt_getc (); + if (c < 0) + break; + if (j < bytes) + k[j++] = c; + } + } + for (i = 0; i < j; i++) + { + if ((i % cols) == 0) + tty_gotoyx (starty + (i / cols), 0); + tty_print_char (is_printable (k[i]) ? k[i] : ' '); + } + g_free (k); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +look_for_rxvt_extensions (void) +{ + static gboolean been_called = FALSE; + + if (!been_called) + { + const char *e = getenv ("RXVT_EXT"); + rxvt_extensions = ((e != NULL) && (strcmp (e, "1.0") == 0)); + been_called = TRUE; + } + + if (rxvt_extensions) + mc_global.tty.console_flag = '\004'; + + return rxvt_extensions; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/win.h b/lib/tty/win.h new file mode 100644 index 0000000..4c31607 --- /dev/null +++ b/lib/tty/win.h @@ -0,0 +1,24 @@ +/** \file win.h + * \brief Header: X terminal management: xterm and rxvt + */ + +#ifndef MC__WIN_H +#define MC__WIN_H + +#include "lib/global.h" /* <glib.h> */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +void show_rxvt_contents (int starty, unsigned char y1, unsigned char y2); +gboolean look_for_rxvt_extensions (void); + +/*** inline functions ****************************************************************************/ +#endif /* MC_WIN_H */ diff --git a/lib/tty/x11conn.c b/lib/tty/x11conn.c new file mode 100644 index 0000000..20e201b --- /dev/null +++ b/lib/tty/x11conn.c @@ -0,0 +1,266 @@ +/* + X11 support for the Midnight Commander. + + Copyright (C) 2005-2023 + Free Software Foundation, Inc. + + Written by: + Roland Illig <roland.illig@gmx.de>, 2005. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file x11conn.c + * \brief Source: X11 support + * \warning This code uses setjmp() and longjmp(). Before you modify _anything_ here, + * please read the relevant sections of the C standard. + */ + +#include <config.h> + +#include <setjmp.h> +#include <X11/Xlib.h> +#ifdef HAVE_GMODULE +#include <gmodule.h> +#endif + +#include "lib/global.h" +#include "x11conn.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#ifndef HAVE_GMODULE +#define func_XOpenDisplay XOpenDisplay +#define func_XCloseDisplay XCloseDisplay +#define func_XSetErrorHandler XSetErrorHandler +#define func_XSetIOErrorHandler XSetIOErrorHandler +#define func_XQueryPointer XQueryPointer +#endif + +/*** file scope type declarations ****************************************************************/ + +typedef int (*mc_XErrorHandler_callback) (Display *, XErrorEvent *); +typedef int (*mc_XIOErrorHandler_callback) (Display *); + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +#ifdef HAVE_GMODULE +static Display *(*func_XOpenDisplay) (_Xconst char *); +static int (*func_XCloseDisplay) (Display *); +static mc_XErrorHandler_callback (*func_XSetErrorHandler) (mc_XErrorHandler_callback); +static mc_XIOErrorHandler_callback (*func_XSetIOErrorHandler) (mc_XIOErrorHandler_callback); +static Bool (*func_XQueryPointer) (Display *, Window, Window *, Window *, + int *, int *, int *, int *, unsigned int *); + +static GModule *x11_module; +#endif + +static gboolean handlers_installed = FALSE; + +/* This flag is set as soon as an X11 error is reported. Usually that + * means that the DISPLAY is not available anymore. We do not try to + * reconnect, as that would violate the X11 protocol. */ +static gboolean lost_connection = FALSE; + +static jmp_buf x11_exception; /* FIXME: get a better name */ +static gboolean longjmp_allowed = FALSE; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +x_io_error_handler (Display * dpy) +{ + (void) dpy; + + lost_connection = TRUE; + if (longjmp_allowed) + { + longjmp_allowed = FALSE; + longjmp (x11_exception, 1); + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +x_error_handler (Display * dpy, XErrorEvent * ee) +{ + (void) ee; + (void) func_XCloseDisplay (dpy); + return x_io_error_handler (dpy); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +install_error_handlers (void) +{ + if (handlers_installed) + return; + + (void) func_XSetErrorHandler (x_error_handler); + (void) func_XSetIOErrorHandler (x_io_error_handler); + handlers_installed = TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +x11_available (void) +{ +#ifdef HAVE_GMODULE + gchar *x11_module_fname; + + if (lost_connection) + return FALSE; + + if (x11_module != NULL) + return TRUE; + + x11_module_fname = g_module_build_path (NULL, "X11"); + x11_module = g_module_open (x11_module_fname, G_MODULE_BIND_LAZY); + if (x11_module == NULL) + x11_module = g_module_open ("libX11.so.6", G_MODULE_BIND_LAZY); + + g_free (x11_module_fname); + + if (x11_module == NULL) + return FALSE; + + if (!g_module_symbol (x11_module, "XOpenDisplay", (void *) &func_XOpenDisplay)) + goto cleanup; + if (!g_module_symbol (x11_module, "XCloseDisplay", (void *) &func_XCloseDisplay)) + goto cleanup; + if (!g_module_symbol (x11_module, "XQueryPointer", (void *) &func_XQueryPointer)) + goto cleanup; + if (!g_module_symbol (x11_module, "XSetErrorHandler", (void *) &func_XSetErrorHandler)) + goto cleanup; + if (!g_module_symbol (x11_module, "XSetIOErrorHandler", (void *) &func_XSetIOErrorHandler)) + goto cleanup; + + install_error_handlers (); + return TRUE; + + cleanup: + func_XOpenDisplay = 0; + func_XCloseDisplay = 0; + func_XQueryPointer = 0; + func_XSetErrorHandler = 0; + func_XSetIOErrorHandler = 0; + g_module_close (x11_module); + x11_module = NULL; + return FALSE; +#else + install_error_handlers (); + return !(lost_connection); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +Display * +mc_XOpenDisplay (const char *displayname) +{ + if (x11_available ()) + { + if (setjmp (x11_exception) == 0) + { + Display *retval; + + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = TRUE; + + retval = func_XOpenDisplay (displayname); + + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = FALSE; + return retval; + } + } + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +mc_XCloseDisplay (Display * display) +{ + if (x11_available ()) + { + if (setjmp (x11_exception) == 0) + { + int retval; + + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = TRUE; + + retval = func_XCloseDisplay (display); + + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = FALSE; + + return retval; + } + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +Bool +mc_XQueryPointer (Display * display, Window win, Window * root_return, + Window * child_return, int *root_x_return, int *root_y_return, + int *win_x_return, int *win_y_return, unsigned int *mask_return) +{ + Bool retval; + + if (x11_available ()) + { + if (setjmp (x11_exception) == 0) + { + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = TRUE; + + retval = func_XQueryPointer (display, win, root_return, + child_return, root_x_return, root_y_return, + win_x_return, win_y_return, mask_return); + + /* cppcheck-suppress redundantAssignment */ + longjmp_allowed = FALSE; + + return retval; + } + } + *root_return = None; + *child_return = None; + *root_x_return = 0; + *root_y_return = 0; + *win_x_return = 0; + *win_y_return = 0; + *mask_return = 0; + return False; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/tty/x11conn.h b/lib/tty/x11conn.h new file mode 100644 index 0000000..fbfe15a --- /dev/null +++ b/lib/tty/x11conn.h @@ -0,0 +1,40 @@ +/** \file x11conn.h + * \brief Header: X11 support + * \warning This code uses setjmp() and longjmp(). Before you modify _anything_ here, + * please read the relevant sections of the C standard. + */ + +#ifndef MC__X11CONN_H +#define MC__X11CONN_H + +/* + This module provides support for some X11 functions. The functions + are loaded dynamically if GModule is available, and statically if + not. X11 session handling is somewhat robust. If there is an X11 + error or a connection error, all further traffic to the X server + will be suppressed, and the functions will return reasonable default + values. + */ + +#include <X11/Xlib.h> + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +extern Display *mc_XOpenDisplay (const char *displayname); +extern int mc_XCloseDisplay (Display * display); + +extern Bool mc_XQueryPointer (Display * display, Window win, Window * root_return, + Window * child_return, int *root_x_return, int *root_y_return, + int *win_x_return, int *win_y_return, unsigned int *mask_return); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__X11CONN_H */ |