├── libtsm ├── NEWS ├── src │ ├── tsm │ │ ├── libtsm.pc.in │ │ ├── libtsm.sym │ │ ├── tsm-render.c │ │ ├── libtsm-int.h │ │ ├── tsm-selection.c │ │ ├── tsm-vte-charsets.c │ │ └── libtsm.h │ ├── shared │ │ ├── shl-ring.h │ │ ├── shl-pty.h │ │ ├── shl-ring.c │ │ ├── shl-array.h │ │ ├── shl-htable.h │ │ ├── shl-macro.h │ │ ├── shl-llog.h │ │ ├── shl-htable.c │ │ └── shl-pty.c │ └── gtktsm │ │ ├── gtktsm.c │ │ ├── gtktsm-app.h │ │ ├── gtktsm-win.h │ │ ├── gtktsm-terminal.h │ │ ├── gtktsm-win.c │ │ └── gtktsm-app.c ├── autogen.sh ├── test.supp ├── .gitignore ├── external │ ├── wcwidth.h │ └── wcwidth.c ├── test │ ├── test_valgrind.c │ ├── test_symbol.c │ ├── test_common.h │ └── test_htable.c ├── README ├── COPYING ├── configure.ac └── Makefile.am ├── README.md ├── .gitignore ├── protocol └── orbital-dropdown.xml ├── CMakeLists.txt ├── src ├── vte.h ├── terminal.h ├── screen.h ├── main.cpp ├── vte.cpp └── terminal.cpp └── wayland.cmake /libtsm/NEWS: -------------------------------------------------------------------------------- 1 | = libtsm Release News = 2 | 3 | CHANGES WITH 1: 4 | * TODO 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | termistor 2 | ========= 3 | 4 | A drop-down terminal for Wayland 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /libtsm/src/tsm/libtsm.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libtsm 7 | Description: @PACKAGE_DESCRIPTION@ 8 | URL: @PACKAGE_URL@ 9 | Version: @PACKAGE_VERSION@ 10 | Libs: -L${libdir} -ltsm 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /libtsm/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | srcdir=`dirname $0` 5 | test -z "$srcdir" && srcdir=. 6 | 7 | origdir=`pwd` 8 | cd $srcdir 9 | 10 | mkdir -p m4 11 | autoreconf -is --force 12 | 13 | cd $origdir 14 | 15 | if test -z "$NOCONFIGURE" ; then 16 | exec $srcdir/configure "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /libtsm/test.supp: -------------------------------------------------------------------------------- 1 | # 2 | # Some suppression rules for our test-suite to work correctly. 3 | # libcheck has some weird errors, so lets ignore them. 4 | # 5 | 6 | { 7 | libcheck timer_create warnings 8 | Memcheck:Param 9 | timer_create(evp) 10 | fun:timer_create 11 | obj:/usr/lib/libcheck.so.0.0.0 12 | obj:/usr/lib/libcheck.so.0.0.0 13 | fun:srunner_run 14 | fun:main 15 | } 16 | -------------------------------------------------------------------------------- /libtsm/.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.log 4 | *.memlog 5 | *.o 6 | *.swp 7 | *.tar.xz 8 | *.trs 9 | .deps/ 10 | .dirstamp 11 | .libs/ 12 | Makefile 13 | Makefile.in 14 | aclocal.m4 15 | autom4te.cache/ 16 | build-aux/ 17 | config.h 18 | config.h.in 19 | config.h.in~ 20 | config.log 21 | config.status 22 | configure 23 | /gtktsm 24 | libtool 25 | m4/ 26 | src/tsm/libtsm.pc 27 | stamp-h1 28 | test-suite.log 29 | test_htable 30 | test_symbol 31 | test_valgrind 32 | -------------------------------------------------------------------------------- /libtsm/external/wcwidth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an implementation of wcwidth() and wcswidth() (defined in 3 | * IEEE Std 1002.1-2001) for Unicode. 4 | * 5 | * Markus Kuhn -- 2007-05-26 (Unicode 5.0) 6 | * 7 | * Permission to use, copy, modify, and distribute this software 8 | * for any purpose and without fee is hereby granted. The author 9 | * disclaims all warranties with regard to this software. 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | int mk_wcwidth(wchar_t ucs); 16 | int mk_wcswidth(const wchar_t *pwcs, size_t n); 17 | int mk_wcwidth_cjk(wchar_t ucs); 18 | int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n); 19 | -------------------------------------------------------------------------------- /protocol/orbital-dropdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-ring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #ifndef SHL_RING_H 13 | #define SHL_RING_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | struct shl_ring { 22 | uint8_t *buf; /* buffer or NULL */ 23 | size_t size; /* actual size of @buf */ 24 | size_t start; /* start position of ring */ 25 | size_t used; /* number of actually used bytes */ 26 | }; 27 | 28 | /* flush buffer so it is empty again */ 29 | void shl_ring_flush(struct shl_ring *r); 30 | 31 | /* flush buffer, free allocated data and reset to initial state */ 32 | void shl_ring_clear(struct shl_ring *r); 33 | 34 | /* get pointers to buffer data and their length */ 35 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec); 36 | 37 | /* copy data into external linear buffer */ 38 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size); 39 | 40 | /* push data to the end of the buffer */ 41 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size); 42 | 43 | /* pull data from the front of the buffer */ 44 | void shl_ring_pull(struct shl_ring *r, size_t size); 45 | 46 | /* return size of occupied buffer in bytes */ 47 | static inline size_t shl_ring_get_size(struct shl_ring *r) 48 | { 49 | return r->used; 50 | } 51 | 52 | #endif /* SHL_RING_H */ 53 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 2.8) 3 | project(termistor) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-parameter -g -std=c++11") 7 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") 8 | 9 | include(wayland) 10 | 11 | find_package(Qt5Core) 12 | find_package(Qt5Gui) 13 | find_package(PkgConfig "0.22" REQUIRED) 14 | 15 | pkg_check_modules(wayland-client wayland-client REQUIRED) 16 | pkg_check_modules(xkbcommon xkbcommon REQUIRED) 17 | 18 | set(CMAKE_AUTOMOC ON) 19 | foreach(dir ${Qt5Gui_INCLUDE_DIRS}) 20 | include_directories(${dir}/${Qt5Gui_VERSION_STRING}/QtGui/) 21 | endforeach(dir) 22 | include_directories(${wayland-client_INCLUDE_DIRS} ${xkbcommon_INCLUDE_DIRS} 23 | libtsm libtsm/src/tsm libtsm/src/shared) 24 | 25 | set(libtsm_SOURCES 26 | libtsm/src/tsm/tsm-vte.c 27 | libtsm/src/tsm/tsm-render.c 28 | libtsm/src/tsm/tsm-screen.c 29 | libtsm/src/tsm/tsm-selection.c 30 | libtsm/src/tsm/tsm-unicode.c 31 | libtsm/src/tsm/tsm-vte-charsets.c 32 | libtsm/src/shared/shl-htable.c 33 | libtsm/src/shared/shl-pty.c 34 | libtsm/src/shared/shl-ring.c 35 | libtsm/external/wcwidth.c) 36 | add_library(tsm ${libtsm_SOURCES}) 37 | 38 | set(SOURCES 39 | src/main.cpp 40 | src/vte.cpp 41 | src/terminal.cpp 42 | src/screen.cpp) 43 | 44 | wayland_add_protocol_client(SOURCES 45 | ${CMAKE_SOURCE_DIR}/protocol/orbital-dropdown.xml 46 | dropdown 47 | ) 48 | 49 | add_executable(termistor ${SOURCES}) 50 | qt5_use_modules(termistor Gui) 51 | target_link_libraries(termistor util tsm ${wayland-client_LIBRARIES}) 52 | 53 | install(TARGETS termistor DESTINATION bin) 54 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "gtktsm-app.h" 30 | 31 | int main(int argc, char **argv) 32 | { 33 | GApplication *app; 34 | int r; 35 | 36 | setlocale(LC_ALL, ""); 37 | g_set_application_name("GtkTsm"); 38 | 39 | app = G_APPLICATION(gtktsm_app_new()); 40 | r = g_application_run(app, argc, argv); 41 | 42 | g_object_unref(app); 43 | return r; 44 | } 45 | -------------------------------------------------------------------------------- /libtsm/test/test_valgrind.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Valgrind Verification 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* Dummy which just leaks memory. Used to verify valgrind memcheck. */ 27 | 28 | #include "test_common.h" 29 | 30 | START_TEST(test_valgrind) 31 | { 32 | void *p; 33 | 34 | p = malloc(0x100); 35 | ck_assert(!!p); 36 | } 37 | END_TEST 38 | 39 | TEST_DEFINE_CASE(misc) 40 | TEST(test_valgrind) 41 | TEST_END_CASE 42 | 43 | TEST_DEFINE( 44 | TEST_SUITE(valgrind, 45 | TEST_CASE(misc), 46 | TEST_END 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /src/vte.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Giulio Camuffo 3 | * 4 | * This file is part of Termistor 5 | * 6 | * Termistor is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Termistor is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Termistor. If not, see . 18 | */ 19 | 20 | #ifndef VTE_H 21 | #define VTE_H 22 | 23 | #include 24 | #include 25 | 26 | class QSocketNotifier; 27 | 28 | class Screen; 29 | 30 | class VTE : public QObject 31 | { 32 | Q_OBJECT 33 | public: 34 | explicit VTE(Screen *screen); 35 | ~VTE(); 36 | 37 | void write(const QChar &ch); 38 | void resize(int rows, int cols); 39 | void paste(const QByteArray &data); 40 | 41 | inline tsm_vte *vte() const { return m_vte; } 42 | inline tsm_screen *screen() const { return m_screen; } 43 | 44 | public: 45 | void keyPress(int key, Qt::KeyboardModifiers mods, const QString &string); 46 | 47 | private slots: 48 | void onSocketActivated(int); 49 | 50 | private: 51 | void vte_event(const char *u8, size_t len); 52 | 53 | tsm_screen *m_screen; 54 | tsm_vte *m_vte; 55 | int m_master; 56 | QSocketNotifier *m_notifier; 57 | Screen *m_termScreen; 58 | }; 59 | 60 | #endif // VTE_H 61 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm-app.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_APP_H 27 | #define GTKTSM_APP_H 28 | 29 | #include 30 | #include 31 | 32 | typedef struct _GtkTsmApp GtkTsmApp; 33 | typedef struct _GtkTsmAppClass GtkTsmAppClass; 34 | 35 | #define GTKTSM_APP_TYPE (gtktsm_app_get_type()) 36 | #define GTKTSM_APP(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 37 | GTKTSM_APP_TYPE, \ 38 | GtkTsmApp)) 39 | 40 | GType gtktsm_app_get_type(void); 41 | GtkTsmApp *gtktsm_app_new(void); 42 | 43 | #endif /* GTKTSM_APP_H */ 44 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-pty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - PTY Helpers 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * PTY Helpers 10 | */ 11 | 12 | #ifndef SHL_PTY_H 13 | #define SHL_PTY_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "shl-macro.h" 22 | 23 | /* pty */ 24 | 25 | struct shl_pty; 26 | 27 | typedef void (*shl_pty_input_fn) (struct shl_pty *pty, 28 | void *data, 29 | char *u8, 30 | size_t len); 31 | 32 | pid_t shl_pty_open(struct shl_pty **out, 33 | shl_pty_input_fn fn_input, 34 | void *fn_input_data, 35 | unsigned short term_width, 36 | unsigned short term_height); 37 | void shl_pty_ref(struct shl_pty *pty); 38 | void shl_pty_unref(struct shl_pty *pty); 39 | void shl_pty_close(struct shl_pty *pty); 40 | 41 | static inline void shl_pty_unref_p(struct shl_pty **pty) 42 | { 43 | shl_pty_unref(*pty); 44 | } 45 | 46 | #define _shl_pty_unref_ _shl_cleanup_(shl_pty_unref_p) 47 | 48 | bool shl_pty_is_open(struct shl_pty *pty); 49 | int shl_pty_get_fd(struct shl_pty *pty); 50 | pid_t shl_pty_get_child(struct shl_pty *pty); 51 | 52 | int shl_pty_dispatch(struct shl_pty *pty); 53 | int shl_pty_write(struct shl_pty *pty, const char *u8, size_t len); 54 | int shl_pty_signal(struct shl_pty *pty, int sig); 55 | int shl_pty_resize(struct shl_pty *pty, 56 | unsigned short term_width, 57 | unsigned short term_height); 58 | 59 | /* pty bridge */ 60 | 61 | int shl_pty_bridge_new(void); 62 | void shl_pty_bridge_free(int bridge); 63 | 64 | int shl_pty_bridge_dispatch_pty(int bridge, struct shl_pty *pty); 65 | int shl_pty_bridge_dispatch(int bridge, int timeout); 66 | int shl_pty_bridge_add(int bridge, struct shl_pty *pty); 67 | void shl_pty_bridge_remove(int bridge, struct shl_pty *pty); 68 | 69 | #endif /* SHL_PTY_H */ 70 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm-win.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_WIN_H 27 | #define GTKTSM_WIN_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "gtktsm-app.h" 34 | #include "gtktsm-terminal.h" 35 | 36 | typedef struct _GtkTsmWin GtkTsmWin; 37 | typedef struct _GtkTsmWinClass GtkTsmWinClass; 38 | 39 | #define GTKTSM_WIN_TYPE (gtktsm_win_get_type()) 40 | #define GTKTSM_WIN(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 41 | GTKTSM_WIN_TYPE, \ 42 | GtkTsmWin)) 43 | 44 | GType gtktsm_win_get_type(void); 45 | GtkTsmWin *gtktsm_win_new(GtkTsmApp *app); 46 | 47 | void gtktsm_win_run(GtkTsmWin *win); 48 | GtkTsmTerminal *gtktsm_win_get_terminal(GtkTsmWin *win); 49 | 50 | #endif /* GTKTSM_WIN_H */ 51 | -------------------------------------------------------------------------------- /libtsm/test/test_symbol.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Symbol Table Tests 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | 28 | START_TEST(test_symbol_null) 29 | { 30 | int r; 31 | tsm_symbol_t sym = 0, s; 32 | const tsm_symbol_t *sp; 33 | unsigned int n; 34 | 35 | r = tsm_symbol_table_new(NULL); 36 | ck_assert(r == -EINVAL); 37 | 38 | tsm_symbol_table_ref(NULL); 39 | tsm_symbol_table_unref(NULL); 40 | 41 | sp = tsm_symbol_get(NULL, &sym, NULL); 42 | ck_assert(sp == &sym); 43 | 44 | s = tsm_symbol_append(NULL, sym, 'a'); 45 | ck_assert(s == sym); 46 | 47 | n = tsm_symbol_get_width(NULL, sym); 48 | ck_assert(!n); 49 | } 50 | END_TEST 51 | 52 | START_TEST(test_symbol_init) 53 | { 54 | struct tsm_symbol_table *t; 55 | int r; 56 | 57 | r = tsm_symbol_table_new(&t); 58 | ck_assert(!r); 59 | 60 | tsm_symbol_table_unref(t); 61 | t = NULL; 62 | } 63 | END_TEST 64 | 65 | TEST_DEFINE_CASE(misc) 66 | TEST(test_symbol_null) 67 | TEST(test_symbol_init) 68 | TEST_END_CASE 69 | 70 | TEST_DEFINE( 71 | TEST_SUITE(symbol, 72 | TEST_CASE(misc), 73 | TEST_END 74 | ) 75 | ) 76 | -------------------------------------------------------------------------------- /libtsm/README: -------------------------------------------------------------------------------- 1 | = TSM - Terminal Emulator State Machine = 2 | 3 | TSM is a state machine for DEC VT100-VT520 compatible terminal emulators. It 4 | tries to support all common standards while keeping compatibility to existing 5 | emulators like xterm, gnome-terminal, konsole, .. 6 | 7 | TSM itself does not provide any rendering nor window management. It is a simple 8 | plain state machine without any external dependencies. It can be used to 9 | implement terminal emulators, but also to implement other applications that need 10 | to interpret terminal escape sequences. 11 | 12 | This library is very similar to libvte of the gnome project. However, libvte is 13 | highly bound to GTK+, which makes it unsuitable for non-graphics projects that 14 | need to parse escape sequences. Instead, TSM tries to restrict its API to 15 | terminal emulation only. Furthermore, TSM does not try to establish a new 16 | terminal emulation standard, but instead keeps compatibility as close to xterm 17 | as possible. This is why the TERM variable can be set to xterm-color256 with any 18 | TSM based terminal emulator. 19 | 20 | Website: 21 | http://www.freedesktop.org/wiki/Software/libtsm 22 | 23 | == Requirements == 24 | 25 | libtsm has no runtime requirements other than a ISO-C compatible C library. 26 | For keyboard key-symbols, the headers of libxkbcommon are needed during 27 | compile-time only. libtsm ships a copy of these headers if they are not 28 | available at compile-time. 29 | 30 | == Download == 31 | 32 | Released tarballs can be found at: 33 | http://www.freedesktop.org/software/kmscon/releases 34 | 35 | == Install == 36 | 37 | To compile libtsm, run the standard autotools commands: 38 | $ test -f ./configure || NOCONFIGURE=1 ./autogen.sh 39 | $ ./configure 40 | $ make 41 | $ make install 42 | To compile the test applications, run: 43 | $ make check 44 | 45 | == Documentation == 46 | 47 | There is currently no API documentation available. You can have a look at the 48 | example terminal-emulator "wlterm" available at: 49 | http://www.freedesktop.org/wiki/Software/kmscon/wlterm 50 | 51 | == License == 52 | 53 | This software is licensed under the terms of an MIT-like license. Please see 54 | ./COPYING for further information. 55 | 56 | == Contact == 57 | 58 | This software is maintained by: 59 | David Herrmann 60 | If you have any questions, do not hesitate to contact one of the maintainers. 61 | -------------------------------------------------------------------------------- /src/terminal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Giulio Camuffo 3 | * 4 | * This file is part of Termistor 5 | * 6 | * Termistor is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Termistor is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Termistor. If not, see . 18 | */ 19 | 20 | #ifndef TERMINAL_H 21 | #define TERMINAL_H 22 | 23 | #include 24 | 25 | #include 26 | 27 | class Screen; 28 | class VTE; 29 | 30 | class Terminal: public QWindow 31 | { 32 | Q_OBJECT 33 | public: 34 | explicit Terminal(QWindow *parent = 0); 35 | 36 | void init(VTE *vte, tsm_screen *screen); 37 | void render(); 38 | void renderNow(); 39 | void update(); 40 | void closeScreen(Screen *screen); 41 | 42 | inline Screen *currentScreen() const { return m_screens.at(m_currentScreen); } 43 | void setScreenSize(const QSize &s); 44 | 45 | protected: 46 | bool event(QEvent *event) override; 47 | 48 | void exposeEvent(QExposeEvent *event) override; 49 | void resizeEvent(QResizeEvent *event) override; 50 | void focusInEvent(QFocusEvent *event) override; 51 | void focusOutEvent(QFocusEvent *event) override; 52 | 53 | private: 54 | void addScreen(); 55 | void delScreen(); 56 | void setScreen(int i); 57 | QRect addScreenRect() const; 58 | QRect delScreenRect() const; 59 | QRect tabRect(int i) const; 60 | QRect quitRect() const; 61 | void paste(); 62 | void moveScreen(int screen, int d); 63 | 64 | QList m_screens; 65 | int m_currentScreen; 66 | bool m_updatePending; 67 | QMargins m_borders; 68 | bool m_bordersDirty; 69 | QBackingStore *m_backingStore; 70 | bool m_hasFocus; 71 | }; 72 | 73 | class Debugger 74 | { 75 | public: 76 | static void print(const char *msg); 77 | static void printCache(int num, int size); 78 | 79 | private: 80 | static bool printedCache; 81 | static int cacheNum; 82 | static int cacheSize; 83 | }; 84 | 85 | #endif // TERMINAL_H 86 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm-terminal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_TERMINAL_H 27 | #define GTKTSM_TERMINAL_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | G_BEGIN_DECLS 36 | 37 | typedef struct _GtkTsmTerminal GtkTsmTerminal; 38 | typedef struct _GtkTsmTerminalClass GtkTsmTerminalClass; 39 | 40 | struct _GtkTsmTerminal { 41 | GtkDrawingArea parent; 42 | }; 43 | 44 | struct _GtkTsmTerminalClass { 45 | GtkDrawingAreaClass parent_class; 46 | }; 47 | 48 | #define GTKTSM_TYPE_TERMINAL (gtktsm_terminal_get_type()) 49 | #define GTKTSM_TERMINAL(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 50 | GTKTSM_TYPE_TERMINAL, \ 51 | GtkTsmTerminal)) 52 | #define GTKTSM_TERMINAL_CLASS(_klass) \ 53 | (G_TYPE_CHECK_CLASS_CAST((_klass), \ 54 | GTKTSM_TYPE_TERMINAL, \ 55 | GtkTsmTerminalClass)) 56 | #define GTKTSM_IS_TERMINAL(_obj) \ 57 | (G_TYPE_CHECK_INSTANCE_TYPE((_obj), \ 58 | GTKTSM_TYPE_TERMINAL)) 59 | #define GTKTSM_IS_TERMINAL_CLASS(_klass) \ 60 | (G_TYPE_CHECK_CLASS_TYPE((_klass), \ 61 | GTKTSM_TYPE_TERMINAL)) 62 | #define GTKTSM_TERMINAL_GET_CLASS(_obj) \ 63 | (G_TYPE_INSTANCE_GET_CLASS((_obj), \ 64 | GTKTSM_TYPE_TERMINAL, \ 65 | GtkTsmTerminalClass)) 66 | 67 | #define GTKTSM_TERMINAL_DONT_CARE (-1) 68 | 69 | GType gtktsm_terminal_get_type(void) G_GNUC_CONST; 70 | GtkTsmTerminal *gtktsm_terminal_new(void); 71 | 72 | pid_t gtktsm_terminal_fork(GtkTsmTerminal *term); 73 | void gtktsm_terminal_kill(GtkTsmTerminal *term, int sig); 74 | 75 | G_END_DECLS 76 | 77 | #endif /* GTKTSM_TERMINAL_H */ 78 | -------------------------------------------------------------------------------- /src/screen.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SCREEN_H 3 | #define SCREEN_H 4 | /* 5 | * Copyright 2013 Giulio Camuffo 6 | * 7 | * This file is part of Termistor 8 | * 9 | * Termistor is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Termistor is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with Termistor. If not, see . 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | class QPainter; 32 | class QKeyEvent; 33 | class QWheelEvent; 34 | class QMouseEvent; 35 | 36 | struct tsm_screen; 37 | 38 | class Terminal; 39 | class VTE; 40 | struct Cell; 41 | 42 | class Screen : public QObject 43 | { 44 | Q_OBJECT 45 | public: 46 | explicit Screen(Terminal *term, const QString &name); 47 | ~Screen(); 48 | 49 | void initCells(); 50 | 51 | QString name() const; 52 | 53 | void close(); 54 | void resize(const QSize &size); 55 | void update(); 56 | void render(QPainter *painter); 57 | void forceRedraw(); 58 | 59 | QByteArray copy(); 60 | void paste(const QByteArray &data); 61 | void keyPressEvent(QKeyEvent *ev); 62 | void wheelEvent(QWheelEvent *ev); 63 | void mousePressEvent(QMouseEvent *ev); 64 | void mouseMoveEvent(QMouseEvent *ev); 65 | void mouseDoubleClickEvent(QMouseEvent *ev); 66 | void focusIn(); 67 | void focusOut(); 68 | 69 | private: 70 | inline QRect geometry() const { return m_geometry; } 71 | int drawCell(uint32_t id, const uint32_t *ch, size_t len, uint32_t width, unsigned int posx, unsigned int posy, const tsm_screen_attr *attr, tsm_age_t age); 72 | QPoint gridPosFromGlobal(const QPointF &pos); 73 | char getCharacter(int x, int y); 74 | 75 | Terminal *m_terminal; 76 | VTE *m_vte; 77 | int m_rows; 78 | int m_columns; 79 | QString m_name; 80 | 81 | Cell *m_cells; 82 | Cell *m_cursor; 83 | 84 | QPainter *m_painter; 85 | struct { 86 | int cellW; 87 | int cellH; 88 | QFont font; 89 | tsm_age_t age; 90 | } m_renderdata; 91 | 92 | bool m_updatePending; 93 | QMargins m_margins; 94 | QSize m_screenSize; 95 | QRect m_geometry; 96 | bool m_forceRedraw; 97 | bool m_hasFocus; 98 | QPoint m_selectionStart; 99 | int m_backgroundAlpha; 100 | double m_accumDelta; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /libtsm/src/tsm/libtsm.sym: -------------------------------------------------------------------------------- 1 | /* 2 | * libtsm - Terminal-Emulator State Machine 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | LIBTSM_1 { 27 | global: 28 | tsm_ucs4_get_width; 29 | local: 30 | *; 31 | }; 32 | 33 | LIBTSM_2 { 34 | global: 35 | tsm_ucs4_to_utf8; 36 | tsm_ucs4_to_utf8_alloc; 37 | } LIBTSM_1; 38 | 39 | LIBTSM_3 { 40 | global: 41 | tsm_screen_new; 42 | tsm_screen_ref; 43 | tsm_screen_unref; 44 | 45 | tsm_screen_get_width; 46 | tsm_screen_get_height; 47 | tsm_screen_resize; 48 | tsm_screen_set_margins; 49 | tsm_screen_set_max_sb; 50 | tsm_screen_clear_sb; 51 | 52 | tsm_screen_sb_up; 53 | tsm_screen_sb_down; 54 | tsm_screen_sb_page_up; 55 | tsm_screen_sb_page_down; 56 | tsm_screen_sb_reset; 57 | 58 | tsm_screen_set_def_attr; 59 | tsm_screen_reset; 60 | tsm_screen_set_flags; 61 | tsm_screen_reset_flags; 62 | tsm_screen_get_flags; 63 | 64 | tsm_screen_get_cursor_x; 65 | tsm_screen_get_cursor_y; 66 | 67 | tsm_screen_set_tabstop; 68 | tsm_screen_reset_tabstop; 69 | tsm_screen_reset_all_tabstops; 70 | 71 | tsm_screen_write; 72 | tsm_screen_newline; 73 | tsm_screen_scroll_up; 74 | tsm_screen_scroll_down; 75 | tsm_screen_move_to; 76 | tsm_screen_move_up; 77 | tsm_screen_move_down; 78 | tsm_screen_move_left; 79 | tsm_screen_move_right; 80 | tsm_screen_move_line_end; 81 | tsm_screen_move_line_home; 82 | tsm_screen_tab_right; 83 | tsm_screen_tab_left; 84 | tsm_screen_insert_lines; 85 | tsm_screen_delete_lines; 86 | tsm_screen_insert_chars; 87 | tsm_screen_delete_chars; 88 | tsm_screen_erase_cursor; 89 | tsm_screen_erase_chars; 90 | tsm_screen_erase_cursor_to_end; 91 | tsm_screen_erase_home_to_cursor; 92 | tsm_screen_erase_current_line; 93 | tsm_screen_erase_screen_to_cursor; 94 | tsm_screen_erase_cursor_to_screen; 95 | tsm_screen_erase_screen; 96 | 97 | tsm_screen_selection_reset; 98 | tsm_screen_selection_start; 99 | tsm_screen_selection_target; 100 | tsm_screen_selection_copy; 101 | 102 | tsm_screen_draw; 103 | 104 | tsm_vte_new; 105 | tsm_vte_ref; 106 | tsm_vte_unref; 107 | 108 | tsm_vte_set_palette; 109 | tsm_vte_set_palette_colors; 110 | tsm_vte_get_def_attr; 111 | 112 | tsm_vte_reset; 113 | tsm_vte_hard_reset; 114 | tsm_vte_input; 115 | tsm_vte_handle_keyboard; 116 | } LIBTSM_2; 117 | -------------------------------------------------------------------------------- /libtsm/test/test_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Test Helper 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Test Helper 28 | * This header includes all kinds of helpers for testing. It tries to include 29 | * everything required and provides simple macros to avoid duplicating code in 30 | * each test. We try to keep tests as small as possible and move everything that 31 | * might be common here. 32 | * 33 | * We avoid sticking to our usual coding conventions (including headers in 34 | * source files, etc. ..) and instead make this the most convenient we can. 35 | */ 36 | 37 | #ifndef TEST_COMMON_H 38 | #define TEST_COMMON_H 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "tsm/libtsm.h" 47 | #include "tsm/libtsm-int.h" 48 | #include "shl_htable.h" 49 | 50 | /* lower address-space is protected from user-allocation, so this is invalid */ 51 | #define TEST_INVALID_PTR ((void*)0x10) 52 | 53 | #define TEST_DEFINE_CASE(_name) \ 54 | static TCase *test_create_case_##_name(void) \ 55 | { \ 56 | TCase *tc; \ 57 | \ 58 | tc = tcase_create(#_name); \ 59 | 60 | #define TEST(_name) tcase_add_test(tc, _name); 61 | 62 | #define TEST_END_CASE \ 63 | return tc; \ 64 | } \ 65 | 66 | #define TEST_END NULL 67 | 68 | #define TEST_CASE(_name) test_create_case_##_name 69 | 70 | static inline Suite *test_create_suite(const char *name, ...) 71 | { 72 | Suite *s; 73 | va_list list; 74 | TCase *(*fn)(void); 75 | 76 | s = suite_create(name); 77 | 78 | va_start(list, name); 79 | while ((fn = va_arg(list, TCase *(*)(void)))) 80 | suite_add_tcase(s, fn()); 81 | va_end(list); 82 | 83 | return s; 84 | } 85 | 86 | #define TEST_SUITE(_name, ...) test_create_suite((#_name), ##__VA_ARGS__) 87 | 88 | static inline int test_run_suite(Suite *s) 89 | { 90 | int ret; 91 | SRunner *sr; 92 | 93 | sr = srunner_create(s); 94 | srunner_run_all(sr, CK_NORMAL); 95 | ret = srunner_ntests_failed(sr); 96 | srunner_free(sr); 97 | 98 | return ret; 99 | } 100 | 101 | #define TEST_DEFINE(_suite) \ 102 | int main(int argc, char **argv) \ 103 | { \ 104 | return test_run_suite(_suite); \ 105 | } 106 | 107 | #endif /* TEST_COMMON_H */ 108 | -------------------------------------------------------------------------------- /wayland.cmake: -------------------------------------------------------------------------------- 1 | #============================================================================= 2 | # Copyright (C) 2012-2013 Pier Luigi Fiorini 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions 7 | # are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright 13 | # notice, this list of conditions and the following disclaimer in the 14 | # documentation and/or other materials provided with the distribution. 15 | # 16 | # * Neither the name of Pier Luigi Fiorini nor the names of his 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | #============================================================================= 32 | 33 | find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) 34 | 35 | # wayland_add_protocol_client(outfiles inputfile basename) 36 | function(WAYLAND_ADD_PROTOCOL_CLIENT _sources _protocol _basename) 37 | if(NOT WAYLAND_SCANNER_EXECUTABLE) 38 | message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.") 39 | endif() 40 | 41 | get_filename_component(_infile ${_protocol} ABSOLUTE) 42 | set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h") 43 | set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-protocol.c") 44 | 45 | add_custom_command(OUTPUT "${_client_header}" 46 | COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header < ${_infile} > ${_client_header} 47 | DEPENDS ${_infile} VERBATIM) 48 | 49 | add_custom_command(OUTPUT "${_code}" 50 | COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code} 51 | DEPENDS ${_infile} VERBATIM) 52 | 53 | list(APPEND ${_sources} "${_client_header}" "${_code}") 54 | set(${_sources} ${${_sources}} PARENT_SCOPE) 55 | endfunction() 56 | 57 | # wayland_add_protocol_server(outfiles inputfile basename) 58 | function(WAYLAND_ADD_PROTOCOL_SERVER _sources _protocol _basename) 59 | if(NOT WAYLAND_SCANNER_EXECUTABLE) 60 | message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.") 61 | endif() 62 | 63 | get_filename_component(_infile ${_protocol} ABSOLUTE) 64 | set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.h") 65 | set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-protocol.c") 66 | 67 | add_custom_command(OUTPUT "${_server_header}" 68 | COMMAND ${WAYLAND_SCANNER_EXECUTABLE} server-header < ${_infile} > ${_server_header} 69 | DEPENDS ${_infile} VERBATIM) 70 | 71 | add_custom_command(OUTPUT "${_code}" 72 | COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code} 73 | DEPENDS ${_infile} VERBATIM) 74 | 75 | list(APPEND ${_sources} "${_server_header}" "${_code}") 76 | set(${_sources} ${${_sources}} PARENT_SCOPE) 77 | endfunction() 78 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm-win.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "gtktsm-app.h" 34 | #include "gtktsm-terminal.h" 35 | #include "gtktsm-win.h" 36 | #include "shl-macro.h" 37 | 38 | struct _GtkTsmWin { 39 | GtkWindow parent; 40 | }; 41 | 42 | struct _GtkTsmWinClass { 43 | GtkWindowClass parent_class; 44 | }; 45 | 46 | typedef struct _GtkTsmWinPrivate { 47 | GtkTsmTerminal *term; 48 | } GtkTsmWinPrivate; 49 | 50 | G_DEFINE_TYPE_WITH_PRIVATE(GtkTsmWin, gtktsm_win, GTK_TYPE_WINDOW); 51 | 52 | static void win_realize(GtkWidget *widget) 53 | { 54 | GtkTsmWin *win = GTKTSM_WIN(widget); 55 | GdkWindow *w; 56 | GdkRGBA col; 57 | 58 | GTK_WIDGET_CLASS(gtktsm_win_parent_class)->realize(widget); 59 | 60 | w = gtk_widget_get_window(GTK_WIDGET(win)); 61 | col.red = 0.0; 62 | col.green = 0.0; 63 | col.blue = 0.0; 64 | col.alpha = 1.0; 65 | gdk_window_set_background_rgba(w, &col); 66 | } 67 | 68 | static gboolean win_stopped_fn(GtkTsmTerminal *term, gpointer data) 69 | { 70 | GtkTsmWin *win = data; 71 | 72 | gtk_widget_destroy(GTK_WIDGET(win)); 73 | 74 | return FALSE; 75 | } 76 | 77 | static void gtktsm_win_init(GtkTsmWin *win) 78 | { 79 | GtkTsmWinPrivate *p = gtktsm_win_get_instance_private(win); 80 | 81 | gtk_window_set_default_size(GTK_WINDOW(win), 800, 600); 82 | 83 | p->term = gtktsm_terminal_new(); 84 | gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(p->term)); 85 | gtk_widget_show(GTK_WIDGET(p->term)); 86 | 87 | g_signal_connect(G_OBJECT(p->term), 88 | "terminal-stopped", 89 | G_CALLBACK(win_stopped_fn), 90 | win); 91 | } 92 | 93 | static void gtktsm_win_class_init(GtkTsmWinClass *klass) 94 | { 95 | GTK_WIDGET_CLASS(klass)->realize = win_realize; 96 | } 97 | 98 | GtkTsmWin *gtktsm_win_new(GtkTsmApp *app) 99 | { 100 | return g_object_new(GTKTSM_WIN_TYPE, 101 | "application", app, 102 | NULL); 103 | } 104 | 105 | void gtktsm_win_run(GtkTsmWin *win) 106 | { 107 | char **argv = (char*[]) { 108 | getenv("SHELL") ? : _PATH_BSHELL, 109 | NULL 110 | }; 111 | GtkTsmWinPrivate *p; 112 | pid_t pid; 113 | int r; 114 | 115 | if (!win) 116 | return; 117 | 118 | p = gtktsm_win_get_instance_private(win); 119 | pid = gtktsm_terminal_fork(p->term); 120 | if (pid < 0) { 121 | if (pid != -EALREADY) 122 | g_error("gtktsm_terminal_fork() failed: %d", (int)pid); 123 | 124 | return; 125 | } else if (pid != 0) { 126 | /* parent */ 127 | return; 128 | } 129 | 130 | /* child */ 131 | 132 | setenv("TERM", "xterm-256color", 1); 133 | setenv("COLORTERM", "gtktsm", 1); 134 | 135 | r = execve(argv[0], argv, environ); 136 | if (r < 0) 137 | g_error("gtktsm_win_run() execve(%s) failed: %m", 138 | argv[0]); 139 | 140 | _exit(1); 141 | } 142 | 143 | GtkTsmTerminal *gtktsm_win_get_terminal(GtkTsmWin *win) 144 | { 145 | GtkTsmWinPrivate *p; 146 | 147 | if (!win) 148 | return NULL; 149 | 150 | p = gtktsm_win_get_instance_private(win); 151 | return p->term; 152 | } 153 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-ring.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "shl-macro.h" 17 | #include "shl-ring.h" 18 | 19 | #define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1)) 20 | 21 | void shl_ring_flush(struct shl_ring *r) 22 | { 23 | r->start = 0; 24 | r->used = 0; 25 | } 26 | 27 | void shl_ring_clear(struct shl_ring *r) 28 | { 29 | free(r->buf); 30 | memset(r, 0, sizeof(*r)); 31 | } 32 | 33 | /* 34 | * Get data pointers for current ring-buffer data. @vec must be an array of 2 35 | * iovec objects. They are filled according to the data available in the 36 | * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects 37 | * that were filled (0 meaning buffer is empty). 38 | * 39 | * Hint: "struct iovec" is defined in and looks like this: 40 | * struct iovec { 41 | * void *iov_base; 42 | * size_t iov_len; 43 | * }; 44 | */ 45 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec) 46 | { 47 | if (r->used == 0) { 48 | return 0; 49 | } else if (r->start + r->used <= r->size) { 50 | if (vec) { 51 | vec[0].iov_base = &r->buf[r->start]; 52 | vec[0].iov_len = r->used; 53 | } 54 | return 1; 55 | } else { 56 | if (vec) { 57 | vec[0].iov_base = &r->buf[r->start]; 58 | vec[0].iov_len = r->size - r->start; 59 | vec[1].iov_base = r->buf; 60 | vec[1].iov_len = r->used - (r->size - r->start); 61 | } 62 | return 2; 63 | } 64 | } 65 | 66 | /* 67 | * Copy data from the ring buffer into the linear external buffer @buf. Copy 68 | * at most @size bytes. If the ring buffer size is smaller, copy less bytes and 69 | * return the number of bytes copied. 70 | */ 71 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size) 72 | { 73 | size_t l; 74 | 75 | if (size > r->used) 76 | size = r->used; 77 | 78 | if (size > 0) { 79 | l = r->size - r->start; 80 | if (size <= l) { 81 | memcpy(buf, &r->buf[r->start], size); 82 | } else { 83 | memcpy(buf, &r->buf[r->start], l); 84 | memcpy((uint8_t*)buf + l, r->buf, size - l); 85 | } 86 | } 87 | 88 | return size; 89 | } 90 | 91 | /* 92 | * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise 93 | * ring operations will behave incorrectly. 94 | */ 95 | static int ring_resize(struct shl_ring *r, size_t nsize) 96 | { 97 | uint8_t *buf; 98 | size_t l; 99 | 100 | buf = malloc(nsize); 101 | if (!buf) 102 | return -ENOMEM; 103 | 104 | if (r->used > 0) { 105 | l = r->size - r->start; 106 | if (r->used <= l) { 107 | memcpy(buf, &r->buf[r->start], r->used); 108 | } else { 109 | memcpy(buf, &r->buf[r->start], l); 110 | memcpy(&buf[l], r->buf, r->used - l); 111 | } 112 | } 113 | 114 | free(r->buf); 115 | r->buf = buf; 116 | r->size = nsize; 117 | r->start = 0; 118 | 119 | return 0; 120 | } 121 | 122 | /* 123 | * Resize ring-buffer to provide enough room for @add bytes of new data. This 124 | * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on 125 | * success. 126 | */ 127 | static int ring_grow(struct shl_ring *r, size_t add) 128 | { 129 | size_t need; 130 | 131 | if (r->size - r->used >= add) 132 | return 0; 133 | 134 | need = r->used + add; 135 | if (need <= r->used) 136 | return -ENOMEM; 137 | else if (need < 4096) 138 | need = 4096; 139 | 140 | need = SHL_ALIGN_POWER2(need); 141 | if (need == 0) 142 | return -ENOMEM; 143 | 144 | return ring_resize(r, need); 145 | } 146 | 147 | /* 148 | * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it 149 | * is too small. -ENOMEM is returned on OOM, 0 on success. 150 | */ 151 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size) 152 | { 153 | int err; 154 | size_t pos, l; 155 | 156 | if (size == 0) 157 | return 0; 158 | 159 | err = ring_grow(r, size); 160 | if (err < 0) 161 | return err; 162 | 163 | pos = RING_MASK(r, r->start + r->used); 164 | l = r->size - pos; 165 | if (l >= size) { 166 | memcpy(&r->buf[pos], u8, size); 167 | } else { 168 | memcpy(&r->buf[pos], u8, l); 169 | memcpy(r->buf, (const uint8_t*)u8 + l, size - l); 170 | } 171 | 172 | r->used += size; 173 | 174 | return 0; 175 | } 176 | 177 | /* 178 | * Remove @len bytes from the start of the ring-buffer. Note that we protect 179 | * against overflows so removing more bytes than available is safe. 180 | */ 181 | void shl_ring_pull(struct shl_ring *r, size_t size) 182 | { 183 | if (size > r->used) 184 | size = r->used; 185 | 186 | r->start = RING_MASK(r, r->start + size); 187 | r->used -= size; 188 | } 189 | -------------------------------------------------------------------------------- /libtsm/COPYING: -------------------------------------------------------------------------------- 1 | = Authors = 2 | 3 | This software was written by: 4 | David Herrmann 5 | Ran Benita 6 | 7 | = Copyright Notice = 8 | 9 | This software is licensed under the terms of the MIT license. Please see each 10 | source file for the related copyright notice and license. 11 | 12 | If a file does not contain a copright notice, the following license shall 13 | apply: 14 | 15 | Copyright (c) 2011-2013 David Herrmann 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining 18 | a copy of this software and associated documentation files 19 | (the "Software"), to deal in the Software without restriction, including 20 | without limitation the rights to use, copy, modify, merge, publish, 21 | distribute, sublicense, and/or sell copies of the Software, and to 22 | permit persons to whom the Software is furnished to do so, subject to 23 | the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included 26 | in all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 29 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 31 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 32 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 33 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 34 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | == Third-Party Source == 37 | 38 | The hash-table implementation in external/htable.* is copied from ccan and 39 | licensed under the conditions of the LGPL-2.1 or later as published by the FSF: 40 | 41 | Author: Rusty Russel 42 | 43 | The wcwidth() implementation in ./external/wcwidth.c is from: 44 | 45 | Markus Kuhn -- 2007-05-26 (Unicode 5.0) 46 | 47 | Permission to use, copy, modify, and distribute this software 48 | for any purpose and without fee is hereby granted. The author 49 | disclaims all warranties with regard to this software. 50 | 51 | Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c 52 | 53 | UCS-4 to UTF-8 encoding is copied from "terminology": 54 | 55 | Copyright (C) 2012-2012 Carsten Haitzler and various contributors 56 | 57 | All rights reserved. 58 | 59 | Redistribution and use in source and binary forms, with or without 60 | modification, are permitted provided that the following conditions are 61 | met: 62 | 63 | 1. Redistributions of source code must retain the above copyright 64 | notice, this list of conditions and the following disclaimer. 65 | 2. Redistributions in binary form must reproduce the above copyright 66 | notice, this list of conditions and the following disclaimer in the 67 | documentation and/or other materials provided with the distribution. 68 | 69 | THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 70 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 71 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 72 | THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 73 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 74 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 75 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 76 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 77 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 78 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 79 | 80 | The "solarized" color palettes in tsm_vte.c are from: 81 | 82 | Copyright (c) 2011 Ethan Schoonover 83 | 84 | Permission is hereby granted, free of charge, to any person obtaining a copy 85 | of this software and associated documentation files (the "Software"), to deal 86 | in the Software without restriction, including without limitation the rights 87 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 88 | copies of the Software, and to permit persons to whom the Software is 89 | furnished to do so, subject to the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be included in 92 | all copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 95 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 96 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 97 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 98 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 99 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 100 | THE SOFTWARE. 101 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-array.h: -------------------------------------------------------------------------------- 1 | /* 2 | * shl - Dynamic Array 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * A dynamic array implementation 28 | */ 29 | 30 | #ifndef SHL_ARRAY_H 31 | #define SHL_ARRAY_H 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | struct shl_array { 38 | size_t element_size; 39 | size_t length; 40 | size_t size; 41 | void *data; 42 | }; 43 | 44 | #define SHL_ARRAY_AT(_arr, _type, _pos) \ 45 | (&((_type*)shl_array_get_array(_arr))[(_pos)]) 46 | 47 | static inline int shl_array_new(struct shl_array **out, size_t element_size, 48 | size_t initial_size) 49 | { 50 | struct shl_array *arr; 51 | 52 | if (!out || !element_size) 53 | return -EINVAL; 54 | 55 | if (!initial_size) 56 | initial_size = 4; 57 | 58 | arr = malloc(sizeof(*arr)); 59 | if (!arr) 60 | return -ENOMEM; 61 | memset(arr, 0, sizeof(*arr)); 62 | arr->element_size = element_size; 63 | arr->length = 0; 64 | arr->size = initial_size; 65 | 66 | arr->data = malloc(arr->element_size * arr->size); 67 | if (!arr->data) { 68 | free(arr); 69 | return -ENOMEM; 70 | } 71 | 72 | *out = arr; 73 | return 0; 74 | } 75 | 76 | static inline void shl_array_free(struct shl_array *arr) 77 | { 78 | if (!arr) 79 | return; 80 | 81 | free(arr->data); 82 | free(arr); 83 | } 84 | 85 | /* Compute next higher power-of-2 of @v. Returns 4 in case v is 0. */ 86 | static inline size_t shl_array_pow2(size_t v) 87 | { 88 | size_t i; 89 | 90 | if (!v) 91 | return 4; 92 | 93 | --v; 94 | 95 | for (i = 1; i < 8 * sizeof(size_t); i *= 2) 96 | v |= v >> i; 97 | 98 | return ++v; 99 | } 100 | 101 | /* resize to length=size and zero out new array entries */ 102 | static inline int shl_array_zresize(struct shl_array *arr, size_t size) 103 | { 104 | void *tmp; 105 | size_t newsize; 106 | 107 | if (!arr) 108 | return -EINVAL; 109 | 110 | if (size > arr->size) { 111 | newsize = shl_array_pow2(size); 112 | tmp = realloc(arr->data, arr->element_size * newsize); 113 | if (!tmp) 114 | return -ENOMEM; 115 | 116 | arr->data = tmp; 117 | arr->size = newsize; 118 | 119 | memset(((uint8_t*)arr->data) + arr->element_size * arr->length, 120 | 0, arr->element_size * (size - arr->length)); 121 | } 122 | 123 | arr->length = size; 124 | return 0; 125 | } 126 | 127 | static inline int shl_array_push(struct shl_array *arr, const void *data) 128 | { 129 | void *tmp; 130 | size_t newsize; 131 | 132 | if (!arr || !data) 133 | return -EINVAL; 134 | 135 | if (arr->length >= arr->size) { 136 | newsize = arr->size * 2; 137 | tmp = realloc(arr->data, arr->element_size * newsize); 138 | if (!tmp) 139 | return -ENOMEM; 140 | 141 | arr->data = tmp; 142 | arr->size = newsize; 143 | } 144 | 145 | memcpy(((uint8_t*)arr->data) + arr->element_size * arr->length, 146 | data, arr->element_size); 147 | ++arr->length; 148 | 149 | return 0; 150 | } 151 | 152 | static inline void shl_array_pop(struct shl_array *arr) 153 | { 154 | if (!arr || !arr->length) 155 | return; 156 | 157 | --arr->length; 158 | } 159 | 160 | static inline void *shl_array_get_array(struct shl_array *arr) 161 | { 162 | if (!arr) 163 | return NULL; 164 | 165 | return arr->data; 166 | } 167 | 168 | static inline size_t shl_array_get_length(struct shl_array *arr) 169 | { 170 | if (!arr) 171 | return 0; 172 | 173 | return arr->length; 174 | } 175 | 176 | static inline size_t shl_array_get_bsize(struct shl_array *arr) 177 | { 178 | if (!arr) 179 | return 0; 180 | 181 | return arr->length * arr->element_size; 182 | } 183 | 184 | static inline size_t shl_array_get_element_size(struct shl_array *arr) 185 | { 186 | if (!arr) 187 | return 0; 188 | 189 | return arr->element_size; 190 | } 191 | 192 | #endif /* SHL_ARRAY_H */ 193 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Giulio Camuffo 3 | * 4 | * This file is part of Termistor 5 | * 6 | * Termistor is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Termistor is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Termistor. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "vte.h" 29 | #include "terminal.h" 30 | #include "wayland-dropdown-client-protocol.h" 31 | 32 | class Term 33 | { 34 | public: 35 | Term(bool window) 36 | : m_display(nullptr) 37 | , m_registry(nullptr) 38 | , m_dropdown(nullptr) 39 | { 40 | if (QGuiApplication::platformName().contains("wayland")) { 41 | QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); 42 | m_display = static_cast(native->nativeResourceForIntegration("display")); 43 | m_registry = wl_display_get_registry(m_display); 44 | 45 | if (!window) { 46 | wl_registry_add_listener(m_registry, &s_registryListener, this); 47 | wl_display_roundtrip(m_display); 48 | if (!m_dropdown) { 49 | window = true; 50 | } 51 | } 52 | } else { 53 | window = true; 54 | } 55 | 56 | m_term = new Terminal; 57 | m_term->setTitle("Termistor"); 58 | if (!window) { 59 | m_term->setFlags(Qt::BypassWindowManagerHint); 60 | } 61 | 62 | QSurfaceFormat format; 63 | format.setSamples(4); 64 | format.setAlphaBufferSize(8); 65 | m_term->setFormat(format); 66 | QScreen *screen = m_term->screen(); 67 | if (!window) { 68 | m_term->resize(screen->size().width() * 0.9, screen->size().height() * 0.5); 69 | } else { 70 | m_term->resize(500, 400); 71 | } 72 | m_term->show(); 73 | 74 | if (!window) { 75 | wl_surface *wlSurface = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", m_term)); 76 | orbital_dropdown_surface *surface = orbital_dropdown_get_dropdown_surface(m_dropdown, wlSurface); 77 | 78 | static const orbital_dropdown_surface_listener listener = { 79 | [](void *d, orbital_dropdown_surface *surface, int w, int h) { 80 | Term *term = static_cast(d); 81 | term->m_term->resize(w * 0.9, h * 0.5); 82 | term->m_term->update(); 83 | } 84 | }; 85 | orbital_dropdown_surface_add_listener(surface, &listener, this); 86 | } 87 | } 88 | 89 | wl_display *m_display; 90 | wl_registry *m_registry; 91 | orbital_dropdown *m_dropdown; 92 | orbital_dropdown_surface *m_surface; 93 | Terminal *m_term; 94 | static const wl_registry_listener s_registryListener; 95 | }; 96 | 97 | const wl_registry_listener Term::s_registryListener = { 98 | [](void *data, wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { 99 | Term *t = static_cast(data); 100 | 101 | if (strcmp(interface, "orbital_dropdown") == 0) { 102 | t->m_dropdown = static_cast(wl_registry_bind(registry, id, &orbital_dropdown_interface, 1)); 103 | } 104 | }, 105 | [](void *, wl_registry *registry, uint32_t id) {} 106 | }; 107 | 108 | void usage() 109 | { 110 | printf("Usage: termistor [-w]\n\n"); 111 | printf(" -w run in a normal window\n"); 112 | printf(" -h show this help\n"); 113 | } 114 | 115 | int main(int argc, char *argv[]) 116 | { 117 | setenv("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT", "1", 1); 118 | 119 | QDir::setCurrent(QDir::homePath()); 120 | QGuiApplication app(argc, argv); 121 | 122 | bool window = false; 123 | for (int i = 1; i < app.arguments().count(); ++i) { 124 | QString arg = app.arguments().at(i); 125 | if (arg == "-w") { 126 | window = true; 127 | } else if (arg == "-h") { 128 | usage(); 129 | return 0; 130 | } else { 131 | printf("Invalid option \"%s\"\n", qPrintable(arg)); 132 | usage(); 133 | return 1; 134 | } 135 | } 136 | 137 | Term t(window); 138 | return app.exec(); 139 | } 140 | -------------------------------------------------------------------------------- /libtsm/src/tsm/tsm-render.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libtsm - Rendering 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Rendering 28 | * TSM does not depend on any graphics system or rendering libraries. Instead, 29 | * it provides iterators and ageing support so you can implement renderers 30 | * yourself. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "libtsm.h" 39 | #include "libtsm-int.h" 40 | #include "shl-llog.h" 41 | 42 | #define LLOG_SUBSYSTEM "tsm-render" 43 | 44 | SHL_EXPORT 45 | tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, 46 | void *data) 47 | { 48 | unsigned int cur_x, cur_y; 49 | unsigned int i, j, k; 50 | struct line *iter, *line = NULL; 51 | struct cell *cell, empty; 52 | struct tsm_screen_attr attr; 53 | int ret, warned = 0; 54 | const uint32_t *ch; 55 | size_t len; 56 | bool in_sel = false, sel_start = false, sel_end = false; 57 | bool was_sel = false; 58 | tsm_age_t age; 59 | 60 | if (!con || !draw_cb) 61 | return 0; 62 | 63 | screen_cell_init(con, &empty); 64 | 65 | cur_x = con->cursor_x; 66 | if (con->cursor_x >= con->size_x) 67 | cur_x = con->size_x - 1; 68 | cur_y = con->cursor_y; 69 | if (con->cursor_y >= con->size_y) 70 | cur_y = con->size_y - 1; 71 | 72 | /* push each character into rendering pipeline */ 73 | 74 | iter = con->sb_pos; 75 | k = 0; 76 | 77 | if (con->sel_active) { 78 | if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) 79 | in_sel = !in_sel; 80 | if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) 81 | in_sel = !in_sel; 82 | 83 | if (con->sel_start.line && 84 | (!iter || con->sel_start.line->sb_id < iter->sb_id)) 85 | in_sel = !in_sel; 86 | if (con->sel_end.line && 87 | (!iter || con->sel_end.line->sb_id < iter->sb_id)) 88 | in_sel = !in_sel; 89 | } 90 | 91 | for (i = 0; i < con->size_y; ++i) { 92 | if (iter) { 93 | line = iter; 94 | iter = iter->next; 95 | } else { 96 | line = con->lines[k]; 97 | k++; 98 | } 99 | 100 | if (con->sel_active) { 101 | if (con->sel_start.line == line || 102 | (!con->sel_start.line && 103 | con->sel_start.y == k - 1)) 104 | sel_start = true; 105 | else 106 | sel_start = false; 107 | if (con->sel_end.line == line || 108 | (!con->sel_end.line && 109 | con->sel_end.y == k - 1)) 110 | sel_end = true; 111 | else 112 | sel_end = false; 113 | 114 | was_sel = false; 115 | } 116 | 117 | for (j = 0; j < con->size_x; ++j) { 118 | if (j < line->size) 119 | cell = &line->cells[j]; 120 | else 121 | cell = ∅ 122 | 123 | memcpy(&attr, &cell->attr, sizeof(attr)); 124 | 125 | if (con->sel_active) { 126 | if (sel_start && 127 | j == con->sel_start.x) { 128 | was_sel = in_sel; 129 | in_sel = !in_sel; 130 | } 131 | if (sel_end && 132 | j == con->sel_end.x) { 133 | was_sel = in_sel; 134 | in_sel = !in_sel; 135 | } 136 | } 137 | 138 | if (k == cur_y + 1 && j == cur_x && 139 | !(con->flags & TSM_SCREEN_HIDE_CURSOR)) 140 | attr.inverse = !attr.inverse; 141 | 142 | /* TODO: do some more sophisticated inverse here. When 143 | * INVERSE mode is set, we should instead just select 144 | * inverse colors instead of switching background and 145 | * foreground */ 146 | if (con->flags & TSM_SCREEN_INVERSE) 147 | attr.inverse = !attr.inverse; 148 | 149 | if (in_sel || was_sel) { 150 | was_sel = false; 151 | attr.inverse = !attr.inverse; 152 | } 153 | 154 | if (con->age_reset) { 155 | age = 0; 156 | } else { 157 | age = cell->age; 158 | if (line->age > age) 159 | age = line->age; 160 | if (con->age > age) 161 | age = con->age; 162 | } 163 | 164 | ch = tsm_symbol_get(con->sym_table, &cell->ch, &len); 165 | if (cell->ch == ' ' || cell->ch == 0) 166 | len = 0; 167 | ret = draw_cb(con, cell->ch, ch, len, cell->width, 168 | j, i, &attr, age, data); 169 | if (ret && warned++ < 3) { 170 | llog_debug(con, 171 | "cannot draw glyph at %ux%u via text-renderer", 172 | j, i); 173 | if (warned == 3) 174 | llog_debug(con, 175 | "suppressing further warnings during this rendering round"); 176 | } 177 | } 178 | } 179 | 180 | if (con->age_reset) { 181 | con->age_reset = 0; 182 | return 0; 183 | } else { 184 | return con->age_cnt; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /libtsm/src/tsm/libtsm-int.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Main internal header 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef TSM_LIBTSM_INT_H 27 | #define TSM_LIBTSM_INT_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include "libtsm.h" 33 | #include "shl-llog.h" 34 | 35 | #define SHL_EXPORT __attribute__((visibility("default"))) 36 | 37 | /* max combined-symbol length */ 38 | #define TSM_UCS4_MAXLEN 10 39 | 40 | /* symbols */ 41 | 42 | struct tsm_symbol_table; 43 | 44 | extern const tsm_symbol_t tsm_symbol_default; 45 | 46 | int tsm_symbol_table_new(struct tsm_symbol_table **out); 47 | void tsm_symbol_table_ref(struct tsm_symbol_table *tbl); 48 | void tsm_symbol_table_unref(struct tsm_symbol_table *tbl); 49 | 50 | tsm_symbol_t tsm_symbol_make(uint32_t ucs4); 51 | tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, 52 | tsm_symbol_t sym, uint32_t ucs4); 53 | const uint32_t *tsm_symbol_get(struct tsm_symbol_table *tbl, 54 | tsm_symbol_t *sym, size_t *size); 55 | unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, 56 | tsm_symbol_t sym); 57 | 58 | /* utf8 state machine */ 59 | 60 | struct tsm_utf8_mach; 61 | 62 | enum tsm_utf8_mach_state { 63 | TSM_UTF8_START, 64 | TSM_UTF8_ACCEPT, 65 | TSM_UTF8_REJECT, 66 | TSM_UTF8_EXPECT1, 67 | TSM_UTF8_EXPECT2, 68 | TSM_UTF8_EXPECT3, 69 | }; 70 | 71 | int tsm_utf8_mach_new(struct tsm_utf8_mach **out); 72 | void tsm_utf8_mach_free(struct tsm_utf8_mach *mach); 73 | 74 | int tsm_utf8_mach_feed(struct tsm_utf8_mach *mach, char c); 75 | uint32_t tsm_utf8_mach_get(struct tsm_utf8_mach *mach); 76 | void tsm_utf8_mach_reset(struct tsm_utf8_mach *mach); 77 | 78 | /* TSM screen */ 79 | 80 | struct cell { 81 | tsm_symbol_t ch; /* stored character */ 82 | unsigned int width; /* character width */ 83 | struct tsm_screen_attr attr; /* cell attributes */ 84 | tsm_age_t age; /* age of the single cell */ 85 | }; 86 | 87 | struct line { 88 | struct line *next; /* next line (NULL if not sb) */ 89 | struct line *prev; /* prev line (NULL if not sb) */ 90 | 91 | unsigned int size; /* real width */ 92 | struct cell *cells; /* actuall cells */ 93 | uint64_t sb_id; /* sb ID */ 94 | tsm_age_t age; /* age of the whole line */ 95 | }; 96 | 97 | #define SELECTION_TOP -1 98 | struct selection_pos { 99 | struct line *line; 100 | unsigned int x; 101 | int y; 102 | }; 103 | 104 | struct tsm_screen { 105 | size_t ref; 106 | llog_submit_t llog; 107 | void *llog_data; 108 | unsigned int opts; 109 | unsigned int flags; 110 | struct tsm_symbol_table *sym_table; 111 | 112 | /* default attributes for new cells */ 113 | struct tsm_screen_attr def_attr; 114 | 115 | /* ageing */ 116 | tsm_age_t age_cnt; /* current age counter */ 117 | unsigned int age_reset : 1; /* age-overflow flag */ 118 | 119 | /* current buffer */ 120 | unsigned int size_x; /* width of screen */ 121 | unsigned int size_y; /* height of screen */ 122 | unsigned int margin_top; /* top-margin index */ 123 | unsigned int margin_bottom; /* bottom-margin index */ 124 | unsigned int line_num; /* real number of allocated lines */ 125 | struct line **lines; /* active lines; copy of main/alt */ 126 | struct line **main_lines; /* real main lines */ 127 | struct line **alt_lines; /* real alternative lines */ 128 | tsm_age_t age; /* whole screen age */ 129 | 130 | /* scroll-back buffer */ 131 | unsigned int sb_count; /* number of lines in sb */ 132 | struct line *sb_first; /* first line; was moved first */ 133 | struct line *sb_last; /* last line; was moved last*/ 134 | unsigned int sb_max; /* max-limit of lines in sb */ 135 | struct line *sb_pos; /* current position in sb or NULL */ 136 | uint64_t sb_last_id; /* last id given to sb-line */ 137 | 138 | /* cursor: positions are always in-bound, but cursor_x might be 139 | * bigger than size_x if new-line is pending */ 140 | unsigned int cursor_x; /* current cursor x-pos */ 141 | unsigned int cursor_y; /* current cursor y-pos */ 142 | 143 | /* tab ruler */ 144 | bool *tab_ruler; /* tab-flag for all cells of one row */ 145 | 146 | /* selection */ 147 | bool sel_active; 148 | struct selection_pos sel_start; 149 | struct selection_pos sel_end; 150 | }; 151 | 152 | void screen_cell_init(struct tsm_screen *con, struct cell *cell); 153 | 154 | void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts); 155 | void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts); 156 | unsigned int tsm_screen_get_opts(struct tsm_screen *scr); 157 | 158 | static inline void screen_inc_age(struct tsm_screen *con) 159 | { 160 | if (!++con->age_cnt) { 161 | con->age_reset = 1; 162 | ++con->age_cnt; 163 | } 164 | } 165 | 166 | /* available character sets */ 167 | 168 | typedef tsm_symbol_t tsm_vte_charset[96]; 169 | 170 | extern tsm_vte_charset tsm_vte_unicode_lower; 171 | extern tsm_vte_charset tsm_vte_unicode_upper; 172 | extern tsm_vte_charset tsm_vte_dec_supplemental_graphics; 173 | extern tsm_vte_charset tsm_vte_dec_special_graphics; 174 | 175 | #endif /* TSM_LIBTSM_INT_H */ 176 | -------------------------------------------------------------------------------- /libtsm/configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # libtsm - build configuration script 3 | # Copyright (c) 2012-2013 David Herrmann 4 | # 5 | 6 | AC_PREREQ(2.68) 7 | 8 | AC_INIT([libtsm], 9 | [3], 10 | [http://bugs.freedesktop.org/enter_bug.cgi?product=kmscon], 11 | [libtsm], 12 | [http://www.freedesktop.org/wiki/Software/libtsm]) 13 | AC_CONFIG_SRCDIR([src/tsm/libtsm.h]) 14 | AC_CONFIG_AUX_DIR([build-aux]) 15 | AC_CONFIG_MACRO_DIR([m4]) 16 | AC_CONFIG_HEADER(config.h) 17 | AC_USE_SYSTEM_EXTENSIONS 18 | AC_SYS_LARGEFILE 19 | AC_CANONICAL_HOST 20 | 21 | AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects dist-xz no-dist-gzip tar-pax -Wall -Werror -Wno-portability]) 22 | AM_SILENT_RULES([yes]) 23 | 24 | AC_SUBST(PACKAGE_DESCRIPTION, ["terminal-emulator state machine"]) 25 | 26 | # 27 | # Don't add a default "-g -O2" if CFLAGS wasn't specified. For debugging it is 28 | # often more convenient to have "-g -O0". You can still override it by 29 | # explicitly setting it on the command line. 30 | # 31 | 32 | : ${CFLAGS=""} 33 | 34 | AC_PROG_CC 35 | AC_PROG_CC_C99 36 | AM_PROG_CC_C_O 37 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 38 | AC_PROG_SED 39 | AC_PROG_MKDIR_P 40 | AC_PROG_LN_S 41 | AC_PROG_GREP 42 | AC_PROG_AWK 43 | 44 | LT_PREREQ(2.2) 45 | LT_INIT 46 | 47 | # 48 | # We need xkbcommon for keysym definitions. If it's not found, we use our own 49 | # private copy of xkbcommon-keysyms.h. 50 | # 51 | 52 | PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon], 53 | [have_xkbcommon=yes], [have_xkbcommon=no]) 54 | AC_SUBST(XKBCOMMON_CFLAGS) 55 | if test "x$have_xkbcommon" = "xyes" ; then 56 | AC_DEFINE([BUILD_HAVE_XKBCOMMON], [1], [Have xkbcommon library]) 57 | fi 58 | AM_CONDITIONAL([BUILD_HAVE_XKBCOMMON], [test "x$have_xkbcommon" = "xyes"]) 59 | 60 | # 61 | # Test for "check" which we use for our test-suite. If not found, we disable 62 | # all tests. 63 | # 64 | 65 | PKG_CHECK_MODULES([CHECK], [check], 66 | [have_check=yes], [have_check=no]) 67 | AC_SUBST(CHECK_CFLAGS) 68 | AC_SUBST(CHECK_LIBS) 69 | AM_CONDITIONAL([BUILD_HAVE_CHECK], [test "x$have_check" = "xyes"]) 70 | 71 | # 72 | # Optionally, look for gtk+-3 and friends for our gtktsm example. 73 | # This is linux-only as it uses epoll and friends. Therefore, it's disabled by 74 | # default. 75 | # 76 | 77 | AC_MSG_CHECKING([whether to build gtktsm]) 78 | AC_ARG_ENABLE([gtktsm], 79 | [AS_HELP_STRING([--enable-gtktsm], 80 | [whether to build gtktsm])]) 81 | if test "x$enable_gtktsm" = "x" ; then 82 | enable_gtktsm="no (default)" 83 | fi 84 | AC_MSG_RESULT([$enable_gtktsm]) 85 | 86 | PKG_CHECK_MODULES([GTKTSM], [gtk+-3.0 cairo pango pangocairo xkbcommon], 87 | [have_gtktsm=yes], [have_gtktsm=no]) 88 | AC_SUBST(GTKTSM_CFLAGS) 89 | AC_SUBST(GTKTSM_LIBS) 90 | 91 | if test ! "x$have_gtktsm" = "xyes" ; then 92 | if test "x$enable_gtktsm" = "xyes" ; then 93 | AC_ERROR([GtkTsm dependencies not found]) 94 | else 95 | enable_gtktsm="no" 96 | fi 97 | elif test "x${enable_gtktsm% *}" = "xyes" ; then 98 | enable_gtktsm="yes" 99 | else 100 | enable_gtktsm="no" 101 | fi 102 | AM_CONDITIONAL([BUILD_ENABLE_GTKTSM], [test "x$enable_gtktsm" = "xyes"]) 103 | 104 | # 105 | # debug mode 106 | # If --enable-debug is given, we enable several non-standard debug options. We 107 | # enable a lot of debug options by default, so this option is really only for 108 | # extended developer debug modes. 109 | # 110 | 111 | AC_MSG_CHECKING([whether to build with debugging on]) 112 | AC_ARG_ENABLE([debug], 113 | [AS_HELP_STRING([--enable-debug], 114 | [whether to build with debugging on])]) 115 | if test "x$enable_debug" = "x" ; then 116 | enable_debug="yes (default)" 117 | fi 118 | AC_MSG_RESULT([$enable_debug]) 119 | 120 | if test "x${enable_debug% *}" = "xyes" ; then 121 | enable_debug="yes" 122 | AC_DEFINE([BUILD_ENABLE_DEBUG], [1], [Enable debug mode]) 123 | else 124 | enable_debug="no" 125 | AC_DEFINE([NDEBUG], [1], [No Debug]) 126 | fi 127 | AM_CONDITIONAL([BUILD_ENABLE_DEBUG], 128 | [test "x$enable_debug" = "xyes"]) 129 | 130 | # 131 | # Enable gcc compiler optimizations. We enable them by default but allow 132 | # disabling them for better backtraces during debugging. 133 | # 134 | 135 | AC_MSG_CHECKING([whether to enable code optimizations]) 136 | AC_ARG_ENABLE([optimizations], 137 | [AS_HELP_STRING([--disable-optimizations], 138 | [whether to disable code optimizations])]) 139 | if test "x$enable_optimizations" = "x" ; then 140 | enable_optimizations="yes (default)" 141 | fi 142 | AC_MSG_RESULT([$enable_optimizations]) 143 | 144 | if test "x${enable_optimizations% *}" = "xyes" ; then 145 | enable_optimizations="yes" 146 | else 147 | enable_optimizations="no" 148 | fi 149 | AM_CONDITIONAL([BUILD_ENABLE_OPTIMIZATIONS], 150 | [test "x$enable_optimizations" = "xyes"]) 151 | 152 | # 153 | # Check which linker is used 154 | # 155 | 156 | AM_CONDITIONAL([BUILD_HAVE_GNU_LD], 157 | [test "x$with_gnu_ld" = "xyes"]) 158 | 159 | # 160 | # Detect darwin targets 161 | # 162 | 163 | case "$host_os" in 164 | darwin*) 165 | $is_darwin = "xyes";; 166 | esac 167 | 168 | AM_CONDITIONAL([BUILD_IS_DARWIN], 169 | [test "x$is_darwin" = "xyes"]) 170 | 171 | # 172 | # Makefile vars 173 | # After everything is configured, we create all makefiles. 174 | # 175 | 176 | AC_CONFIG_FILES([Makefile 177 | src/tsm/libtsm.pc]) 178 | AC_OUTPUT 179 | 180 | # 181 | # Configuration output 182 | # Show configuration to the user so they can check whether everything was 183 | # configured as expected. 184 | # 185 | 186 | AC_MSG_NOTICE([Build configuration: 187 | 188 | prefix: $prefix 189 | exec-prefix: $exec_prefix 190 | libdir: $libdir 191 | includedir: $includedir 192 | host-os: $host_os 193 | 194 | Miscellaneous Options: 195 | gtktsm: $enable_gtktsm 196 | debug: $enable_debug 197 | optimizations: $enable_optimizations 198 | building tests: $have_check 199 | 200 | Run "${MAKE-make}" to start compilation process]) 201 | -------------------------------------------------------------------------------- /libtsm/src/gtktsm/gtktsm-app.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "gtktsm-app.h" 30 | #include "gtktsm-win.h" 31 | #include "shl-macro.h" 32 | 33 | struct _GtkTsmApp { 34 | GtkApplication parent; 35 | }; 36 | 37 | struct _GtkTsmAppClass { 38 | GtkApplicationClass parent_class; 39 | }; 40 | 41 | G_DEFINE_TYPE(GtkTsmApp, gtktsm_app, GTK_TYPE_APPLICATION); 42 | 43 | static gboolean arg_version; 44 | 45 | static GOptionEntry app_options[] = { 46 | { "version", 0, 0, G_OPTION_ARG_NONE, &arg_version, "Show version information and exit", NULL }, 47 | { "font", 0, 0, G_OPTION_ARG_STRING, NULL, "Terminal font", "FONT" }, 48 | { "sb-size", 0, 0, G_OPTION_ARG_INT, NULL, "Scroll-back buffer size in lines", "COUNT" }, 49 | { "anti-aliasing", 0, 0, G_OPTION_ARG_STRING, NULL, "Anti-aliasing mode for font rendering", "{none,gray,subpixel,default}" }, 50 | { "subpixel-order", 0, 0, G_OPTION_ARG_STRING, NULL, "Subpixel order for font rendering", "{rgb,bgr,vrgb,vbgr,default}" }, 51 | { "show-dirty", 0, 0, G_OPTION_ARG_NONE, NULL, "Mark dirty cells during redraw", NULL }, 52 | { "debug", 0, 0, G_OPTION_ARG_NONE, NULL, "Enable extensive live-debugging", NULL }, 53 | { NULL } 54 | }; 55 | 56 | static gint app_handle_local_options(GApplication *gapp, 57 | GVariantDict *opts) 58 | { 59 | if (arg_version) { 60 | g_print("GtkTsm\n"); 61 | return 0; 62 | } 63 | 64 | return -1; 65 | } 66 | 67 | static gint app_command_line(GApplication *gapp, 68 | GApplicationCommandLine *cmd) 69 | { 70 | GtkTsmApp *app = GTKTSM_APP(gapp); 71 | GtkTsmWin *win; 72 | GtkTsmTerminal *term; 73 | GVariantDict *dict; 74 | const gchar *sval; 75 | GVariant *val; 76 | int r; 77 | 78 | win = gtktsm_win_new(app); 79 | term = gtktsm_win_get_terminal(win); 80 | dict = g_application_command_line_get_options_dict(cmd); 81 | 82 | val = g_variant_dict_lookup_value(dict, 83 | "font", 84 | G_VARIANT_TYPE_STRING); 85 | if (val) 86 | g_object_set(G_OBJECT(term), 87 | "font", g_variant_get_string(val, NULL), 88 | NULL); 89 | 90 | val = g_variant_dict_lookup_value(dict, 91 | "sb-size", 92 | G_VARIANT_TYPE_INT32); 93 | if (val) 94 | g_object_set(G_OBJECT(term), 95 | "sb-size", shl_max(g_variant_get_int32(val), 0), 96 | NULL); 97 | 98 | val = g_variant_dict_lookup_value(dict, 99 | "anti-aliasing", 100 | G_VARIANT_TYPE_STRING); 101 | if (val) { 102 | sval = g_variant_get_string(val, NULL); 103 | if (strcmp(sval, "none") && 104 | strcmp(sval, "gray") && 105 | strcmp(sval, "subpixel") && 106 | strcmp(sval, "default")) { 107 | g_application_command_line_printerr(cmd, 108 | "invalid anti-aliasing argument: %s\n", 109 | sval); 110 | r = 1; 111 | goto error; 112 | } 113 | 114 | g_object_set(G_OBJECT(term), 115 | "anti-aliasing", sval, 116 | NULL); 117 | } 118 | 119 | val = g_variant_dict_lookup_value(dict, 120 | "subpixel-order", 121 | G_VARIANT_TYPE_STRING); 122 | if (val) { 123 | sval = g_variant_get_string(val, NULL); 124 | if (strcmp(sval, "rgb") && 125 | strcmp(sval, "bgr") && 126 | strcmp(sval, "vrgb") && 127 | strcmp(sval, "vbgr") && 128 | strcmp(sval, "default")) { 129 | g_application_command_line_printerr(cmd, 130 | "invalid subpixel-order argument: %s\n", 131 | sval); 132 | r = 1; 133 | goto error; 134 | } 135 | 136 | g_object_set(G_OBJECT(term), 137 | "subpixel-order", sval, 138 | NULL); 139 | } 140 | 141 | val = g_variant_dict_lookup_value(dict, 142 | "show-dirty", 143 | G_VARIANT_TYPE_BOOLEAN); 144 | if (val) 145 | g_object_set(G_OBJECT(term), 146 | "show-dirty", g_variant_get_boolean(val), 147 | NULL); 148 | 149 | val = g_variant_dict_lookup_value(dict, 150 | "debug", 151 | G_VARIANT_TYPE_BOOLEAN); 152 | if (val) 153 | g_object_set(G_OBJECT(term), 154 | "debug", g_variant_get_boolean(val), 155 | NULL); 156 | 157 | gtktsm_win_run(win); 158 | gtk_window_present(GTK_WINDOW(win)); 159 | 160 | return 0; 161 | 162 | error: 163 | gtk_widget_destroy(GTK_WIDGET(win)); 164 | return r; 165 | } 166 | 167 | static void app_activate(GApplication *gapp) 168 | { 169 | GtkTsmApp *app = GTKTSM_APP(gapp); 170 | GtkTsmWin *win; 171 | 172 | win = gtktsm_win_new(app); 173 | gtktsm_win_run(win); 174 | gtk_window_present(GTK_WINDOW(win)); 175 | } 176 | 177 | static void gtktsm_app_init(GtkTsmApp *app) 178 | { 179 | g_application_add_main_option_entries(G_APPLICATION(app), 180 | app_options); 181 | } 182 | 183 | static void gtktsm_app_class_init(GtkTsmAppClass *klass) 184 | { 185 | GApplicationClass *gapp_klass; 186 | 187 | gapp_klass = G_APPLICATION_CLASS(klass); 188 | gapp_klass->handle_local_options = app_handle_local_options; 189 | gapp_klass->command_line = app_command_line; 190 | gapp_klass->activate = app_activate; 191 | } 192 | 193 | GtkTsmApp *gtktsm_app_new(void) 194 | { 195 | return g_object_new(GTKTSM_APP_TYPE, 196 | "application-id", "org.freedesktop.libtsm.gtktsm", 197 | "flags", G_APPLICATION_SEND_ENVIRONMENT | 198 | G_APPLICATION_HANDLES_COMMAND_LINE, 199 | NULL); 200 | } 201 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-htable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 6 | */ 7 | 8 | /* 9 | * Dynamic hash-table 10 | * Implementation of a self-resizing hashtable to store arbitrary objects. 11 | * Entries are not allocated by the table itself but are user-allocated. A 12 | * single entry can be stored multiple times in the hashtable. No 13 | * maintenance-members need to be embedded in user-allocated objects. However, 14 | * the key (and optionally the hash) must be stored in the objects. 15 | * 16 | * Uses internally the htable from CCAN. See LICENSE_htable. 17 | */ 18 | 19 | #ifndef SHL_HTABLE_H 20 | #define SHL_HTABLE_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* miscellaneous */ 29 | 30 | #define shl_htable_offsetof(pointer, type, member) ({ \ 31 | const typeof(((type*)0)->member) *__ptr = (pointer); \ 32 | (type*)(((char*)__ptr) - offsetof(type, member)); \ 33 | }) 34 | 35 | /* htable */ 36 | 37 | struct shl_htable_int { 38 | size_t (*rehash)(const void *elem, void *priv); 39 | void *priv; 40 | unsigned int bits; 41 | size_t elems, deleted, max, max_with_deleted; 42 | /* These are the bits which are the same in all pointers. */ 43 | uintptr_t common_mask, common_bits; 44 | uintptr_t perfect_bit; 45 | uintptr_t *table; 46 | }; 47 | 48 | struct shl_htable { 49 | bool (*compare) (const void *a, const void *b); 50 | struct shl_htable_int htable; 51 | }; 52 | 53 | #define SHL_HTABLE_INIT(_obj, _compare, _rehash, _priv) \ 54 | { \ 55 | .compare = (_compare), \ 56 | .htable = { \ 57 | .rehash = (_rehash), \ 58 | .priv = (_priv), \ 59 | .bits = 0, \ 60 | .elems = 0, \ 61 | .deleted = 0, \ 62 | .max = 0, \ 63 | .max_with_deleted = 0, \ 64 | .common_mask = -1, \ 65 | .common_bits = 0, \ 66 | .perfect_bit = 0, \ 67 | .table = &(_obj).htable.perfect_bit \ 68 | } \ 69 | } 70 | 71 | void shl_htable_init(struct shl_htable *htable, 72 | bool (*compare) (const void *a, const void *b), 73 | size_t (*rehash)(const void *elem, void *priv), 74 | void *priv); 75 | void shl_htable_clear(struct shl_htable *htable, 76 | void (*free_cb) (void *elem, void *ctx), 77 | void *ctx); 78 | void shl_htable_visit(struct shl_htable *htable, 79 | void (*visit_cb) (void *elem, void *ctx), 80 | void *ctx); 81 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 82 | void **out); 83 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash); 84 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 85 | void **out); 86 | 87 | /* ulong htables */ 88 | 89 | #if SIZE_MAX < ULONG_MAX 90 | # error "'size_t' is smaller than 'unsigned long'" 91 | #endif 92 | 93 | bool shl_htable_compare_ulong(const void *a, const void *b); 94 | size_t shl_htable_rehash_ulong(const void *elem, void *priv); 95 | 96 | #define SHL_HTABLE_INIT_ULONG(_obj) \ 97 | SHL_HTABLE_INIT((_obj), shl_htable_compare_ulong, \ 98 | shl_htable_rehash_ulong, \ 99 | NULL) 100 | 101 | static inline void shl_htable_init_ulong(struct shl_htable *htable) 102 | { 103 | shl_htable_init(htable, shl_htable_compare_ulong, 104 | shl_htable_rehash_ulong, NULL); 105 | } 106 | 107 | static inline void shl_htable_clear_ulong(struct shl_htable *htable, 108 | void (*cb) (unsigned long *elem, 109 | void *ctx), 110 | void *ctx) 111 | { 112 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 113 | } 114 | 115 | static inline void shl_htable_visit_ulong(struct shl_htable *htable, 116 | void (*cb) (unsigned long *elem, 117 | void *ctx), 118 | void *ctx) 119 | { 120 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 121 | } 122 | 123 | static inline bool shl_htable_lookup_ulong(struct shl_htable *htable, 124 | unsigned long key, 125 | unsigned long **out) 126 | { 127 | return shl_htable_lookup(htable, (const void*)&key, (size_t)key, 128 | (void**)out); 129 | } 130 | 131 | static inline int shl_htable_insert_ulong(struct shl_htable *htable, 132 | const unsigned long *key) 133 | { 134 | return shl_htable_insert(htable, (const void*)key, (size_t)*key); 135 | } 136 | 137 | static inline bool shl_htable_remove_ulong(struct shl_htable *htable, 138 | unsigned long key, 139 | unsigned long **out) 140 | { 141 | return shl_htable_remove(htable, (const void*)&key, (size_t)key, 142 | (void**)out); 143 | } 144 | 145 | /* string htables */ 146 | 147 | bool shl_htable_compare_str(const void *a, const void *b); 148 | size_t shl_htable_rehash_str(const void *elem, void *priv); 149 | 150 | #define SHL_HTABLE_INIT_STR(_obj) \ 151 | SHL_HTABLE_INIT((_obj), shl_htable_compare_str, \ 152 | shl_htable_rehash_str, \ 153 | NULL) 154 | 155 | static inline void shl_htable_init_str(struct shl_htable *htable) 156 | { 157 | shl_htable_init(htable, shl_htable_compare_str, 158 | shl_htable_rehash_str, NULL); 159 | } 160 | 161 | static inline void shl_htable_clear_str(struct shl_htable *htable, 162 | void (*cb) (char **elem, 163 | void *ctx), 164 | void *ctx) 165 | { 166 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 167 | } 168 | 169 | static inline void shl_htable_visit_str(struct shl_htable *htable, 170 | void (*cb) (char **elem, 171 | void *ctx), 172 | void *ctx) 173 | { 174 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 175 | } 176 | 177 | static inline size_t shl_htable_hash_str(struct shl_htable *htable, 178 | const char *str, size_t *hash) 179 | { 180 | size_t h; 181 | 182 | if (hash && *hash) { 183 | h = *hash; 184 | } else { 185 | h = htable->htable.rehash((const void*)&str, NULL); 186 | if (hash) 187 | *hash = h; 188 | } 189 | 190 | return h; 191 | } 192 | 193 | static inline bool shl_htable_lookup_str(struct shl_htable *htable, 194 | const char *str, size_t *hash, 195 | char ***out) 196 | { 197 | size_t h; 198 | 199 | h = shl_htable_hash_str(htable, str, hash); 200 | return shl_htable_lookup(htable, (const void*)&str, h, (void**)out); 201 | } 202 | 203 | static inline int shl_htable_insert_str(struct shl_htable *htable, 204 | char **str, size_t *hash) 205 | { 206 | size_t h; 207 | 208 | h = shl_htable_hash_str(htable, *str, hash); 209 | return shl_htable_insert(htable, (const void*)str, h); 210 | } 211 | 212 | static inline bool shl_htable_remove_str(struct shl_htable *htable, 213 | const char *str, size_t *hash, 214 | char ***out) 215 | { 216 | size_t h; 217 | 218 | h = shl_htable_hash_str(htable, str, hash); 219 | return shl_htable_remove(htable, (const void*)&str, h, (void **)out); 220 | } 221 | 222 | #endif /* SHL_HTABLE_H */ 223 | -------------------------------------------------------------------------------- /libtsm/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # libtsm - Global Makefile 3 | # Copyright (c) 2012-2013 David Herrmann 4 | # 5 | 6 | # 7 | # Library Version Numbers 8 | # 9 | 10 | LIBTSM_CURRENT = 3 11 | LIBTSM_REVISION = 0 12 | LIBTSM_AGE = 0 13 | 14 | # 15 | # Global Configurations and Initializations 16 | # 17 | 18 | ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} 19 | AM_MAKEFLAGS = --no-print-directory 20 | AUTOMAKE_OPTIONS = color-tests 21 | AM_DISTCHECK_CONFIGURE_FLAGS = \ 22 | --enable-debug \ 23 | --enable-optimizations 24 | 25 | SUBDIRS = . 26 | 27 | .DELETE_ON_ERROR: 28 | 29 | include_HEADERS = 30 | EXTRA_DIST = \ 31 | README \ 32 | COPYING \ 33 | LICENSE_htable \ 34 | NEWS 35 | CLEANFILES = 36 | pkgconfigdir = $(libdir)/pkgconfig 37 | pkgconfig_DATA = 38 | TPHONY = 39 | 40 | TESTS = 41 | MEMTESTS = 42 | check_PROGRAMS = 43 | bin_PROGRAMS = 44 | lib_LTLIBRARIES = 45 | noinst_LTLIBRARIES = 46 | 47 | # 48 | # Default CFlags 49 | # Make all files include "config.h" by default. This shouldn't cause any 50 | # problems and we cannot forget to include it anymore. 51 | # 52 | # Also make the linker discard all unused symbols. 53 | # 54 | # When compiling in debug mode, we enable debug symbols so debugging with gdb 55 | # is easier. If optimizations are disabled, we pass -O0 to the compiler. 56 | # Otherwise, we use standard optimizations -O2. 57 | # 58 | 59 | AM_CFLAGS = \ 60 | -Wall \ 61 | -pipe \ 62 | -fno-common \ 63 | -ffast-math \ 64 | -fdiagnostics-show-option \ 65 | -fno-strict-aliasing \ 66 | -fvisibility=hidden \ 67 | -ffunction-sections \ 68 | -fdata-sections 69 | AM_CPPFLAGS = \ 70 | -include $(top_builddir)/config.h \ 71 | -I $(srcdir)/src \ 72 | -I $(srcdir)/src/shared 73 | AM_LDFLAGS = 74 | 75 | if BUILD_HAVE_GNU_LD 76 | AM_LDFLAGS += \ 77 | -Wl,--as-needed \ 78 | -Wl,--gc-sections \ 79 | -Wl,-z,relro \ 80 | -Wl,-z,now 81 | else 82 | if BUILD_IS_DARWIN 83 | AM_LDFLAGS += \ 84 | -Wl,-dead_strip \ 85 | -Wl,-dead_strip_dylibs \ 86 | -Wl,-bind_at_load 87 | endif 88 | endif 89 | 90 | if BUILD_ENABLE_DEBUG 91 | AM_CFLAGS += -g 92 | endif 93 | 94 | if BUILD_ENABLE_OPTIMIZATIONS 95 | AM_CFLAGS += -O2 96 | else 97 | AM_CFLAGS += -O0 98 | endif 99 | 100 | # 101 | # SHL - Static Helper Library 102 | # The SHL subsystem contains several small code pieces used all over libtsm and 103 | # other applications. 104 | # 105 | 106 | noinst_LTLIBRARIES += libshl.la 107 | 108 | libshl_la_SOURCES = \ 109 | src/shared/shl-array.h \ 110 | src/shared/shl-htable.h \ 111 | src/shared/shl-htable.c \ 112 | src/shared/shl-llog.h \ 113 | src/shared/shl-macro.h \ 114 | src/shared/shl-ring.h \ 115 | src/shared/shl-ring.c 116 | libshl_la_CPPFLAGS = $(AM_CPPFLAGS) 117 | libshl_la_LDFLAGS = $(AM_LDFLAGS) 118 | libshl_la_LIBADD = $(AM_LIBADD) 119 | 120 | # 121 | # libtsm 122 | # Main library build instructions 123 | # 124 | 125 | lib_LTLIBRARIES += libtsm.la 126 | noinst_LTLIBRARIES += libtsm-test.la 127 | include_HEADERS += src/tsm/libtsm.h 128 | pkgconfig_DATA += src/tsm/libtsm.pc 129 | EXTRA_DIST += \ 130 | src/tsm/libtsm.pc.in \ 131 | src/tsm/libtsm.sym 132 | 133 | libtsm_la_SOURCES = \ 134 | src/tsm/libtsm.h \ 135 | src/tsm/libtsm-int.h \ 136 | src/tsm/tsm-render.c \ 137 | src/tsm/tsm-screen.c \ 138 | src/tsm/tsm-selection.c \ 139 | src/tsm/tsm-unicode.c \ 140 | src/tsm/tsm-vte.c \ 141 | src/tsm/tsm-vte-charsets.c \ 142 | external/wcwidth.h \ 143 | external/wcwidth.c \ 144 | external/xkbcommon-keysyms.h 145 | libtsm_test_la_SOURCES = $(libtsm_la_SOURCES) 146 | 147 | libtsm_la_CPPFLAGS = $(AM_CPPFLAGS) 148 | libtsm_test_la_CPPFLAGS = $(AM_CPPFLAGS) 149 | 150 | libtsm_la_LIBADD = libshl.la 151 | libtsm_test_la_LIBADD = libshl.la 152 | 153 | EXTRA_libtsm_la_DEPENDENCIES = $(top_srcdir)/src/tsm/libtsm.sym 154 | 155 | libtsm_la_LDFLAGS = \ 156 | $(AM_LDFLAGS) \ 157 | -version-info $(LIBTSM_CURRENT):$(LIBTSM_REVISION):$(LIBTSM_AGE) 158 | 159 | if BUILD_HAVE_GNU_LD 160 | libtsm_la_LDFLAGS += \ 161 | -Wl,--version-script="$(top_srcdir)/src/tsm/libtsm.sym" 162 | endif 163 | 164 | libtsm_test_la_LDFLAGS = \ 165 | $(AM_LDFLAGS) 166 | 167 | if BUILD_HAVE_XKBCOMMON 168 | libtsm_la_CPPFLAGS += $(XKBCOMMON_CFLAGS) 169 | libtsm_test_la_CPPFLAGS += $(XKBCOMMON_CFLAGS) 170 | endif 171 | 172 | # 173 | # GtkTsm - Example 174 | # 175 | 176 | if BUILD_ENABLE_GTKTSM 177 | bin_PROGRAMS += gtktsm 178 | endif 179 | 180 | gtktsm_SOURCES = \ 181 | src/gtktsm/gtktsm.c \ 182 | src/gtktsm/gtktsm-app.h \ 183 | src/gtktsm/gtktsm-app.c \ 184 | src/gtktsm/gtktsm-terminal.h \ 185 | src/gtktsm/gtktsm-terminal.c \ 186 | src/gtktsm/gtktsm-win.h \ 187 | src/gtktsm/gtktsm-win.c \ 188 | src/shared/shl-pty.h \ 189 | src/shared/shl-pty.c 190 | gtktsm_CPPFLAGS = \ 191 | $(AM_CPPFLAGS) \ 192 | $(GTKTSM_CFLAGS) 193 | gtktsm_LDADD = \ 194 | -lm \ 195 | libshl.la \ 196 | libtsm.la \ 197 | $(GTKTSM_LIBS) 198 | gtktsm_LDFLAGS = \ 199 | $(AM_LDFLAGS) 200 | 201 | # 202 | # Tests 203 | # We add a separate "memcheck" target which runs valgrind on all tests in 204 | # MEMTESTS. Note that we fail if _any_ leak is detected by valgrind. Thus, you 205 | # need to have valgrind installed and libcheck running properly (without leaks) 206 | # to make memcheck succeed. 207 | # A separate memcheck-verify actually runs a faulty test and verifies the 208 | # valgrind tests work properly. 209 | # 210 | 211 | if BUILD_HAVE_CHECK 212 | check_PROGRAMS += \ 213 | test_htable \ 214 | test_symbol \ 215 | test_valgrind 216 | TESTS += \ 217 | test_htable \ 218 | test_symbol \ 219 | test_valgrind 220 | MEMTESTS += \ 221 | test_htable \ 222 | test_symbol 223 | endif 224 | 225 | test_sources = \ 226 | test/test_common.h 227 | test_libs = \ 228 | libshl.la \ 229 | libtsm-test.la \ 230 | $(CHECK_LIBS) 231 | test_cflags = \ 232 | $(AM_CPPFLAGS) \ 233 | $(CHECK_CFLAGS) 234 | test_lflags = \ 235 | $(AM_LDFLAGS) 236 | 237 | test_htable_SOURCES = test/test_htable.c $(test_sources) 238 | test_htable_CPPFLAGS = $(test_cflags) 239 | test_htable_LDADD = $(test_libs) 240 | test_htable_LDFLAGS = $(test_lflags) 241 | 242 | test_symbol_SOURCES = test/test_symbol.c $(test_sources) 243 | test_symbol_CPPFLAGS = $(test_cflags) 244 | test_symbol_LDADD = $(test_libs) 245 | test_symbol_LDFLAGS = $(test_lflags) 246 | 247 | test_valgrind_SOURCES = test/test_valgrind.c $(test_sources) 248 | test_valgrind_CPPFLAGS = $(test_cflags) 249 | test_valgrind_LDADD = $(test_libs) 250 | test_valgrind_LDFLAGS = $(test_lflags) 251 | 252 | EXTRA_DIST += test.supp 253 | 254 | VALGRIND = CK_FORK=no valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --leak-resolution=high --error-exitcode=1 --suppressions=$(top_builddir)/test.supp 255 | 256 | # verify that test_valgrind actually leaks data 257 | memcheck-verify: check 258 | $(AM_V_GEN)$(VALGRIND) --log-file=/dev/null ./test_valgrind >/dev/null ; test 1 = $$? 259 | 260 | TPHONY += memcheck-verify 261 | 262 | # run memcheck tests via valgrind 263 | memcheck: memcheck-verify 264 | $(AM_V_GEN)for i in $(MEMTESTS) ; do \ 265 | $(VALGRIND) --log-file=$(top_builddir)/$$i.memlog \ 266 | $(top_builddir)/$$i >/dev/null || (echo "memcheck failed on: $$i" ; exit 1) ; \ 267 | done 268 | 269 | TPHONY += memcheck memcheck-verify 270 | 271 | distcheck-hook: memcheck 272 | 273 | # 274 | # Phony targets 275 | # 276 | 277 | .PHONY: $(TPHONY) 278 | 279 | # 280 | # Empty .SECONDARY target causes alle intermediate files to be treated as 281 | # secondary files. That is, they don't get deleted after make finished. 282 | # 283 | 284 | .SECONDARY: 285 | -------------------------------------------------------------------------------- /src/vte.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Giulio Camuffo 3 | * 4 | * This file is part of Termistor 5 | * 6 | * Termistor is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Termistor is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Termistor. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "vte.h" 32 | #include "screen.h" 33 | #include "terminal.h" 34 | 35 | static const char *sev2str_table[] = { 36 | "FATAL", 37 | "ALERT", 38 | "CRITICAL", 39 | "ERROR", 40 | "WARNING", 41 | "NOTICE", 42 | "INFO", 43 | "DEBUG" 44 | }; 45 | 46 | static const char *sev2str(unsigned int sev) 47 | { 48 | if (sev > 7) 49 | return "DEBUG"; 50 | 51 | return sev2str_table[sev]; 52 | } 53 | 54 | static void log(void *data, const char *file, int line, const char *func, const char *subs, unsigned int sev, const char *format, va_list args) 55 | { 56 | char msg[256]; 57 | int n = snprintf(msg, 256, "%s: %s: ", sev2str(sev), subs); 58 | vsnprintf(msg + n, 256 - n, format, args); 59 | // fprintf(stderr, "%s \n",msg); 60 | Debugger::print(msg); 61 | } 62 | 63 | 64 | static uint8_t color_palette_solarized_white[TSM_COLOR_NUM][3] = { 65 | [TSM_COLOR_BLACK] = { 63, 63, 63 }, /* black */ 66 | [TSM_COLOR_RED] = { 112, 80, 80 }, /* red */ 67 | [TSM_COLOR_GREEN] = { 96, 180, 138 }, /* green */ 68 | [TSM_COLOR_YELLOW] = { 223, 175, 143 }, /* yellow */ 69 | [TSM_COLOR_BLUE] = { 154, 184, 215 }, /* blue */ 70 | [TSM_COLOR_MAGENTA] = { 220, 140, 195 }, /* magenta */ 71 | [TSM_COLOR_CYAN] = { 140, 208, 211 }, /* cyan */ 72 | [TSM_COLOR_LIGHT_GREY] = { 238, 232, 213 }, /* light grey */ 73 | [TSM_COLOR_DARK_GREY] = { 112, 144, 128 }, /* dark grey */ 74 | [TSM_COLOR_LIGHT_RED] = { 220, 163, 163 }, /* light red */ 75 | [TSM_COLOR_LIGHT_GREEN] = { 114, 213, 163 }, /* light green */ 76 | [TSM_COLOR_LIGHT_YELLOW] = { 240, 223, 175 }, /* light yellow */ 77 | [TSM_COLOR_LIGHT_BLUE] = { 148, 191, 243 }, /* light blue */ 78 | [TSM_COLOR_LIGHT_MAGENTA] = { 236, 147, 211 }, /* light magenta */ 79 | [TSM_COLOR_LIGHT_CYAN] = { 147, 224, 227 }, /* light cyan */ 80 | [TSM_COLOR_WHITE] = { 253, 246, 227 }, /* white */ 81 | 82 | [TSM_COLOR_FOREGROUND] = { 220, 220, 204 }, /* black */ 83 | [TSM_COLOR_BACKGROUND] = { 44, 44, 44 }, /* light grey */ 84 | }; 85 | 86 | VTE::VTE(Screen *screen) 87 | : QObject(screen) 88 | , m_termScreen(screen) 89 | { 90 | if (tsm_screen_new(&m_screen, log, 0) < 0) { 91 | tsm_screen_unref(m_screen); 92 | qFatal("Failed to create tsm screen"); 93 | } 94 | 95 | if (tsm_vte_new(&m_vte, m_screen, [](tsm_vte *, const char *u8, size_t len, void *data) { 96 | static_cast(data)->vte_event(u8, len); }, this, log, 0) < 0) { 97 | tsm_vte_unref(m_vte); 98 | tsm_screen_unref(m_screen); 99 | qFatal("Failed to create tsm vte"); 100 | } 101 | tsm_vte_set_palette_colors(m_vte, color_palette_solarized_white, TSM_COLOR_NUM); 102 | tsm_screen_set_max_sb(m_screen, 10000); 103 | 104 | pid_t pid = forkpty(&m_master, NULL, NULL, NULL); 105 | if (pid == 0) { 106 | char **argv = new char*[3]; 107 | argv[0] = getenv("SHELL") ? : strdup("/bin/sh"); 108 | argv[1] = strdup("-i"); 109 | argv[2] = 0; 110 | 111 | setenv("TERM", "xterm-256color", 1); 112 | execve(argv[0], argv, environ); 113 | fprintf(stderr, "exec failed: %m\n"); 114 | exit(EXIT_FAILURE); 115 | } else if (pid < 0) { 116 | fprintf(stderr, "failed to fork and create pty (%d): %m\n", errno); 117 | } 118 | 119 | fcntl(m_master, F_SETFL, O_NONBLOCK); 120 | m_notifier = new QSocketNotifier(m_master, QSocketNotifier::Read, this); 121 | connect(m_notifier, &QSocketNotifier::activated, this, &VTE::onSocketActivated); 122 | } 123 | 124 | VTE::~VTE() 125 | { 126 | tsm_vte_unref(m_vte); 127 | tsm_screen_unref(m_screen); 128 | } 129 | 130 | void VTE::resize(int rows, int cols) 131 | { 132 | struct winsize ws; 133 | memset(&ws, 0, sizeof(ws)); 134 | ws.ws_col = cols; 135 | ws.ws_row = rows; 136 | ioctl(m_master, TIOCSWINSZ, &ws); 137 | } 138 | 139 | void VTE::paste(const QByteArray &data) 140 | { 141 | QByteArray d = data; 142 | d.replace('\n', '\r'); 143 | vte_event(d.constData(), data.length()); 144 | } 145 | 146 | void VTE::vte_event(const char *u8, size_t len) { 147 | QFile file; 148 | file.open(m_master, QIODevice::WriteOnly); 149 | file.write(u8, len); 150 | } 151 | 152 | void VTE::onSocketActivated(int socket) { 153 | QFile file; 154 | file.open(socket, QIODevice::ReadOnly); 155 | QByteArray data = file.readAll(); 156 | if (data.length() == 0) { 157 | Debugger::print("No data read. Exiting."); 158 | m_termScreen->close(); 159 | return; 160 | } 161 | tsm_vte_input(m_vte, data.constData(), data.length()); 162 | m_termScreen->update(); 163 | 164 | } 165 | 166 | struct { 167 | int qtkey; 168 | int sym; 169 | } keysyms[] = { 170 | { Qt::Key_Left, XKB_KEY_Left }, 171 | { Qt::Key_Up, XKB_KEY_Up }, 172 | { Qt::Key_Right, XKB_KEY_Right }, 173 | { Qt::Key_Down, XKB_KEY_Down }, 174 | { Qt::Key_Home, XKB_KEY_Home }, 175 | { Qt::Key_End, XKB_KEY_End }, 176 | { Qt::Key_Delete, XKB_KEY_Delete }, 177 | { Qt::Key_PageUp, XKB_KEY_Page_Up }, 178 | { Qt::Key_PageDown, XKB_KEY_Page_Down } 179 | }; 180 | 181 | static int findSym(int key) 182 | { 183 | if (key >= Qt::Key_F1 && key <= Qt::Key_F12) { 184 | return XKB_KEY_F1 + key - Qt::Key_F1; 185 | } 186 | 187 | for (unsigned int i = 0; i < sizeof(keysyms) / sizeof(keysyms[0]); ++i) { 188 | if (keysyms[i].qtkey == key) { 189 | return keysyms[i].sym; 190 | } 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | void VTE::keyPress(int key, Qt::KeyboardModifiers modifiers, const QString &string) 197 | { 198 | if (modifiers & Qt::ShiftModifier) { 199 | if (key == Qt::Key_PageUp) { 200 | tsm_screen_sb_page_up(m_screen, 1); 201 | m_termScreen->update(); 202 | return; 203 | } 204 | if (key == Qt::Key_PageDown) { 205 | tsm_screen_sb_page_down(m_screen, 1); 206 | m_termScreen->update(); 207 | return; 208 | } 209 | } 210 | 211 | QChar c = string.data()[0]; 212 | 213 | int mods = 0; 214 | if (modifiers & Qt::ShiftModifier) mods |= TSM_SHIFT_MASK; 215 | if (modifiers & Qt::ControlModifier) mods |= TSM_CONTROL_MASK; 216 | if (modifiers & Qt::AltModifier) mods |= TSM_ALT_MASK; 217 | if (modifiers & Qt::MetaModifier) mods |= TSM_LOGO_MASK; 218 | 219 | uint32_t ucs4 = c.unicode(); 220 | if (!ucs4) { 221 | ucs4 = TSM_VTE_INVALID; 222 | } 223 | 224 | if (tsm_vte_handle_keyboard(m_vte, findSym(key), c.toLatin1(), mods, ucs4)) { 225 | tsm_screen_sb_reset(m_screen); 226 | } 227 | } 228 | 229 | 230 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Macros 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Macros 10 | */ 11 | 12 | #ifndef SHL_MACRO_H 13 | #define SHL_MACRO_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* sanity checks required for some macros */ 24 | #if __SIZEOF_POINTER__ != 4 && __SIZEOF_POINTER__ != 8 25 | #error "Pointer size is neither 4 nor 8 bytes" 26 | #endif 27 | 28 | /* gcc attributes; look them up for more information */ 29 | #define _shl_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) 30 | #define _shl_alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) 31 | #define _shl_sentinel_ __attribute__((__sentinel__)) 32 | #define _shl_noreturn_ __attribute__((__noreturn__)) 33 | #define _shl_unused_ __attribute__((__unused__)) 34 | #define _shl_pure_ __attribute__((__pure__)) 35 | #define _shl_const_ __attribute__((__const__)) 36 | #define _shl_deprecated_ __attribute__((__deprecated__)) 37 | #define _shl_packed_ __attribute__((__packed__)) 38 | #define _shl_malloc_ __attribute__((__malloc__)) 39 | #define _shl_weak_ __attribute__((__weak__)) 40 | #define _shl_likely_(_val) (__builtin_expect(!!(_val), 1)) 41 | #define _shl_unlikely_(_val) (__builtin_expect(!!(_val), 0)) 42 | #define _shl_public_ __attribute__((__visibility__("default"))) 43 | #define _shl_hidden_ __attribute__((__visibility__("hidden"))) 44 | #define _shl_weakref_(_val) __attribute__((__weakref__(#_val))) 45 | #define _shl_cleanup_(_val) __attribute__((__cleanup__(_val))) 46 | 47 | static inline void shl_freep(void *p) 48 | { 49 | free(*(void**)p); 50 | } 51 | 52 | #define _shl_free_ _shl_cleanup_(shl_freep) 53 | 54 | static inline void shl_closep(int *p) 55 | { 56 | if (*p >= 0) 57 | close(*p); 58 | } 59 | 60 | #define _shl_close_ _shl_cleanup_(shl_closep) 61 | 62 | static inline void shl_set_errno(int *r) 63 | { 64 | errno = *r; 65 | } 66 | 67 | #define SHL_PROTECT_ERRNO \ 68 | _shl_cleanup_(shl_set_errno) _shl_unused_ int shl__errno = errno 69 | 70 | /* 2-level stringify helper */ 71 | #define SHL__STRINGIFY(_val) #_val 72 | #define SHL_STRINGIFY(_val) SHL__STRINGIFY(_val) 73 | 74 | /* 2-level concatenate helper */ 75 | #define SHL__CONCATENATE(_a, _b) _a ## _b 76 | #define SHL_CONCATENATE(_a, _b) SHL__CONCATENATE(_a, _b) 77 | 78 | /* unique identifier with prefix */ 79 | #define SHL_UNIQUE(_prefix) SHL_CONCATENATE(_prefix, __COUNTER__) 80 | 81 | /* array element count */ 82 | #define SHL_ARRAY_LENGTH(_array) (sizeof(_array)/sizeof(*(_array))) 83 | 84 | /* get parent pointer by container-type, member and member-pointer */ 85 | #define shl_container_of(_ptr, _type, _member) \ 86 | ({ \ 87 | const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \ 88 | (_type *)( (char *)__mptr - offsetof(_type, _member) ); \ 89 | }) 90 | 91 | /* return maximum of two values and do strict type checking */ 92 | #define shl_max(_a, _b) \ 93 | ({ \ 94 | typeof(_a) __a = (_a); \ 95 | typeof(_b) __b = (_b); \ 96 | (void) (&__a == &__b); \ 97 | __a > __b ? __a : __b; \ 98 | }) 99 | 100 | /* same as shl_max() but perform explicit cast beforehand */ 101 | #define shl_max_t(_type, _a, _b) \ 102 | ({ \ 103 | _type __a = (_type)(_a); \ 104 | _type __b = (_type)(_b); \ 105 | __a > __b ? __a : __b; \ 106 | }) 107 | 108 | /* return minimum of two values and do strict type checking */ 109 | #define shl_min(_a, _b) \ 110 | ({ \ 111 | typeof(_a) __a = (_a); \ 112 | typeof(_b) __b = (_b); \ 113 | (void) (&__a == &__b); \ 114 | __a < __b ? __a : __b; \ 115 | }) 116 | 117 | /* same as shl_min() but perform explicit cast beforehand */ 118 | #define shl_min_t(_type, _a, _b) \ 119 | ({ \ 120 | _type __a = (_type)(_a); \ 121 | _type __b = (_type)(_b); \ 122 | __a < __b ? __a : __b; \ 123 | }) 124 | 125 | /* clamp value between low and high barriers */ 126 | #define shl_clamp(_val, _low, _high) \ 127 | ({ \ 128 | typeof(_val) __v = (_val); \ 129 | typeof(_low) __l = (_low); \ 130 | typeof(_high) __h = (_high); \ 131 | (void) (&__v == &__l); \ 132 | (void) (&__v == &__h); \ 133 | ((__v > __h) ? __h : ((__v < __l) ? __l : __v)); \ 134 | }) 135 | 136 | /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ 137 | static inline size_t SHL_ALIGN_POWER2(size_t u) 138 | { 139 | return 1ULL << ((sizeof(u) * 8ULL) - __builtin_clzll(u - 1ULL)); 140 | } 141 | 142 | /* zero memory or type */ 143 | #define shl_memzero(_ptr, _size) (memset((_ptr), 0, (_size))) 144 | #define shl_zero(_ptr) (shl_memzero(&(_ptr), sizeof(_ptr))) 145 | 146 | /* ptr <=> uint casts */ 147 | #define SHL_PTR_TO_TYPE(_type, _ptr) ((_type)((uintptr_t)(_ptr))) 148 | #define SHL_TYPE_TO_PTR(_type, _int) ((void*)((uintptr_t)(_int))) 149 | #define SHL_PTR_TO_INT(_ptr) SHL_PTR_TO_TYPE(int, (_ptr)) 150 | #define SHL_INT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int, (_ptr)) 151 | #define SHL_PTR_TO_UINT(_ptr) SHL_PTR_TO_TYPE(unsigned int, (_ptr)) 152 | #define SHL_UINT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned int, (_ptr)) 153 | #define SHL_PTR_TO_LONG(_ptr) SHL_PTR_TO_TYPE(long, (_ptr)) 154 | #define SHL_LONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(long, (_ptr)) 155 | #define SHL_PTR_TO_ULONG(_ptr) SHL_PTR_TO_TYPE(unsigned long, (_ptr)) 156 | #define SHL_ULONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned long, (_ptr)) 157 | #define SHL_PTR_TO_S32(_ptr) SHL_PTR_TO_TYPE(int32_t, (_ptr)) 158 | #define SHL_S32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int32_t, (_ptr)) 159 | #define SHL_PTR_TO_U32(_ptr) SHL_PTR_TO_TYPE(uint32_t, (_ptr)) 160 | #define SHL_U32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint32_t, (_ptr)) 161 | #define SHL_PTR_TO_S64(_ptr) SHL_PTR_TO_TYPE(int64_t, (_ptr)) 162 | #define SHL_S64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int64_t, (_ptr)) 163 | #define SHL_PTR_TO_U64(_ptr) SHL_PTR_TO_TYPE(uint64_t, (_ptr)) 164 | #define SHL_U64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint64_t, (_ptr)) 165 | 166 | /* compile-time assertions */ 167 | #define shl_assert_cc(_expr) static_assert(_expr, #_expr) 168 | 169 | /* 170 | * Safe Multiplications 171 | * Multiplications are subject to overflows. These helpers guarantee that the 172 | * multiplication can be done safely and return -ERANGE if not. 173 | * 174 | * Note: This is horribly slow for ull/uint64_t as we need a division to test 175 | * for overflows. Take that into account when using these. For smaller integers, 176 | * we can simply use an upcast-multiplication which gcc should be smart enough 177 | * to optimize. 178 | */ 179 | 180 | #define SHL__REAL_MULT(_max, _val, _factor) \ 181 | ({ \ 182 | (_factor == 0 || *(_val) <= (_max) / (_factor)) ? \ 183 | ((*(_val) *= (_factor)), 0) : \ 184 | -ERANGE; \ 185 | }) 186 | 187 | #define SHL__UPCAST_MULT(_type, _max, _val, _factor) \ 188 | ({ \ 189 | _type v = *(_val) * (_type)(_factor); \ 190 | (v <= (_max)) ? \ 191 | ((*(_val) = v), 0) : \ 192 | -ERANGE; \ 193 | }) 194 | 195 | static inline int shl_mult_ull(unsigned long long *val, 196 | unsigned long long factor) 197 | { 198 | return SHL__REAL_MULT(ULLONG_MAX, val, factor); 199 | } 200 | 201 | static inline int shl_mult_ul(unsigned long *val, unsigned long factor) 202 | { 203 | #if ULONG_MAX < ULLONG_MAX 204 | return SHL__UPCAST_MULT(unsigned long long, ULONG_MAX, val, factor); 205 | #else 206 | shl_assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); 207 | return shl_mult_ull((unsigned long long*)val, factor); 208 | #endif 209 | } 210 | 211 | static inline int shl_mult_u(unsigned int *val, unsigned int factor) 212 | { 213 | #if UINT_MAX < ULONG_MAX 214 | return SHL__UPCAST_MULT(unsigned long, UINT_MAX, val, factor); 215 | #elif UINT_MAX < ULLONG_MAX 216 | return SHL__UPCAST_MULT(unsigned long long, UINT_MAX, val, factor); 217 | #else 218 | shl_assert_cc(sizeof(unsigned int) == sizeof(unsigned long long)); 219 | return shl_mult_ull(val, factor); 220 | #endif 221 | } 222 | 223 | static inline int shl_mult_u64(uint64_t *val, uint64_t factor) 224 | { 225 | return SHL__REAL_MULT(UINT64_MAX, val, factor); 226 | } 227 | 228 | static inline int shl_mult_u32(uint32_t *val, uint32_t factor) 229 | { 230 | return SHL__UPCAST_MULT(uint_fast64_t, UINT32_MAX, val, factor); 231 | } 232 | 233 | static inline int shl_mult_u16(uint16_t *val, uint16_t factor) 234 | { 235 | return SHL__UPCAST_MULT(uint_fast32_t, UINT16_MAX, val, factor); 236 | } 237 | 238 | static inline int shl_mult_u8(uint8_t *val, uint8_t factor) 239 | { 240 | return SHL__UPCAST_MULT(uint_fast16_t, UINT8_MAX, val, factor); 241 | } 242 | 243 | #endif /* SHL_MACRO_H */ 244 | -------------------------------------------------------------------------------- /libtsm/test/test_htable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Hashtable Tests 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | 27 | #include "test_common.h" 28 | 29 | static struct shl_htable ht = SHL_HTABLE_INIT_STR(ht); 30 | static struct shl_htable uht = SHL_HTABLE_INIT_ULONG(uht); 31 | 32 | struct node { 33 | char huge_padding[16384]; 34 | uint8_t v; 35 | char paaaaaadding[16384]; 36 | char *key; 37 | unsigned long ul; 38 | char more_padding[32768]; 39 | size_t hash; 40 | }; 41 | 42 | #define to_node(_key) shl_htable_offsetof((_key), struct node, key) 43 | #define ul_to_node(_key) shl_htable_offsetof((_key), struct node, ul) 44 | 45 | static struct node o[] = { 46 | { .v = 0, .key = "o0", .ul = 0 }, 47 | { .v = 1, .key = "o1", .ul = 1 }, 48 | { .v = 2, .key = "o2", .ul = 2 }, 49 | { .v = 3, .key = "o3", .ul = 3 }, 50 | { .v = 4, .key = "o4", .ul = 4 }, 51 | { .v = 5, .key = "o5", .ul = 5 }, 52 | { .v = 6, .key = "o6", .ul = 6 }, 53 | { .v = 7, .key = "o7", .ul = 7 }, 54 | }; 55 | 56 | static void test_htable_str_cb(char **k, void *ctx) 57 | { 58 | int *num = ctx; 59 | 60 | ck_assert(to_node(k)->v == to_node(k)->ul); 61 | ++*num; 62 | } 63 | 64 | START_TEST(test_htable_str) 65 | { 66 | int r, i, num; 67 | char **k; 68 | bool b; 69 | 70 | /* insert once, remove once, try removing again */ 71 | 72 | ck_assert(!o[0].hash); 73 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 74 | ck_assert(!r); 75 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 76 | 77 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 78 | ck_assert(b); 79 | ck_assert(k != NULL); 80 | ck_assert(to_node(k)->v == 0); 81 | 82 | k = NULL; 83 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 84 | ck_assert(!b); 85 | ck_assert(k == NULL); 86 | 87 | /* insert twice, remove twice, try removing again */ 88 | 89 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 90 | ck_assert(!r); 91 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 92 | 93 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 94 | ck_assert(!r); 95 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 96 | 97 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 98 | ck_assert(b); 99 | ck_assert(k != NULL); 100 | ck_assert(to_node(k)->v == 0); 101 | 102 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 103 | ck_assert(b); 104 | ck_assert(k != NULL); 105 | ck_assert(to_node(k)->v == 0); 106 | 107 | k = NULL; 108 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 109 | ck_assert(!b); 110 | ck_assert(k == NULL); 111 | 112 | /* same as before but without hash-cache */ 113 | 114 | r = shl_htable_insert_str(&ht, &o[0].key, NULL); 115 | ck_assert(!r); 116 | 117 | r = shl_htable_insert_str(&ht, &o[0].key, NULL); 118 | ck_assert(!r); 119 | 120 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 121 | ck_assert(b); 122 | ck_assert(k != NULL); 123 | ck_assert(to_node(k)->v == 0); 124 | 125 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 126 | ck_assert(b); 127 | ck_assert(k != NULL); 128 | ck_assert(to_node(k)->v == 0); 129 | 130 | k = NULL; 131 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 132 | ck_assert(!b); 133 | ck_assert(k == NULL); 134 | 135 | /* insert all elements and verify empty hash-caches */ 136 | 137 | o[0].hash = 0; 138 | for (i = 0; i < 8; ++i) { 139 | ck_assert(!o[i].hash); 140 | r = shl_htable_insert_str(&ht, &o[i].key, &o[i].hash); 141 | ck_assert(!r); 142 | ck_assert(o[i].hash == shl_htable_rehash_str(&o[i].key, NULL)); 143 | } 144 | 145 | /* verify */ 146 | 147 | for (i = 0; i < 8; ++i) { 148 | k = NULL; 149 | b = shl_htable_lookup_str(&ht, o[i].key, NULL, &k); 150 | ck_assert(b); 151 | ck_assert(k != NULL); 152 | ck_assert(to_node(k)->v == i); 153 | } 154 | 155 | /* remove all elements again */ 156 | 157 | for (i = 0; i < 8; ++i) { 158 | b = shl_htable_remove_str(&ht, o[i].key, NULL, &k); 159 | ck_assert(b); 160 | ck_assert(k != NULL); 161 | ck_assert(to_node(k)->v == i); 162 | } 163 | 164 | /* verify they're gone */ 165 | 166 | for (i = 0; i < 8; ++i) { 167 | k = NULL; 168 | b = shl_htable_remove_str(&ht, o[i].key, NULL, &k); 169 | ck_assert(!b); 170 | ck_assert(k == NULL); 171 | } 172 | 173 | for (i = 0; i < 8; ++i) { 174 | k = NULL; 175 | b = shl_htable_lookup_str(&ht, o[i].key, NULL, &k); 176 | ck_assert(!b); 177 | ck_assert(k == NULL); 178 | } 179 | 180 | num = 0; 181 | shl_htable_visit_str(&ht, test_htable_str_cb, &num); 182 | ck_assert(num == 0); 183 | 184 | num = 0; 185 | shl_htable_clear_str(&ht, test_htable_str_cb, &num); 186 | ck_assert(num == 0); 187 | 188 | /* test shl_htable_clear_str() */ 189 | 190 | for (i = 0; i < 8; ++i) { 191 | r = shl_htable_insert_str(&ht, &o[i].key, &o[i].hash); 192 | ck_assert(!r); 193 | } 194 | 195 | num = 0; 196 | shl_htable_visit_str(&ht, test_htable_str_cb, &num); 197 | ck_assert(num == 8); 198 | 199 | num = 0; 200 | shl_htable_clear_str(&ht, test_htable_str_cb, &num); 201 | ck_assert(num == 8); 202 | } 203 | END_TEST 204 | 205 | static void test_htable_ulong_cb(unsigned long *k, void *ctx) 206 | { 207 | int *num = ctx; 208 | 209 | ck_assert(ul_to_node(k)->v == ul_to_node(k)->ul); 210 | ++*num; 211 | } 212 | 213 | START_TEST(test_htable_ulong) 214 | { 215 | int r, i, num; 216 | unsigned long *k; 217 | bool b; 218 | 219 | /* insert once, remove once, try removing again */ 220 | 221 | r = shl_htable_insert_ulong(&uht, &o[0].ul); 222 | ck_assert(!r); 223 | ck_assert(o[0].ul == shl_htable_rehash_ulong(&o[0].ul, NULL)); 224 | 225 | b = shl_htable_remove_ulong(&uht, o[0].ul, &k); 226 | ck_assert(b); 227 | ck_assert(k != NULL); 228 | ck_assert(ul_to_node(k)->v == 0); 229 | 230 | k = NULL; 231 | b = shl_htable_remove_ulong(&uht, o[0].ul, &k); 232 | ck_assert(!b); 233 | ck_assert(k == NULL); 234 | 235 | /* insert all */ 236 | 237 | for (i = 0; i < 8; ++i) { 238 | r = shl_htable_insert_ulong(&uht, &o[i].ul); 239 | ck_assert(!r); 240 | } 241 | 242 | /* verify */ 243 | 244 | for (i = 0; i < 8; ++i) { 245 | k = NULL; 246 | b = shl_htable_lookup_ulong(&uht, o[i].ul, &k); 247 | ck_assert(b); 248 | ck_assert(k != NULL); 249 | } 250 | 251 | /* remove all elements again */ 252 | 253 | for (i = 0; i < 8; ++i) { 254 | b = shl_htable_remove_ulong(&uht, o[i].ul, &k); 255 | ck_assert(b); 256 | ck_assert(k != NULL); 257 | ck_assert(ul_to_node(k)->v == i); 258 | } 259 | 260 | /* verify they're gone */ 261 | 262 | for (i = 0; i < 8; ++i) { 263 | k = NULL; 264 | b = shl_htable_remove_ulong(&uht, o[i].ul, &k); 265 | ck_assert(!b); 266 | ck_assert(k == NULL); 267 | } 268 | 269 | for (i = 0; i < 8; ++i) { 270 | k = NULL; 271 | b = shl_htable_lookup_ulong(&uht, o[i].ul, &k); 272 | ck_assert(!b); 273 | ck_assert(k == NULL); 274 | } 275 | 276 | num = 0; 277 | shl_htable_visit_ulong(&uht, test_htable_ulong_cb, &num); 278 | ck_assert(num == 0); 279 | 280 | num = 0; 281 | shl_htable_clear_ulong(&uht, test_htable_ulong_cb, &num); 282 | ck_assert(num == 0); 283 | 284 | /* test shl_htable_clear_ulong() */ 285 | 286 | for (i = 0; i < 8; ++i) { 287 | r = shl_htable_insert_ulong(&uht, &o[i].ul); 288 | ck_assert(!r); 289 | } 290 | 291 | num = 0; 292 | shl_htable_visit_ulong(&uht, test_htable_ulong_cb, &num); 293 | ck_assert(num == 8); 294 | 295 | num = 0; 296 | shl_htable_clear_ulong(&uht, test_htable_ulong_cb, &num); 297 | ck_assert(num == 8); 298 | } 299 | END_TEST 300 | 301 | TEST_DEFINE_CASE(misc) 302 | TEST(test_htable_str) 303 | TEST(test_htable_ulong) 304 | TEST_END_CASE 305 | 306 | TEST_DEFINE( 307 | TEST_SUITE(hashtable, 308 | TEST_CASE(misc), 309 | TEST_END 310 | ) 311 | ) 312 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-llog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Library Log/Debug Interface 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Library Log/Debug Interface 10 | * Libraries should always avoid producing side-effects. This includes writing 11 | * log-messages of any kind. However, you often don't want to disable debugging 12 | * entirely, therefore, the core objects often contain a pointer to a function 13 | * which performs logging. If that pointer is NULL (default), logging is 14 | * disabled. 15 | * 16 | * This header should never be installed into the system! This is _no_ public 17 | * header. Instead, copy it into your application if you want and use it there. 18 | * Your public library API should include something like this: 19 | * 20 | * typedef void (*MYPREFIX_log_t) (void *data, 21 | * const char *file, 22 | * int line, 23 | * const char *func, 24 | * const char *subs, 25 | * unsigned int sev, 26 | * const char *format, 27 | * va_list args); 28 | * 29 | * And then the user can supply such a function when creating a new context 30 | * object of your library or simply supply NULL. Internally, you have a field of 31 | * type "MYPREFIX_log_t llog" in your main structure. If you pass this to the 32 | * convenience helpers like llog_dbg(), llog_warn() etc. it will automatically 33 | * use the "llog" field to print the message. If it is NULL, nothing is done. 34 | * 35 | * The arguments of the log-function are defined as: 36 | * data: User-supplied data field that is passed straight through. 37 | * file: Zero terminated string of the file-name where the log-message 38 | * occurred. Can be NULL. 39 | * line: Line number of @file where the message occurred. Set to 0 or smaller 40 | * if not available. 41 | * func: Function name where the log-message occurred. Can be NULL. 42 | * subs: Subsystem where the message occurred (zero terminated). Can be NULL. 43 | * sev: Severity of log-message. An integer between 0 and 7 as defined below. 44 | * These are identical to the linux-kernel severities so there is no need 45 | * to include these in your public API. Every app can define them 46 | * themselves, if they need it. 47 | * format: Format string. Must not be NULL. 48 | * args: Argument array 49 | * 50 | * The user should also be able to optionally provide a data field which is 51 | * always passed unmodified as first parameter to the log-function. This allows 52 | * to add context to the logger. 53 | */ 54 | 55 | #ifndef SHL_LLOG_H 56 | #define SHL_LLOG_H 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | enum llog_severity { 64 | LLOG_FATAL = 0, 65 | LLOG_ALERT = 1, 66 | LLOG_CRITICAL = 2, 67 | LLOG_ERROR = 3, 68 | LLOG_WARNING = 4, 69 | LLOG_NOTICE = 5, 70 | LLOG_INFO = 6, 71 | LLOG_DEBUG = 7, 72 | LLOG_SEV_NUM, 73 | }; 74 | 75 | typedef void (*llog_submit_t) (void *data, 76 | const char *file, 77 | int line, 78 | const char *func, 79 | const char *subs, 80 | unsigned int sev, 81 | const char *format, 82 | va_list args); 83 | 84 | static inline __attribute__((format(printf, 8, 9))) 85 | void llog_format(llog_submit_t llog, 86 | void *data, 87 | const char *file, 88 | int line, 89 | const char *func, 90 | const char *subs, 91 | unsigned int sev, 92 | const char *format, 93 | ...) 94 | { 95 | int saved_errno = errno; 96 | va_list list; 97 | 98 | if (llog) { 99 | va_start(list, format); 100 | errno = saved_errno; 101 | llog(data, file, line, func, subs, sev, format, list); 102 | va_end(list); 103 | } 104 | } 105 | 106 | #ifndef LLOG_SUBSYSTEM 107 | static const char *LLOG_SUBSYSTEM __attribute__((__unused__)); 108 | #endif 109 | 110 | #define LLOG_DEFAULT __FILE__, __LINE__, __func__, LLOG_SUBSYSTEM 111 | 112 | #define llog_printf(obj, sev, format, ...) \ 113 | llog_format((obj)->llog, \ 114 | (obj)->llog_data, \ 115 | LLOG_DEFAULT, \ 116 | (sev), \ 117 | (format), \ 118 | ##__VA_ARGS__) 119 | #define llog_dprintf(obj, data, sev, format, ...) \ 120 | llog_format((obj), \ 121 | (data), \ 122 | LLOG_DEFAULT, \ 123 | (sev), \ 124 | (format), \ 125 | ##__VA_ARGS__) 126 | 127 | static inline __attribute__((format(printf, 4, 5))) 128 | void llog_dummyf(llog_submit_t llog, void *data, unsigned int sev, 129 | const char *format, ...) 130 | { 131 | } 132 | 133 | /* 134 | * Helpers 135 | * They pick up all the default values and submit the message to the 136 | * llog-subsystem. The llog_debug() function will discard the message unless 137 | * BUILD_ENABLE_DEBUG is defined. 138 | */ 139 | 140 | #ifdef BUILD_ENABLE_DEBUG 141 | #define llog_ddebug(obj, data, format, ...) \ 142 | llog_dprintf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 143 | #define llog_debug(obj, format, ...) \ 144 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 145 | #else 146 | #define llog_ddebug(obj, data, format, ...) \ 147 | llog_dummyf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 148 | #define llog_debug(obj, format, ...) \ 149 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 150 | #endif 151 | 152 | #define llog_info(obj, format, ...) \ 153 | llog_printf((obj), LLOG_INFO, (format), ##__VA_ARGS__) 154 | #define llog_dinfo(obj, data, format, ...) \ 155 | llog_dprintf((obj), (data), LLOG_INFO, (format), ##__VA_ARGS__) 156 | #define llog_notice(obj, format, ...) \ 157 | llog_printf((obj), LLOG_NOTICE, (format), ##__VA_ARGS__) 158 | #define llog_dnotice(obj, data, format, ...) \ 159 | llog_dprintf((obj), (data), LLOG_NOTICE, (format), ##__VA_ARGS__) 160 | #define llog_warning(obj, format, ...) \ 161 | llog_printf((obj), LLOG_WARNING, (format), ##__VA_ARGS__) 162 | #define llog_dwarning(obj, data, format, ...) \ 163 | llog_dprintf((obj), (data), LLOG_WARNING, (format), ##__VA_ARGS__) 164 | #define llog_error(obj, format, ...) \ 165 | llog_printf((obj), LLOG_ERROR, (format), ##__VA_ARGS__) 166 | #define llog_derror(obj, data, format, ...) \ 167 | llog_dprintf((obj), (data), LLOG_ERROR, (format), ##__VA_ARGS__) 168 | #define llog_critical(obj, format, ...) \ 169 | llog_printf((obj), LLOG_CRITICAL, (format), ##__VA_ARGS__) 170 | #define llog_dcritical(obj, data, format, ...) \ 171 | llog_dprintf((obj), (data), LLOG_CRITICAL, (format), ##__VA_ARGS__) 172 | #define llog_alert(obj, format, ...) \ 173 | llog_printf((obj), LLOG_ALERT, (format), ##__VA_ARGS__) 174 | #define llog_dalert(obj, data, format, ...) \ 175 | llog_dprintf((obj), (data), LLOG_ALERT, (format), ##__VA_ARGS__) 176 | #define llog_fatal(obj, format, ...) \ 177 | llog_printf((obj), LLOG_FATAL, (format), ##__VA_ARGS__) 178 | #define llog_dfatal(obj, data, format, ...) \ 179 | llog_dprintf((obj), (data), LLOG_FATAL, (format), ##__VA_ARGS__) 180 | 181 | /* 182 | * Default log messages 183 | * These macros can be used to produce default log messages. You can use them 184 | * directly in an "return" statement. The "v" variants automatically cast the 185 | * result to void so it can be used in return statements inside of void 186 | * functions. The "d" variants use the logging object directly as the parent 187 | * might not exist, yet. 188 | * 189 | * Most of the messages work only if debugging is enabled. This is, because they 190 | * are used in debug paths and would slow down normal applications. 191 | */ 192 | 193 | #define llog_dEINVAL(obj, data) \ 194 | (llog_derror((obj), (data), "invalid arguments"), -EINVAL) 195 | #define llog_EINVAL(obj) \ 196 | (llog_dEINVAL((obj)->llog, (obj)->llog_data)) 197 | #define llog_vEINVAL(obj) \ 198 | ((void)llog_EINVAL(obj)) 199 | #define llog_vdEINVAL(obj, data) \ 200 | ((void)llog_dEINVAL((obj), (data))) 201 | 202 | #define llog_dEFAULT(obj, data) \ 203 | (llog_derror((obj), (data), "internal operation failed"), -EFAULT) 204 | #define llog_EFAULT(obj) \ 205 | (llog_dEFAULT((obj)->llog, (obj)->llog_data)) 206 | #define llog_vEFAULT(obj) \ 207 | ((void)llog_EFAULT(obj)) 208 | #define llog_vdEFAULT(obj, data) \ 209 | ((void)llog_dEFAULT((obj), (data))) 210 | 211 | #define llog_dENOMEM(obj, data) \ 212 | (llog_derror((obj), (data), "out of memory"), -ENOMEM) 213 | #define llog_ENOMEM(obj) \ 214 | (llog_dENOMEM((obj)->llog, (obj)->llog_data)) 215 | #define llog_vENOMEM(obj) \ 216 | ((void)llog_ENOMEM(obj)) 217 | #define llog_vdENOMEM(obj, data) \ 218 | ((void)llog_dENOMEM((obj), (data))) 219 | 220 | #define llog_dEPIPE(obj, data) \ 221 | (llog_derror((obj), (data), "fd closed unexpectedly"), -EPIPE) 222 | #define llog_EPIPE(obj) \ 223 | (llog_dEPIPE((obj)->llog, (obj)->llog_data)) 224 | #define llog_vEPIPE(obj) \ 225 | ((void)llog_EPIPE(obj)) 226 | #define llog_vdEPIPE(obj, data) \ 227 | ((void)llog_dEPIPE((obj), (data))) 228 | 229 | #define llog_dERRNO(obj, data) \ 230 | (llog_derror((obj), (data), "syscall failed (%d): %m", errno), -errno) 231 | #define llog_ERRNO(obj) \ 232 | (llog_dERRNO((obj)->llog, (obj)->llog_data)) 233 | #define llog_vERRNO(obj) \ 234 | ((void)llog_ERRNO(obj)) 235 | #define llog_vdERRNO(obj, data) \ 236 | ((void)llog_dERRNO((obj), (data))) 237 | 238 | #define llog_dERR(obj, data, _r) \ 239 | (errno = -(_r), llog_derror((obj), (data), "syscall failed (%d): %m", (_r)), (_r)) 240 | #define llog_ERR(obj, _r) \ 241 | (llog_dERR((obj)->llog, (obj)->llog_data, (_r))) 242 | #define llog_vERR(obj, _r) \ 243 | ((void)llog_ERR((obj), (_r))) 244 | #define llog_vdERR(obj, data, _r) \ 245 | ((void)llog_dERR((obj), (data), (_r))) 246 | 247 | #endif /* SHL_LLOG_H */ 248 | -------------------------------------------------------------------------------- /libtsm/src/tsm/tsm-selection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libtsm - Screen Selections 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Screen Selections 28 | * If a running pty-client does not support mouse-tracking extensions, a 29 | * terminal can manually mark selected areas if it does mouse-tracking itself. 30 | * This tracking is slightly different than the integrated client-tracking: 31 | * 32 | * Initial state is no-selection. At any time selection_reset() can be called to 33 | * clear the selection and go back to initial state. 34 | * If the user presses a mouse-button, the terminal can calculate the selected 35 | * cell and call selection_start() to notify the terminal that the user started 36 | * the selection. While the mouse-button is held down, the terminal should call 37 | * selection_target() whenever a mouse-event occurs. This will tell the screen 38 | * layer to draw the selection from the initial start up to the last given 39 | * target. 40 | * Please note that the selection-start cannot be modified by the terminal 41 | * during a selection. Instead, the screen-layer automatically moves it along 42 | * with any scroll-operations or inserts/deletes. This also means, the terminal 43 | * must _not_ cache the start-position itself as it may change under the hood. 44 | * This selection takes also care of scrollback-buffer selections and correctly 45 | * moves selection state along. 46 | * 47 | * Please note that this is not the kind of selection that some PTY applications 48 | * support. If the client supports the mouse-protocol, then it can also control 49 | * a separate screen-selection which is always inside of the actual screen. This 50 | * is a totally different selection. 51 | */ 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include "libtsm.h" 59 | #include "libtsm-int.h" 60 | #include "shl-llog.h" 61 | 62 | #define LLOG_SUBSYSTEM "tsm-selection" 63 | 64 | static void selection_set(struct tsm_screen *con, struct selection_pos *sel, 65 | unsigned int x, unsigned int y) 66 | { 67 | struct line *pos; 68 | 69 | sel->line = NULL; 70 | pos = con->sb_pos; 71 | 72 | while (y && pos) { 73 | --y; 74 | pos = pos->next; 75 | } 76 | 77 | if (pos) 78 | sel->line = pos; 79 | 80 | sel->x = x; 81 | sel->y = y; 82 | } 83 | 84 | SHL_EXPORT 85 | void tsm_screen_selection_reset(struct tsm_screen *con) 86 | { 87 | if (!con) 88 | return; 89 | 90 | screen_inc_age(con); 91 | /* TODO: more sophisticated ageing */ 92 | con->age = con->age_cnt; 93 | 94 | con->sel_active = false; 95 | } 96 | 97 | SHL_EXPORT 98 | void tsm_screen_selection_start(struct tsm_screen *con, 99 | unsigned int posx, 100 | unsigned int posy) 101 | { 102 | if (!con) 103 | return; 104 | 105 | screen_inc_age(con); 106 | /* TODO: more sophisticated ageing */ 107 | con->age = con->age_cnt; 108 | 109 | con->sel_active = true; 110 | selection_set(con, &con->sel_start, posx, posy); 111 | memcpy(&con->sel_end, &con->sel_start, sizeof(con->sel_end)); 112 | } 113 | 114 | SHL_EXPORT 115 | void tsm_screen_selection_target(struct tsm_screen *con, 116 | unsigned int posx, 117 | unsigned int posy) 118 | { 119 | if (!con || !con->sel_active) 120 | return; 121 | 122 | screen_inc_age(con); 123 | /* TODO: more sophisticated ageing */ 124 | con->age = con->age_cnt; 125 | 126 | selection_set(con, &con->sel_end, posx, posy); 127 | } 128 | 129 | /* TODO: tsm_ucs4_to_utf8 expects UCS4 characters, but a cell contains a 130 | * tsm-symbol (which can contain multiple UCS4 chars). Fix this when introducing 131 | * support for combining characters. */ 132 | static unsigned int copy_line(struct line *line, char *buf, 133 | unsigned int start, unsigned int len) 134 | { 135 | unsigned int i, end; 136 | char *pos = buf; 137 | 138 | end = start + len; 139 | for (i = start; i < line->size && i < end; ++i) { 140 | if (i < line->size || !line->cells[i].ch) 141 | pos += tsm_ucs4_to_utf8(line->cells[i].ch, pos); 142 | else 143 | pos += tsm_ucs4_to_utf8(' ', pos); 144 | } 145 | 146 | return pos - buf; 147 | } 148 | 149 | /* TODO: This beast definitely needs some "beautification", however, it's meant 150 | * as a "proof-of-concept" so its enough for now. */ 151 | SHL_EXPORT 152 | int tsm_screen_selection_copy(struct tsm_screen *con, char **out) 153 | { 154 | unsigned int len, i; 155 | struct selection_pos *start, *end; 156 | struct line *iter; 157 | char *str, *pos; 158 | 159 | if (!con || !out) 160 | return -EINVAL; 161 | 162 | if (!con->sel_active) 163 | return -ENOENT; 164 | 165 | /* check whether sel_start or sel_end comes first */ 166 | if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) { 167 | if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) { 168 | str = strdup(""); 169 | if (!str) 170 | return -ENOMEM; 171 | *out = str; 172 | return 0; 173 | } 174 | start = &con->sel_start; 175 | end = &con->sel_end; 176 | } else if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) { 177 | start = &con->sel_end; 178 | end = &con->sel_start; 179 | } else if (con->sel_start.line && con->sel_end.line) { 180 | if (con->sel_start.line->sb_id < con->sel_end.line->sb_id) { 181 | start = &con->sel_start; 182 | end = &con->sel_end; 183 | } else if (con->sel_start.line->sb_id > con->sel_end.line->sb_id) { 184 | start = &con->sel_end; 185 | end = &con->sel_start; 186 | } else if (con->sel_start.x < con->sel_end.x) { 187 | start = &con->sel_start; 188 | end = &con->sel_end; 189 | } else { 190 | start = &con->sel_end; 191 | end = &con->sel_start; 192 | } 193 | } else if (con->sel_start.line) { 194 | start = &con->sel_start; 195 | end = &con->sel_end; 196 | } else if (con->sel_end.line) { 197 | start = &con->sel_end; 198 | end = &con->sel_start; 199 | } else if (con->sel_start.y < con->sel_end.y) { 200 | start = &con->sel_start; 201 | end = &con->sel_end; 202 | } else if (con->sel_start.y > con->sel_end.y) { 203 | start = &con->sel_end; 204 | end = &con->sel_start; 205 | } else if (con->sel_start.x < con->sel_end.x) { 206 | start = &con->sel_start; 207 | end = &con->sel_end; 208 | } else { 209 | start = &con->sel_end; 210 | end = &con->sel_start; 211 | } 212 | 213 | /* calculate size of buffer */ 214 | len = 0; 215 | iter = start->line; 216 | if (!iter && start->y == SELECTION_TOP) 217 | iter = con->sb_first; 218 | 219 | while (iter) { 220 | if (iter == start->line && iter == end->line) { 221 | if (iter->size > start->x) { 222 | if (iter->size > end->x) 223 | len += end->x - start->x + 1; 224 | else 225 | len += iter->size - start->x; 226 | } 227 | break; 228 | } else if (iter == start->line) { 229 | if (iter->size > start->x) 230 | len += iter->size - start->x; 231 | } else if (iter == end->line) { 232 | if (iter->size > end->x) 233 | len += end->x + 1; 234 | else 235 | len += iter->size; 236 | break; 237 | } else { 238 | len += iter->size; 239 | } 240 | 241 | ++len; 242 | iter = iter->next; 243 | } 244 | 245 | if (!end->line) { 246 | if (start->line || start->y == SELECTION_TOP) 247 | i = 0; 248 | else 249 | i = start->y; 250 | for ( ; i < con->size_y; ++i) { 251 | if (!start->line && start->y == i && end->y == i) { 252 | if (con->size_x > start->x) { 253 | if (con->size_x > end->x) 254 | len += end->x - start->x + 1; 255 | else 256 | len += con->size_x - start->x; 257 | } 258 | break; 259 | } else if (!start->line && start->y == i) { 260 | if (con->size_x > start->x) 261 | len += con->size_x - start->x; 262 | } else if (end->y == i) { 263 | if (con->size_x > end->x) 264 | len += end->x + 1; 265 | else 266 | len += con->size_x; 267 | break; 268 | } else { 269 | len += con->size_x; 270 | } 271 | 272 | ++len; 273 | } 274 | } 275 | 276 | /* allocate buffer */ 277 | len *= 4; 278 | ++len; 279 | str = malloc(len); 280 | if (!str) 281 | return -ENOMEM; 282 | pos = str; 283 | 284 | /* copy data into buffer */ 285 | iter = start->line; 286 | if (!iter && start->y == SELECTION_TOP) 287 | iter = con->sb_first; 288 | 289 | while (iter) { 290 | if (iter == start->line && iter == end->line) { 291 | if (iter->size > start->x) { 292 | if (iter->size > end->x) 293 | len = end->x - start->x + 1; 294 | else 295 | len = iter->size - start->x; 296 | pos += copy_line(iter, pos, start->x, len); 297 | } 298 | break; 299 | } else if (iter == start->line) { 300 | if (iter->size > start->x) 301 | pos += copy_line(iter, pos, start->x, 302 | iter->size - start->x); 303 | } else if (iter == end->line) { 304 | if (iter->size > end->x) 305 | len = end->x + 1; 306 | else 307 | len = iter->size; 308 | pos += copy_line(iter, pos, 0, len); 309 | break; 310 | } else { 311 | pos += copy_line(iter, pos, 0, iter->size); 312 | } 313 | 314 | *pos++ = '\n'; 315 | iter = iter->next; 316 | } 317 | 318 | if (!end->line) { 319 | if (start->line || start->y == SELECTION_TOP) 320 | i = 0; 321 | else 322 | i = start->y; 323 | for ( ; i < con->size_y; ++i) { 324 | iter = con->lines[i]; 325 | if (!start->line && start->y == i && end->y == i) { 326 | if (con->size_x > start->x) { 327 | if (con->size_x > end->x) 328 | len = end->x - start->x + 1; 329 | else 330 | len = con->size_x - start->x; 331 | pos += copy_line(iter, pos, start->x, len); 332 | } 333 | break; 334 | } else if (!start->line && start->y == i) { 335 | if (con->size_x > start->x) 336 | pos += copy_line(iter, pos, start->x, 337 | con->size_x - start->x); 338 | } else if (end->y == i) { 339 | if (con->size_x > end->x) 340 | len = end->x + 1; 341 | else 342 | len = con->size_x; 343 | pos += copy_line(iter, pos, 0, len); 344 | break; 345 | } else { 346 | pos += copy_line(iter, pos, 0, con->size_x); 347 | } 348 | 349 | *pos++ = '\n'; 350 | } 351 | } 352 | 353 | /* return buffer */ 354 | *pos = 0; 355 | *out = str; 356 | return pos - str; 357 | } 358 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-htable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Written-by: Rusty Russell 5 | * Adjusted-by: David Herrmann 6 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 7 | */ 8 | 9 | /* 10 | * Please see ccan/htable/_info at: 11 | * https://github.com/rustyrussell/ccan/tree/master/ccan/htable 12 | * for information on the hashtable algorithm. This file copies the code inline 13 | * and is released under the same conditions. 14 | * 15 | * At the end of the file you can find some helpers to use this htable to store 16 | * objects with "unsigned long" or "char*" keys. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl-htable.h" 27 | 28 | #define COLD __attribute__((cold)) 29 | 30 | struct htable { 31 | /* KEEP IN SYNC WITH "struct shl_htable_int" */ 32 | size_t (*rehash)(const void *elem, void *priv); 33 | void *priv; 34 | unsigned int bits; 35 | size_t elems, deleted, max, max_with_deleted; 36 | /* These are the bits which are the same in all pointers. */ 37 | uintptr_t common_mask, common_bits; 38 | uintptr_t perfect_bit; 39 | uintptr_t *table; 40 | }; 41 | 42 | #define HTABLE_INITIALIZER(name, rehash, priv) \ 43 | { rehash, priv, 0, 0, 0, 0, 0, -1, 0, 0, &name.perfect_bit } 44 | 45 | struct htable_iter { 46 | size_t off; 47 | }; 48 | 49 | /* 50 | * INLINE COPY OF ccan/htable.c 51 | */ 52 | 53 | /* We use 0x1 as deleted marker. */ 54 | #define HTABLE_DELETED (0x1) 55 | 56 | /* We clear out the bits which are always the same, and put metadata there. */ 57 | static inline uintptr_t get_extra_ptr_bits(const struct htable *ht, 58 | uintptr_t e) 59 | { 60 | return e & ht->common_mask; 61 | } 62 | 63 | static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e) 64 | { 65 | return (void *)((e & ~ht->common_mask) | ht->common_bits); 66 | } 67 | 68 | static inline uintptr_t make_hval(const struct htable *ht, 69 | const void *p, uintptr_t bits) 70 | { 71 | return ((uintptr_t)p & ~ht->common_mask) | bits; 72 | } 73 | 74 | static inline bool entry_is_valid(uintptr_t e) 75 | { 76 | return e > HTABLE_DELETED; 77 | } 78 | 79 | static inline uintptr_t get_hash_ptr_bits(const struct htable *ht, 80 | size_t hash) 81 | { 82 | /* Shuffling the extra bits (as specified in mask) down the 83 | * end is quite expensive. But the lower bits are redundant, so 84 | * we fold the value first. */ 85 | return (hash ^ (hash >> ht->bits)) 86 | & ht->common_mask & ~ht->perfect_bit; 87 | } 88 | 89 | static void htable_init(struct htable *ht, 90 | size_t (*rehash)(const void *elem, void *priv), 91 | void *priv) 92 | { 93 | struct htable empty = HTABLE_INITIALIZER(empty, NULL, NULL); 94 | *ht = empty; 95 | ht->rehash = rehash; 96 | ht->priv = priv; 97 | ht->table = &ht->perfect_bit; 98 | } 99 | 100 | static void htable_clear(struct htable *ht, 101 | void (*free_cb) (void *entry, void *ctx), 102 | void *ctx) 103 | { 104 | size_t i; 105 | 106 | if (ht->table != &ht->perfect_bit) { 107 | if (free_cb) { 108 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 109 | if (entry_is_valid(ht->table[i])) 110 | free_cb(get_raw_ptr(ht, ht->table[i]), 111 | ctx); 112 | } 113 | } 114 | 115 | free((void *)ht->table); 116 | } 117 | 118 | htable_init(ht, ht->rehash, ht->priv); 119 | } 120 | 121 | static void htable_visit(struct htable *ht, 122 | void (*visit_cb) (void *elem, void *ctx), 123 | void *ctx) 124 | { 125 | size_t i; 126 | 127 | if (visit_cb && ht->table != &ht->perfect_bit) { 128 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 129 | if (entry_is_valid(ht->table[i])) 130 | visit_cb(get_raw_ptr(ht, ht->table[i]), ctx); 131 | } 132 | } 133 | } 134 | 135 | static size_t hash_bucket(const struct htable *ht, size_t h) 136 | { 137 | return h & ((1 << ht->bits)-1); 138 | } 139 | 140 | static void *htable_val(const struct htable *ht, 141 | struct htable_iter *i, size_t hash, uintptr_t perfect) 142 | { 143 | uintptr_t h2 = get_hash_ptr_bits(ht, hash) | perfect; 144 | 145 | while (ht->table[i->off]) { 146 | if (ht->table[i->off] != HTABLE_DELETED) { 147 | if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2) 148 | return get_raw_ptr(ht, ht->table[i->off]); 149 | } 150 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 151 | h2 &= ~perfect; 152 | } 153 | return NULL; 154 | } 155 | 156 | static void *htable_firstval(const struct htable *ht, 157 | struct htable_iter *i, size_t hash) 158 | { 159 | i->off = hash_bucket(ht, hash); 160 | return htable_val(ht, i, hash, ht->perfect_bit); 161 | } 162 | 163 | static void *htable_nextval(const struct htable *ht, 164 | struct htable_iter *i, size_t hash) 165 | { 166 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 167 | return htable_val(ht, i, hash, 0); 168 | } 169 | 170 | /* This does not expand the hash table, that's up to caller. */ 171 | static void ht_add(struct htable *ht, const void *new, size_t h) 172 | { 173 | size_t i; 174 | uintptr_t perfect = ht->perfect_bit; 175 | 176 | i = hash_bucket(ht, h); 177 | 178 | while (entry_is_valid(ht->table[i])) { 179 | perfect = 0; 180 | i = (i + 1) & ((1 << ht->bits)-1); 181 | } 182 | ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect); 183 | } 184 | 185 | static COLD bool double_table(struct htable *ht) 186 | { 187 | unsigned int i; 188 | size_t oldnum = (size_t)1 << ht->bits; 189 | uintptr_t *oldtable, e; 190 | 191 | oldtable = ht->table; 192 | ht->table = calloc(1 << (ht->bits+1), sizeof(size_t)); 193 | if (!ht->table) { 194 | ht->table = oldtable; 195 | return false; 196 | } 197 | ht->bits++; 198 | ht->max = ((size_t)3 << ht->bits) / 4; 199 | ht->max_with_deleted = ((size_t)9 << ht->bits) / 10; 200 | 201 | /* If we lost our "perfect bit", get it back now. */ 202 | if (!ht->perfect_bit && ht->common_mask) { 203 | for (i = 0; i < sizeof(ht->common_mask) * CHAR_BIT; i++) { 204 | if (ht->common_mask & ((size_t)1 << i)) { 205 | ht->perfect_bit = (size_t)1 << i; 206 | break; 207 | } 208 | } 209 | } 210 | 211 | if (oldtable != &ht->perfect_bit) { 212 | for (i = 0; i < oldnum; i++) { 213 | if (entry_is_valid(e = oldtable[i])) { 214 | void *p = get_raw_ptr(ht, e); 215 | ht_add(ht, p, ht->rehash(p, ht->priv)); 216 | } 217 | } 218 | free(oldtable); 219 | } 220 | ht->deleted = 0; 221 | return true; 222 | } 223 | 224 | static COLD void rehash_table(struct htable *ht) 225 | { 226 | size_t start, i; 227 | uintptr_t e; 228 | 229 | /* Beware wrap cases: we need to start from first empty bucket. */ 230 | for (start = 0; ht->table[start]; start++); 231 | 232 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 233 | size_t h = (i + start) & ((1 << ht->bits)-1); 234 | e = ht->table[h]; 235 | if (!e) 236 | continue; 237 | if (e == HTABLE_DELETED) 238 | ht->table[h] = 0; 239 | else if (!(e & ht->perfect_bit)) { 240 | void *p = get_raw_ptr(ht, e); 241 | ht->table[h] = 0; 242 | ht_add(ht, p, ht->rehash(p, ht->priv)); 243 | } 244 | } 245 | ht->deleted = 0; 246 | } 247 | 248 | /* We stole some bits, now we need to put them back... */ 249 | static COLD void update_common(struct htable *ht, const void *p) 250 | { 251 | unsigned int i; 252 | uintptr_t maskdiff, bitsdiff; 253 | 254 | if (ht->elems == 0) { 255 | /* Always reveal one bit of the pointer in the bucket, 256 | * so it's not zero or HTABLE_DELETED (1), even if 257 | * hash happens to be 0. Assumes (void *)1 is not a 258 | * valid pointer. */ 259 | for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) { 260 | if ((uintptr_t)p & ((uintptr_t)1 << i)) 261 | break; 262 | } 263 | 264 | ht->common_mask = ~((uintptr_t)1 << i); 265 | ht->common_bits = ((uintptr_t)p & ht->common_mask); 266 | ht->perfect_bit = 1; 267 | return; 268 | } 269 | 270 | /* Find bits which are unequal to old common set. */ 271 | maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask); 272 | 273 | /* These are the bits which go there in existing entries. */ 274 | bitsdiff = ht->common_bits & maskdiff; 275 | 276 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 277 | if (!entry_is_valid(ht->table[i])) 278 | continue; 279 | /* Clear the bits no longer in the mask, set them as 280 | * expected. */ 281 | ht->table[i] &= ~maskdiff; 282 | ht->table[i] |= bitsdiff; 283 | } 284 | 285 | /* Take away those bits from our mask, bits and perfect bit. */ 286 | ht->common_mask &= ~maskdiff; 287 | ht->common_bits &= ~maskdiff; 288 | ht->perfect_bit &= ~maskdiff; 289 | } 290 | 291 | static bool htable_add(struct htable *ht, size_t hash, const void *p) 292 | { 293 | if (ht->elems+1 > ht->max && !double_table(ht)) 294 | return false; 295 | if (ht->elems+1 + ht->deleted > ht->max_with_deleted) 296 | rehash_table(ht); 297 | assert(p); 298 | if (((uintptr_t)p & ht->common_mask) != ht->common_bits) 299 | update_common(ht, p); 300 | 301 | ht_add(ht, p, hash); 302 | ht->elems++; 303 | return true; 304 | } 305 | 306 | static void htable_delval(struct htable *ht, struct htable_iter *i) 307 | { 308 | assert(i->off < (size_t)1 << ht->bits); 309 | assert(entry_is_valid(ht->table[i->off])); 310 | 311 | ht->elems--; 312 | ht->table[i->off] = HTABLE_DELETED; 313 | ht->deleted++; 314 | } 315 | 316 | /* 317 | * Wrapper code to make it easier to use this hash-table as map. 318 | */ 319 | 320 | void shl_htable_init(struct shl_htable *htable, 321 | bool (*compare) (const void *a, const void *b), 322 | size_t (*rehash)(const void *elem, void *priv), 323 | void *priv) 324 | { 325 | struct htable *ht = (void*)&htable->htable; 326 | 327 | htable->compare = compare; 328 | htable_init(ht, rehash, priv); 329 | } 330 | 331 | void shl_htable_clear(struct shl_htable *htable, 332 | void (*free_cb) (void *elem, void *ctx), 333 | void *ctx) 334 | { 335 | struct htable *ht = (void*)&htable->htable; 336 | 337 | htable_clear(ht, free_cb, ctx); 338 | } 339 | 340 | void shl_htable_visit(struct shl_htable *htable, 341 | void (*visit_cb) (void *elem, void *ctx), 342 | void *ctx) 343 | { 344 | struct htable *ht = (void*)&htable->htable; 345 | 346 | htable_visit(ht, visit_cb, ctx); 347 | } 348 | 349 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 350 | void **out) 351 | { 352 | struct htable *ht = (void*)&htable->htable; 353 | struct htable_iter i; 354 | void *c; 355 | 356 | for (c = htable_firstval(ht, &i, hash); 357 | c; 358 | c = htable_nextval(ht, &i, hash)) { 359 | if (htable->compare(obj, c)) { 360 | if (out) 361 | *out = c; 362 | return true; 363 | } 364 | } 365 | 366 | return false; 367 | } 368 | 369 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash) 370 | { 371 | struct htable *ht = (void*)&htable->htable; 372 | bool b; 373 | 374 | b = htable_add(ht, hash, (void*)obj); 375 | return b ? 0 : -ENOMEM; 376 | } 377 | 378 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 379 | void **out) 380 | { 381 | struct htable *ht = (void*)&htable->htable; 382 | struct htable_iter i; 383 | void *c; 384 | 385 | for (c = htable_firstval(ht, &i, hash); 386 | c; 387 | c = htable_nextval(ht, &i, hash)) { 388 | if (htable->compare(obj, c)) { 389 | if (out) 390 | *out = c; 391 | htable_delval(ht, &i); 392 | return true; 393 | } 394 | } 395 | 396 | return false; 397 | } 398 | 399 | /* 400 | * Helpers 401 | */ 402 | 403 | bool shl_htable_compare_ulong(const void *a, const void *b) 404 | { 405 | return *(const unsigned long*)a == *(const unsigned long*)b; 406 | } 407 | 408 | size_t shl_htable_rehash_ulong(const void *elem, void *priv) 409 | { 410 | return (size_t)*(const unsigned long*)elem; 411 | } 412 | 413 | bool shl_htable_compare_str(const void *a, const void *b) 414 | { 415 | return !strcmp(*(char**)a, *(char**)b); 416 | } 417 | 418 | /* DJB's hash function */ 419 | size_t shl_htable_rehash_str(const void *elem, void *priv) 420 | { 421 | const char *str = *(char**)elem; 422 | size_t hash = 5381; 423 | 424 | for ( ; *str; ++str) 425 | hash = (hash << 5) + hash + (size_t)*str; 426 | 427 | return hash; 428 | } 429 | -------------------------------------------------------------------------------- /libtsm/src/tsm/tsm-vte-charsets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - VT Emulator 3 | * 4 | * Copyright (c) 2012 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * VTE Character Sets 28 | * These are predefined charactersets that can be loaded into GL and GR. By 29 | * default we use unicode_lower and unicode_upper, that is, both sets have the 30 | * exact unicode mapping. unicode_lower is effectively ASCII and unicode_upper 31 | * as defined by the unicode standard. 32 | * Several other character sets are defined here. However, all of them are 33 | * limited to the 96 character space of GL or GR. Everything beyond GR (which 34 | * was not supported by the classic VTs by DEC but is available in VT emulators 35 | * that support unicode/UTF8) is always mapped to unicode and cannot be changed 36 | * by these character sets. Even mapping GL and GR is only available for 37 | * backwards compatibility as new applications can use the Unicode functionality 38 | * of the VTE. 39 | * 40 | * Moreover, mapping GR is almost unnecessary to support. In fact, Unicode UTF-8 41 | * support in VTE works by reading every incoming data as UTF-8 stream. This 42 | * maps GL/ASCII to ASCII, as UTF-8 is backwards compatible to ASCII, however, 43 | * everything that has the 8th bit set is a >=2-byte haracter in UTF-8. That is, 44 | * this is in no way backwards compatible to >=VT220 8bit support. Therefore, if 45 | * someone maps a character set into GR and wants to use them with this VTE, 46 | * then they must already send UTF-8 characters to use GR (all GR characters are 47 | * 8-bits). Hence, they can easily also send the correct UTF-8 character for the 48 | * unicode mapping. 49 | * The only advantage is that most characters in many sets are 3-byte UTF-8 50 | * characters and by mapping the set into GR/GL you can use 2 or 1 byte UTF-8 51 | * characters which saves bandwidth. 52 | * Another reason is, if you have older applications that use the VT220 8-bit 53 | * support and you put a ASCII/8bit-extension to UTF-8 converter in between, you 54 | * need these mappings to have the application behave correctly if it uses GL/GR 55 | * mappings extensively. 56 | * 57 | * Anyway, we support GL/GR mappings so here are the most commonly used maps as 58 | * defined by Unicode-standard, DEC-private maps and other famous charmaps. 59 | * 60 | * Characters 1-32 are always the control characters (part of CL) and cannot be 61 | * mapped. Characters 34-127 (94 characters) are part of GL and can be mapped. 62 | * Characters 33 and 128 are not part of GL and always mapped by VTE but are 63 | * included here in the maps for alignment reasons but always set to 0. 64 | */ 65 | 66 | #include 67 | #include 68 | #include 69 | #include "libtsm.h" 70 | #include "libtsm-int.h" 71 | 72 | /* 73 | * Lower Unicode character set. This maps the characters to the basic ASCII 74 | * characters 33-126. These are all graphics characters defined in ASCII. The 75 | * first an last entry are never used so we can safely set them to anything. 76 | */ 77 | tsm_vte_charset tsm_vte_unicode_lower = { 78 | [0] = 0, 79 | [1] = 33, 80 | [2] = 34, 81 | [3] = 35, 82 | [4] = 36, 83 | [5] = 37, 84 | [6] = 38, 85 | [7] = 39, 86 | [8] = 40, 87 | [9] = 41, 88 | [10] = 42, 89 | [11] = 43, 90 | [12] = 44, 91 | [13] = 45, 92 | [14] = 46, 93 | [15] = 47, 94 | [16] = 48, 95 | [17] = 49, 96 | [18] = 50, 97 | [19] = 51, 98 | [20] = 52, 99 | [21] = 53, 100 | [22] = 54, 101 | [23] = 55, 102 | [24] = 56, 103 | [25] = 57, 104 | [26] = 58, 105 | [27] = 59, 106 | [28] = 60, 107 | [29] = 61, 108 | [30] = 62, 109 | [31] = 63, 110 | [32] = 64, 111 | [33] = 65, 112 | [34] = 66, 113 | [35] = 67, 114 | [36] = 68, 115 | [37] = 69, 116 | [38] = 70, 117 | [39] = 71, 118 | [40] = 72, 119 | [41] = 73, 120 | [42] = 74, 121 | [43] = 75, 122 | [44] = 76, 123 | [45] = 77, 124 | [46] = 78, 125 | [47] = 79, 126 | [48] = 80, 127 | [49] = 81, 128 | [50] = 82, 129 | [51] = 83, 130 | [52] = 84, 131 | [53] = 85, 132 | [54] = 86, 133 | [55] = 87, 134 | [56] = 88, 135 | [57] = 89, 136 | [58] = 90, 137 | [59] = 91, 138 | [60] = 92, 139 | [61] = 93, 140 | [62] = 94, 141 | [63] = 95, 142 | [64] = 96, 143 | [65] = 97, 144 | [66] = 98, 145 | [67] = 99, 146 | [68] = 100, 147 | [69] = 101, 148 | [70] = 102, 149 | [71] = 103, 150 | [72] = 104, 151 | [73] = 105, 152 | [74] = 106, 153 | [75] = 107, 154 | [76] = 108, 155 | [77] = 109, 156 | [78] = 110, 157 | [79] = 111, 158 | [80] = 112, 159 | [81] = 113, 160 | [82] = 114, 161 | [83] = 115, 162 | [84] = 116, 163 | [85] = 117, 164 | [86] = 118, 165 | [87] = 119, 166 | [88] = 120, 167 | [89] = 121, 168 | [90] = 122, 169 | [91] = 123, 170 | [92] = 124, 171 | [93] = 125, 172 | [94] = 126, 173 | [95] = 0, 174 | }; 175 | 176 | /* 177 | * Upper Unicode Table 178 | * This maps all characters to the upper unicode characters 161-254. These are 179 | * not compatible to any older 8 bit character sets. See the Unicode standard 180 | * for the definitions of each symbol. Again, the first an last entry are never 181 | * used so set them to 0. 182 | */ 183 | tsm_vte_charset tsm_vte_unicode_upper = { 184 | [0] = 0, 185 | [1] = 161, 186 | [2] = 162, 187 | [3] = 163, 188 | [4] = 164, 189 | [5] = 165, 190 | [6] = 166, 191 | [7] = 167, 192 | [8] = 168, 193 | [9] = 169, 194 | [10] = 170, 195 | [11] = 171, 196 | [12] = 172, 197 | [13] = 173, 198 | [14] = 174, 199 | [15] = 175, 200 | [16] = 176, 201 | [17] = 177, 202 | [18] = 178, 203 | [19] = 179, 204 | [20] = 180, 205 | [21] = 181, 206 | [22] = 182, 207 | [23] = 183, 208 | [24] = 184, 209 | [25] = 185, 210 | [26] = 186, 211 | [27] = 187, 212 | [28] = 188, 213 | [29] = 189, 214 | [30] = 190, 215 | [31] = 191, 216 | [32] = 192, 217 | [33] = 193, 218 | [34] = 194, 219 | [35] = 195, 220 | [36] = 196, 221 | [37] = 197, 222 | [38] = 198, 223 | [39] = 199, 224 | [40] = 200, 225 | [41] = 201, 226 | [42] = 202, 227 | [43] = 203, 228 | [44] = 204, 229 | [45] = 205, 230 | [46] = 206, 231 | [47] = 207, 232 | [48] = 208, 233 | [49] = 209, 234 | [50] = 210, 235 | [51] = 211, 236 | [52] = 212, 237 | [53] = 213, 238 | [54] = 214, 239 | [55] = 215, 240 | [56] = 216, 241 | [57] = 217, 242 | [58] = 218, 243 | [59] = 219, 244 | [60] = 220, 245 | [61] = 221, 246 | [62] = 222, 247 | [63] = 223, 248 | [64] = 224, 249 | [65] = 225, 250 | [66] = 226, 251 | [67] = 227, 252 | [68] = 228, 253 | [69] = 229, 254 | [70] = 230, 255 | [71] = 231, 256 | [72] = 232, 257 | [73] = 233, 258 | [74] = 234, 259 | [75] = 235, 260 | [76] = 236, 261 | [77] = 237, 262 | [78] = 238, 263 | [79] = 239, 264 | [80] = 240, 265 | [81] = 241, 266 | [82] = 242, 267 | [83] = 243, 268 | [84] = 244, 269 | [85] = 245, 270 | [86] = 246, 271 | [87] = 247, 272 | [88] = 248, 273 | [89] = 249, 274 | [90] = 250, 275 | [91] = 251, 276 | [92] = 252, 277 | [93] = 253, 278 | [94] = 254, 279 | [95] = 0, 280 | }; 281 | 282 | /* 283 | * The DEC supplemental graphics set. For its definition see here: 284 | * http://vt100.net/docs/vt220-rm/table2-3b.html 285 | * Its basically a mixture of common European symbols that are not part of 286 | * ASCII. Most often, this is mapped into GR to extend the basci ASCII part. 287 | * 288 | * This is very similar to unicode_upper, however, few symbols differ so do not 289 | * mix them up! 290 | */ 291 | tsm_vte_charset tsm_vte_dec_supplemental_graphics = { 292 | [0] = 0, 293 | [1] = 161, 294 | [2] = 162, 295 | [3] = 163, 296 | [4] = 0, 297 | [5] = 165, 298 | [6] = 0, 299 | [7] = 167, 300 | [8] = 164, 301 | [9] = 169, 302 | [10] = 170, 303 | [11] = 171, 304 | [12] = 0, 305 | [13] = 0, 306 | [14] = 0, 307 | [15] = 0, 308 | [16] = 176, 309 | [17] = 177, 310 | [18] = 178, 311 | [19] = 179, 312 | [20] = 0, 313 | [21] = 181, 314 | [22] = 182, 315 | [23] = 183, 316 | [24] = 0, 317 | [25] = 185, 318 | [26] = 186, 319 | [27] = 187, 320 | [28] = 188, 321 | [29] = 189, 322 | [30] = 0, 323 | [31] = 191, 324 | [32] = 192, 325 | [33] = 193, 326 | [34] = 194, 327 | [35] = 195, 328 | [36] = 196, 329 | [37] = 197, 330 | [38] = 198, 331 | [39] = 199, 332 | [40] = 200, 333 | [41] = 201, 334 | [42] = 202, 335 | [43] = 203, 336 | [44] = 204, 337 | [45] = 205, 338 | [46] = 206, 339 | [47] = 207, 340 | [48] = 0, 341 | [49] = 209, 342 | [50] = 210, 343 | [51] = 211, 344 | [52] = 212, 345 | [53] = 213, 346 | [54] = 214, 347 | [55] = 338, 348 | [56] = 216, 349 | [57] = 217, 350 | [58] = 218, 351 | [59] = 219, 352 | [60] = 220, 353 | [61] = 376, 354 | [62] = 0, 355 | [63] = 223, 356 | [64] = 224, 357 | [65] = 225, 358 | [66] = 226, 359 | [67] = 227, 360 | [68] = 228, 361 | [69] = 229, 362 | [70] = 230, 363 | [71] = 231, 364 | [72] = 232, 365 | [73] = 233, 366 | [74] = 234, 367 | [75] = 235, 368 | [76] = 236, 369 | [77] = 237, 370 | [78] = 238, 371 | [79] = 239, 372 | [80] = 0, 373 | [81] = 241, 374 | [82] = 242, 375 | [83] = 243, 376 | [84] = 244, 377 | [85] = 245, 378 | [86] = 246, 379 | [87] = 339, 380 | [88] = 248, 381 | [89] = 249, 382 | [90] = 250, 383 | [91] = 251, 384 | [92] = 252, 385 | [93] = 255, 386 | [94] = 0, 387 | [95] = 0, 388 | }; 389 | 390 | /* 391 | * DEC special graphics character set. See here for its definition: 392 | * http://vt100.net/docs/vt220-rm/table2-4.html 393 | * This contains several characters to create ASCII drawings and similar. Its 394 | * commonly mapped into GR to extend the basic ASCII characters. 395 | * 396 | * Lower 62 characters map to ASCII 33-64, everything beyond is special and 397 | * commonly used for ASCII drawings. It depends on the Unicode Standard 3.2 for 398 | * the extended horizontal scan-line characters 3, 5, 7, and 9. 399 | */ 400 | tsm_vte_charset tsm_vte_dec_special_graphics = { 401 | [0] = 0, 402 | [1] = 33, 403 | [2] = 34, 404 | [3] = 35, 405 | [4] = 36, 406 | [5] = 37, 407 | [6] = 38, 408 | [7] = 39, 409 | [8] = 40, 410 | [9] = 41, 411 | [10] = 42, 412 | [11] = 43, 413 | [12] = 44, 414 | [13] = 45, 415 | [14] = 46, 416 | [15] = 47, 417 | [16] = 48, 418 | [17] = 49, 419 | [18] = 50, 420 | [19] = 51, 421 | [20] = 52, 422 | [21] = 53, 423 | [22] = 54, 424 | [23] = 55, 425 | [24] = 56, 426 | [25] = 57, 427 | [26] = 58, 428 | [27] = 59, 429 | [28] = 60, 430 | [29] = 61, 431 | [30] = 62, 432 | [31] = 63, 433 | [32] = 64, 434 | [33] = 65, 435 | [34] = 66, 436 | [35] = 67, 437 | [36] = 68, 438 | [37] = 69, 439 | [38] = 70, 440 | [39] = 71, 441 | [40] = 72, 442 | [41] = 73, 443 | [42] = 74, 444 | [43] = 75, 445 | [44] = 76, 446 | [45] = 77, 447 | [46] = 78, 448 | [47] = 79, 449 | [48] = 80, 450 | [49] = 81, 451 | [50] = 82, 452 | [51] = 83, 453 | [52] = 84, 454 | [53] = 85, 455 | [54] = 86, 456 | [55] = 87, 457 | [56] = 88, 458 | [57] = 89, 459 | [58] = 90, 460 | [59] = 91, 461 | [60] = 92, 462 | [61] = 93, 463 | [62] = 94, 464 | [63] = 0, 465 | [64] = 9830, 466 | [65] = 9618, 467 | [66] = 9225, 468 | [67] = 9228, 469 | [68] = 9229, 470 | [69] = 9226, 471 | [70] = 176, 472 | [71] = 177, 473 | [72] = 9252, 474 | [73] = 9227, 475 | [74] = 9496, 476 | [75] = 9488, 477 | [76] = 9484, 478 | [77] = 9492, 479 | [78] = 9532, 480 | [79] = 9146, 481 | [80] = 9147, 482 | [81] = 9472, 483 | [82] = 9148, 484 | [83] = 9149, 485 | [84] = 9500, 486 | [85] = 9508, 487 | [86] = 9524, 488 | [87] = 9516, 489 | [88] = 9474, 490 | [89] = 8804, 491 | [90] = 8805, 492 | [91] = 960, 493 | [92] = 8800, 494 | [93] = 163, 495 | [94] = 8901, 496 | [95] = 0, 497 | }; 498 | -------------------------------------------------------------------------------- /src/terminal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Giulio Camuffo 3 | * 4 | * This file is part of Termistor 5 | * 6 | * Termistor is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Termistor is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Termistor. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "terminal.h" 30 | #include "vte.h" 31 | #include "screen.h" 32 | 33 | static const QStringList ACCEPTED_MIMETYPES = { "text/plain;charset=utf-8", "text/plain" }; 34 | 35 | Terminal::Terminal(QWindow *parent) 36 | : QWindow(parent) 37 | , m_updatePending(false) 38 | , m_borders(2, 0, 2, 20) 39 | , m_bordersDirty(true) 40 | , m_backingStore(nullptr) 41 | { 42 | setSurfaceType(QWindow::RasterSurface); 43 | 44 | m_currentScreen = 0; 45 | addScreen(); 46 | } 47 | 48 | void Terminal::render() 49 | { 50 | QPainter painter(m_backingStore->paintDevice()); 51 | painter.setCompositionMode(QPainter::CompositionMode_Source); 52 | 53 | if (m_bordersDirty) { 54 | m_bordersDirty = false; 55 | 56 | QFont font; 57 | font.setBold(true); 58 | painter.setFont(font); 59 | 60 | const QRect &geom = geometry(); 61 | painter.fillRect(QRect(QPoint(0, 0), QPoint(m_borders.left(), geom.bottom())), Qt::white); 62 | painter.fillRect(QRect(QPoint(geom.right() - m_borders.right(), 0), geom.bottomRight()), Qt::white); 63 | painter.fillRect(QRect(QPoint(0, geom.bottom() - m_borders.bottom()), geom.bottomRight()), Qt::white); 64 | painter.drawLine(geom.topLeft(), geom.bottomLeft()); 65 | painter.drawLine(geom.topRight(), geom.bottomRight()); 66 | painter.drawLine(geom.bottomLeft(), geom.bottomRight()); 67 | 68 | painter.setBrush(Qt::NoBrush); 69 | painter.setPen(Qt::black); 70 | const QRect &ar = addScreenRect(); 71 | painter.drawRect(ar); 72 | painter.drawText(ar, Qt::AlignCenter, "+"); 73 | const QRect &dr = delScreenRect(); 74 | painter.drawRect(dr); 75 | painter.drawText(dr, Qt::AlignCenter, "-"); 76 | const QRect &qr = quitRect(); 77 | painter.drawRect(qr); 78 | painter.drawText(qr, Qt::AlignCenter, "x"); 79 | 80 | for (int i = 0; i < m_screens.size(); ++i) { 81 | const QRect &r = tabRect(i); 82 | painter.drawRect(r); 83 | font.setBold(m_currentScreen == i); 84 | painter.setFont(font); 85 | painter.drawText(r, Qt::AlignCenter, m_screens.at(i)->name()); 86 | } 87 | } 88 | 89 | painter.setBrush(Qt::SolidPattern); 90 | painter.translate(m_borders.left(), m_borders.top()); 91 | currentScreen()->render(&painter); 92 | } 93 | 94 | bool Terminal::event(QEvent *event) 95 | { 96 | switch (event->type()) { 97 | case QEvent::UpdateRequest: 98 | renderNow(); 99 | return true; 100 | case QEvent::KeyPress: { 101 | QKeyEvent *ev = static_cast(event); 102 | if (ev->modifiers() == Qt::ShiftModifier) { 103 | if (ev->key() == Qt::Key_Left) { 104 | int s = m_currentScreen - 1; 105 | if (s < 0) s = m_screens.size() - 1; 106 | setScreen(s); 107 | break; 108 | } else if (ev->key() == Qt::Key_Right) { 109 | int s = m_currentScreen + 1; 110 | if (s >= m_screens.size()) s = 0; 111 | setScreen(s); 112 | break; 113 | } else if (ev->key() == Qt::Key_Up) { 114 | addScreen(); 115 | break; 116 | } else if (ev->key() == Qt::Key_Insert) { 117 | paste(); 118 | break; 119 | } 120 | } else if (ev->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { 121 | if (ev->key() == Qt::Key_Left) { 122 | moveScreen(m_currentScreen, -1); 123 | break; 124 | } else if (ev->key() == Qt::Key_Right) { 125 | moveScreen(m_currentScreen, +1); 126 | break; 127 | } else if (ev->key() == Qt::Key_C) { 128 | QByteArray data = currentScreen()->copy(); 129 | if (!data.isEmpty()) { 130 | QMimeData *mime = new QMimeData; 131 | for (auto &t: ACCEPTED_MIMETYPES) { 132 | mime->setData(t, data); 133 | } 134 | QGuiApplication::clipboard()->setMimeData(mime); 135 | } 136 | break; 137 | } else if (ev->key() == Qt::Key_V) { 138 | paste(); 139 | break; 140 | } 141 | } 142 | currentScreen()->keyPressEvent(ev); 143 | } break; 144 | case QEvent::Wheel: 145 | currentScreen()->wheelEvent(static_cast(event)); 146 | break; 147 | case QEvent::MouseButtonPress: { 148 | QMouseEvent *ev = static_cast(event); 149 | const QPoint &p = ev->windowPos().toPoint(); 150 | for (int i = 0; i < m_screens.size(); ++i) { 151 | if (tabRect(i).contains(p)) { 152 | setScreen(i); 153 | break; 154 | } 155 | } 156 | if (addScreenRect().contains(p)) { 157 | addScreen(); 158 | } else if (delScreenRect().contains(p)) { 159 | delScreen(); 160 | } else if (quitRect().contains(p)) { 161 | close(); 162 | } else { 163 | if (ev->button() == Qt::MiddleButton) { 164 | paste(); 165 | break; 166 | } else if (ev->button() == Qt::LeftButton) { 167 | currentScreen()->mousePressEvent(ev); 168 | } 169 | } 170 | } break; 171 | case QEvent::MouseMove: { 172 | QMouseEvent *ev = static_cast(event); 173 | if (ev->buttons() == Qt::LeftButton) { 174 | currentScreen()->mouseMoveEvent(ev); 175 | } else { 176 | QRect geom = geometry().marginsRemoved(m_borders); 177 | if (geom.contains(ev->pos())) { 178 | setCursor(QCursor(Qt::IBeamCursor)); 179 | } else { 180 | setCursor(QCursor(Qt::ArrowCursor)); 181 | } 182 | } 183 | } break; 184 | case QEvent::MouseButtonDblClick: { 185 | QMouseEvent *ev = static_cast(event); 186 | QRect geom = geometry().marginsRemoved(m_borders); 187 | if (geom.contains(ev->pos())) { 188 | currentScreen()->mouseDoubleClickEvent(ev); 189 | } 190 | } break; 191 | default: 192 | break; 193 | } 194 | 195 | return QWindow::event(event); 196 | } 197 | 198 | void Terminal::exposeEvent(QExposeEvent *event) 199 | { 200 | Q_UNUSED(event); 201 | renderNow(); 202 | } 203 | 204 | void Terminal::resizeEvent(QResizeEvent *event) 205 | { 206 | Q_UNUSED(event); 207 | 208 | if (m_backingStore) { 209 | m_backingStore->resize(size()); 210 | } 211 | 212 | currentScreen()->resize(size() - QSize(m_borders.left() + m_borders.right(), m_borders.top() + m_borders.bottom())); 213 | m_bordersDirty = true; 214 | renderNow(); 215 | } 216 | 217 | void Terminal::focusInEvent(QFocusEvent *event) 218 | { 219 | currentScreen()->focusIn(); 220 | m_hasFocus = true; 221 | } 222 | 223 | void Terminal::focusOutEvent(QFocusEvent *event) 224 | { 225 | currentScreen()->focusOut(); 226 | m_hasFocus = false; 227 | } 228 | 229 | void Terminal::setScreenSize(const QSize &s) 230 | { 231 | resize(s + QSize(m_borders.left() + m_borders.right(), m_borders.top() + m_borders.bottom())); 232 | } 233 | 234 | void Terminal::update() 235 | { 236 | if (!m_updatePending) { 237 | m_updatePending = true; 238 | QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); 239 | } 240 | } 241 | 242 | void Terminal::renderNow() 243 | { 244 | if (!isExposed()) 245 | return; 246 | 247 | m_updatePending = false; 248 | 249 | if (!m_backingStore) { 250 | m_backingStore = new QBackingStore(this); 251 | m_backingStore->resize(size()); 252 | } 253 | 254 | m_backingStore->beginPaint(geometry()); 255 | 256 | render(); 257 | 258 | m_backingStore->endPaint(); 259 | m_backingStore->flush(QRect(0, 0, width(),height()), this); 260 | } 261 | 262 | void Terminal::closeScreen(Screen *screen) 263 | { 264 | Screen *cur = currentScreen(); 265 | m_screens.removeOne(screen); 266 | 267 | if (cur == screen) { 268 | if (m_screens.isEmpty()) { 269 | addScreen(); 270 | } else { 271 | int s = m_currentScreen - 1; 272 | if (s < 0) s = m_screens.size() - 1; 273 | setScreen(s); 274 | } 275 | } 276 | delete screen; 277 | } 278 | 279 | void Terminal::addScreen() 280 | { 281 | static int i = 1; 282 | m_screens << new Screen(this, QString("Shell %1").arg(i++)); 283 | setScreen(m_screens.size() - 1); 284 | } 285 | 286 | void Terminal::delScreen() 287 | { 288 | if (m_screens.size() == 1) { 289 | return; 290 | } 291 | 292 | closeScreen(currentScreen()); 293 | } 294 | 295 | void Terminal::setScreen(int i) 296 | { 297 | currentScreen()->focusOut(); 298 | m_currentScreen = i; 299 | currentScreen()->forceRedraw(); 300 | currentScreen()->resize(size() - QSize(m_borders.left() + m_borders.right(), m_borders.top() + m_borders.bottom())); 301 | m_bordersDirty = true; 302 | m_hasFocus ? currentScreen()->focusIn() : currentScreen()->focusOut(); 303 | update(); 304 | } 305 | 306 | static const int buttonsHeight = 16; 307 | 308 | QRect Terminal::addScreenRect() const 309 | { 310 | return QRect(5, geometry().bottom() - m_borders.bottom() + (m_borders.bottom() - buttonsHeight) / 2, buttonsHeight, buttonsHeight); 311 | } 312 | 313 | QRect Terminal::delScreenRect() const 314 | { 315 | return QRect(26, geometry().bottom() - m_borders.bottom() + (m_borders.bottom() - buttonsHeight) / 2, buttonsHeight, buttonsHeight); 316 | } 317 | 318 | QRect Terminal::tabRect(int i) const 319 | { 320 | const int start = 54; 321 | const int margin = 5; 322 | float bwidth = 70.f; 323 | int maxwidth = width() - start - buttonsHeight - 5; 324 | if ((bwidth + margin) * m_screens.size() > maxwidth) { 325 | bwidth = (maxwidth - margin * m_screens.size()) / (float)m_screens.size(); 326 | } 327 | return QRect(start + (bwidth + margin) * i, geometry().bottom() - m_borders.bottom() + (m_borders.bottom() - buttonsHeight) / 2, bwidth, buttonsHeight); 328 | } 329 | 330 | QRect Terminal::quitRect() const 331 | { 332 | return QRect(width() - buttonsHeight - 5, geometry().bottom() - m_borders.bottom() + (m_borders.bottom() - buttonsHeight) / 2, buttonsHeight, buttonsHeight); 333 | } 334 | 335 | void Terminal::paste() 336 | { 337 | QClipboard *clipboard = QGuiApplication::clipboard(); 338 | const QMimeData *data = clipboard->mimeData(); 339 | if (!data) { 340 | return; 341 | } 342 | QStringList availableFormats = data->formats(); 343 | for (auto &mime: ACCEPTED_MIMETYPES) { 344 | if (availableFormats.contains(mime)) { 345 | currentScreen()->paste(data->data(mime)); 346 | break; 347 | } 348 | } 349 | } 350 | 351 | void Terminal::moveScreen(int cur, int d) 352 | { 353 | int index = cur + d; 354 | int size = m_screens.size(); 355 | while (index < 0) index += size; 356 | while (index >= size) index -= size; 357 | 358 | Screen *screen = m_screens.takeAt(cur); 359 | m_screens.insert(index, screen); 360 | m_currentScreen = index; 361 | 362 | m_bordersDirty = true; 363 | update(); 364 | } 365 | 366 | // trick to put a newline at app close 367 | static struct A { ~A() { fprintf(stderr, "\n"); } } a; 368 | 369 | bool Debugger::printedCache = false; 370 | int Debugger::cacheNum = 0; 371 | int Debugger::cacheSize = 0; 372 | 373 | void Debugger::print(const char *msg) 374 | { 375 | fprintf(stderr, "\033[2K%s\n",msg); 376 | printedCache = false; 377 | printCache(cacheNum, cacheSize); 378 | } 379 | 380 | void Debugger::printCache(int num, int size) 381 | { 382 | fprintf(stderr, "\033[2KCache: %d images taking approximately %gkB\r", num, size / 1000.f); 383 | printedCache = true; 384 | cacheNum = num; 385 | cacheSize = size; 386 | } 387 | -------------------------------------------------------------------------------- /libtsm/src/shared/shl-pty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - PTY Helpers 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * PTY Helpers 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "shl-macro.h" 28 | #include "shl-pty.h" 29 | #include "shl-ring.h" 30 | 31 | #define SHL_PTY_BUFSIZE 16384 32 | 33 | /* 34 | * PTY 35 | * A PTY object represents a single PTY connection between a master and a 36 | * child. The child process is fork()ed so the caller controls what program 37 | * will be run. 38 | * 39 | * Programs like /bin/login tend to perform a vhangup() on their TTY 40 | * before running the login procedure. This also causes the pty master 41 | * to get a EPOLLHUP event as long as no client has the TTY opened. 42 | * This means, we cannot use the TTY connection as reliable way to track 43 | * the client. Instead, we _must_ rely on the PID of the client to track 44 | * them. 45 | * However, this has the side effect that if the client forks and the 46 | * parent exits, we loose them and restart the client. But this seems to 47 | * be the expected behavior so we implement it here. 48 | * 49 | * Unfortunately, epoll always polls for EPOLLHUP so as long as the 50 | * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep. 51 | * This gets worse if the client closes the TTY but doesn't exit. 52 | * Therefore, the fd must be edge-triggered in the epoll-set so we 53 | * only get the events once they change. This has to be taken into account by 54 | * the user of shl_pty. As many event-loops don't support edge-triggered 55 | * behavior, you can use the shl_pty_bridge interface. 56 | * 57 | * Note that shl_pty does not track SIGHUP, you need to do that yourself 58 | * and call shl_pty_close() once the client exited. 59 | */ 60 | 61 | struct shl_pty { 62 | unsigned long ref; 63 | int fd; 64 | pid_t child; 65 | char in_buf[SHL_PTY_BUFSIZE]; 66 | struct shl_ring out_buf; 67 | 68 | shl_pty_input_fn fn_input; 69 | void *fn_input_data; 70 | }; 71 | 72 | enum shl_pty_msg { 73 | SHL_PTY_FAILED, 74 | SHL_PTY_SETUP, 75 | }; 76 | 77 | static char pty_recv(int fd) 78 | { 79 | int r; 80 | char d; 81 | 82 | do { 83 | r = read(fd, &d, 1); 84 | } while (r < 0 && (errno == EINTR || errno == EAGAIN)); 85 | 86 | return (r <= 0) ? SHL_PTY_FAILED : d; 87 | } 88 | 89 | static int pty_send(int fd, char d) 90 | { 91 | int r; 92 | 93 | do { 94 | r = write(fd, &d, 1); 95 | } while (r < 0 && (errno == EINTR || errno == EAGAIN)); 96 | 97 | return (r == 1) ? 0 : -EINVAL; 98 | } 99 | 100 | static int pty_setup_child(int slave, 101 | unsigned short term_width, 102 | unsigned short term_height) 103 | { 104 | struct termios attr; 105 | struct winsize ws; 106 | 107 | /* get terminal attributes */ 108 | if (tcgetattr(slave, &attr) < 0) 109 | return -errno; 110 | 111 | /* erase character should be normal backspace, PLEASEEE! */ 112 | attr.c_cc[VERASE] = 010; 113 | /* always set UTF8 flag */ 114 | attr.c_iflag |= IUTF8; 115 | 116 | /* set changed terminal attributes */ 117 | if (tcsetattr(slave, TCSANOW, &attr) < 0) 118 | return -errno; 119 | 120 | memset(&ws, 0, sizeof(ws)); 121 | ws.ws_col = term_width; 122 | ws.ws_row = term_height; 123 | 124 | if (ioctl(slave, TIOCSWINSZ, &ws) < 0) 125 | return -errno; 126 | 127 | if (dup2(slave, STDIN_FILENO) != STDIN_FILENO || 128 | dup2(slave, STDOUT_FILENO) != STDOUT_FILENO || 129 | dup2(slave, STDERR_FILENO) != STDERR_FILENO) 130 | return -errno; 131 | 132 | return 0; 133 | } 134 | 135 | static int pty_init_child(int fd) 136 | { 137 | int r; 138 | sigset_t sigset; 139 | char *slave_name; 140 | int slave, i; 141 | pid_t pid; 142 | 143 | /* unlockpt() requires unset signal-handlers */ 144 | sigemptyset(&sigset); 145 | r = sigprocmask(SIG_SETMASK, &sigset, NULL); 146 | if (r < 0) 147 | return -errno; 148 | 149 | for (i = 1; i < SIGUNUSED; ++i) 150 | signal(i, SIG_DFL); 151 | 152 | r = grantpt(fd); 153 | if (r < 0) 154 | return -errno; 155 | 156 | r = unlockpt(fd); 157 | if (r < 0) 158 | return -errno; 159 | 160 | slave_name = ptsname(fd); 161 | if (!slave_name) 162 | return -errno; 163 | 164 | /* open slave-TTY */ 165 | slave = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY); 166 | if (slave < 0) 167 | return -errno; 168 | 169 | /* open session so we loose our controlling TTY */ 170 | pid = setsid(); 171 | if (pid < 0) { 172 | close(slave); 173 | return -errno; 174 | } 175 | 176 | /* set controlling TTY */ 177 | r = ioctl(slave, TIOCSCTTY, 0); 178 | if (r < 0) { 179 | close(slave); 180 | return -errno; 181 | } 182 | 183 | return slave; 184 | } 185 | 186 | pid_t shl_pty_open(struct shl_pty **out, 187 | shl_pty_input_fn fn_input, 188 | void *fn_input_data, 189 | unsigned short term_width, 190 | unsigned short term_height) 191 | { 192 | _shl_pty_unref_ struct shl_pty *pty = NULL; 193 | _shl_close_ int fd = -1; 194 | int slave, r, comm[2]; 195 | pid_t pid; 196 | char d; 197 | 198 | if (!out) 199 | return -EINVAL; 200 | 201 | pty = calloc(1, sizeof(*pty)); 202 | if (!pty) 203 | return -ENOMEM; 204 | 205 | pty->ref = 1; 206 | pty->fd = -1; 207 | pty->fn_input = fn_input; 208 | pty->fn_input_data = fn_input_data; 209 | 210 | fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); 211 | if (fd < 0) 212 | return -errno; 213 | 214 | r = pipe2(comm, O_CLOEXEC); 215 | if (r < 0) 216 | return -errno; 217 | 218 | pid = fork(); 219 | if (pid < 0) { 220 | /* error */ 221 | pid = -errno; 222 | close(comm[0]); 223 | close(comm[1]); 224 | return pid; 225 | } else if (!pid) { 226 | /* child */ 227 | slave = pty_init_child(fd); 228 | if (slave < 0) 229 | exit(1); 230 | 231 | close(comm[0]); 232 | close(fd); 233 | fd = -1; 234 | free(pty); 235 | pty = NULL; 236 | 237 | r = pty_setup_child(slave, term_width, term_height); 238 | if (r < 0) 239 | exit(1); 240 | 241 | /* close slave if it's not one of the std-fds */ 242 | if (slave > 2) 243 | close(slave); 244 | 245 | /* wake parent */ 246 | pty_send(comm[1], SHL_PTY_SETUP); 247 | close(comm[1]); 248 | 249 | *out = NULL; 250 | return pid; 251 | } 252 | 253 | /* parent */ 254 | pty->fd = fd; 255 | pty->child = pid; 256 | 257 | close(comm[1]); 258 | fd = -1; 259 | 260 | /* wait for child setup */ 261 | d = pty_recv(comm[0]); 262 | close(comm[0]); 263 | if (d != SHL_PTY_SETUP) 264 | return -EINVAL; 265 | 266 | *out = pty; 267 | pty = NULL; 268 | return pid; 269 | } 270 | 271 | void shl_pty_ref(struct shl_pty *pty) 272 | { 273 | if (!pty || !pty->ref) 274 | return; 275 | 276 | ++pty->ref; 277 | } 278 | 279 | void shl_pty_unref(struct shl_pty *pty) 280 | { 281 | if (!pty || !pty->ref || --pty->ref) 282 | return; 283 | 284 | shl_pty_close(pty); 285 | shl_ring_clear(&pty->out_buf); 286 | free(pty); 287 | } 288 | 289 | void shl_pty_close(struct shl_pty *pty) 290 | { 291 | if (!pty || pty->fd < 0) 292 | return; 293 | 294 | close(pty->fd); 295 | pty->fd = -1; 296 | } 297 | 298 | bool shl_pty_is_open(struct shl_pty *pty) 299 | { 300 | return pty && pty->fd >= 0; 301 | } 302 | 303 | int shl_pty_get_fd(struct shl_pty *pty) 304 | { 305 | if (!pty) 306 | return -EINVAL; 307 | 308 | return pty->fd >= 0 ? pty->fd : -EPIPE; 309 | } 310 | 311 | pid_t shl_pty_get_child(struct shl_pty *pty) 312 | { 313 | if (!pty) 314 | return -EINVAL; 315 | 316 | return pty->child > 0 ? pty->child : -ECHILD; 317 | } 318 | 319 | static int pty_write(struct shl_pty *pty) 320 | { 321 | struct iovec vec[2]; 322 | unsigned int i; 323 | size_t num; 324 | ssize_t r; 325 | 326 | /* 327 | * Same as pty_read(), we're edge-triggered so we need to call write() 328 | * until either all data is written or it return EAGAIN. We call it 329 | * twice and if it still writes successfully, we return EAGAIN. If we 330 | * bail out early, we also return EAGAIN if there's still data. 331 | */ 332 | 333 | for (i = 0; i < 2; ++i) { 334 | num = shl_ring_peek(&pty->out_buf, vec); 335 | if (!num) 336 | return 0; 337 | 338 | r = writev(pty->fd, vec, (int)num); 339 | if (r < 0) { 340 | if (errno == EAGAIN) 341 | return 0; 342 | if (errno == EINTR) 343 | return -EAGAIN; 344 | 345 | return -errno; 346 | } else if (!r) { 347 | return -EPIPE; 348 | } else { 349 | shl_ring_pull(&pty->out_buf, (size_t)r); 350 | } 351 | } 352 | 353 | return shl_ring_get_size(&pty->out_buf) > 0 ? -EAGAIN : 0; 354 | } 355 | 356 | static int pty_read(struct shl_pty *pty) 357 | { 358 | unsigned int i; 359 | ssize_t len; 360 | 361 | /* 362 | * We're edge-triggered, means we need to read the whole queue. This, 363 | * however, might cause us to stall if the writer is faster than we 364 | * are. Therefore, we read twice and if the second read still returned 365 | * data, we return -EAGAIN and let the caller deal with rescheduling the 366 | * dispatcher. 367 | */ 368 | 369 | for (i = 0; i < 2; ++i) { 370 | len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1); 371 | if (len < 0) { 372 | if (errno == EAGAIN) 373 | return 0; 374 | if (errno == EINTR) 375 | return -EAGAIN; 376 | 377 | return -errno; 378 | } else if (!len) { 379 | return -EPIPE; 380 | } else if (len > 0 && pty->fn_input) { 381 | /* set terminating zero for debugging safety */ 382 | pty->in_buf[len] = 0; 383 | pty->fn_input(pty, 384 | pty->fn_input_data, 385 | pty->in_buf, 386 | len); 387 | } 388 | } 389 | 390 | return -EAGAIN; 391 | } 392 | 393 | int shl_pty_dispatch(struct shl_pty *pty) 394 | { 395 | int r; 396 | 397 | if (!shl_pty_is_open(pty)) 398 | return -ENODEV; 399 | 400 | r = pty_read(pty); 401 | pty_write(pty); 402 | return r; 403 | } 404 | 405 | int shl_pty_write(struct shl_pty *pty, const char *u8, size_t len) 406 | { 407 | if (!shl_pty_is_open(pty)) 408 | return -ENODEV; 409 | 410 | return shl_ring_push(&pty->out_buf, u8, len); 411 | } 412 | 413 | int shl_pty_signal(struct shl_pty *pty, int sig) 414 | { 415 | if (!shl_pty_is_open(pty)) 416 | return -ENODEV; 417 | 418 | return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0; 419 | } 420 | 421 | int shl_pty_resize(struct shl_pty *pty, 422 | unsigned short term_width, 423 | unsigned short term_height) 424 | { 425 | struct winsize ws; 426 | 427 | if (!shl_pty_is_open(pty)) 428 | return -ENODEV; 429 | 430 | memset(&ws, 0, sizeof(ws)); 431 | ws.ws_col = term_width; 432 | ws.ws_row = term_height; 433 | 434 | /* 435 | * This will send SIGWINCH to the pty slave foreground process group. 436 | * We will also get one, but we don't need it. 437 | */ 438 | return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0; 439 | } 440 | 441 | /* 442 | * PTY Bridge 443 | * The PTY bridge wraps multiple ptys in a single file-descriptor. It is 444 | * enough for the caller to listen for read-events on the fd. 445 | * 446 | * This interface is provided to allow integration of PTYs into event-loops 447 | * that do not support edge-triggered interfaces. There is no other reason 448 | * to use this bridge. 449 | */ 450 | 451 | int shl_pty_bridge_new(void) 452 | { 453 | int fd; 454 | 455 | fd = epoll_create1(EPOLL_CLOEXEC); 456 | if (fd < 0) 457 | return -errno; 458 | 459 | return fd; 460 | } 461 | 462 | void shl_pty_bridge_free(int bridge) 463 | { 464 | if (bridge < 0) 465 | return; 466 | 467 | close(bridge); 468 | } 469 | 470 | int shl_pty_bridge_dispatch_pty(int bridge, struct shl_pty *pty) 471 | { 472 | struct epoll_event up; 473 | int r; 474 | 475 | if (bridge < 0 || !pty) 476 | return -EINVAL; 477 | 478 | r = shl_pty_dispatch(pty); 479 | if (r == -EAGAIN) { 480 | /* EAGAIN means we couldn't dispatch data fast enough. Modify 481 | * the fd in the epoll-set so we get edge-triggered events 482 | * next round. */ 483 | memset(&up, 0, sizeof(up)); 484 | up.events = EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET; 485 | up.data.ptr = pty; 486 | epoll_ctl(bridge, 487 | EPOLL_CTL_ADD, 488 | shl_pty_get_fd(pty), 489 | &up); 490 | } 491 | 492 | return 0; 493 | } 494 | 495 | int shl_pty_bridge_dispatch(int bridge, int timeout) 496 | { 497 | struct epoll_event ev; 498 | struct shl_pty *pty; 499 | int r; 500 | 501 | if (bridge < 0) 502 | return -EINVAL; 503 | 504 | r = epoll_wait(bridge, &ev, 1, timeout); 505 | if (r < 0) { 506 | if (errno == EAGAIN || errno == EINTR) 507 | return 0; 508 | 509 | return -errno; 510 | } 511 | 512 | if (!r) 513 | return 0; 514 | 515 | pty = ev.data.ptr; 516 | return shl_pty_bridge_dispatch_pty(bridge, pty); 517 | } 518 | 519 | int shl_pty_bridge_add(int bridge, struct shl_pty *pty) 520 | { 521 | struct epoll_event ev; 522 | int r; 523 | 524 | if (bridge < 0) 525 | return -EINVAL; 526 | if (!shl_pty_is_open(pty)) 527 | return -ENODEV; 528 | 529 | memset(&ev, 0, sizeof(ev)); 530 | ev.events = EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET; 531 | ev.data.ptr = pty; 532 | 533 | r = epoll_ctl(bridge, 534 | EPOLL_CTL_ADD, 535 | shl_pty_get_fd(pty), 536 | &ev); 537 | if (r < 0) 538 | return -errno; 539 | 540 | return 0; 541 | } 542 | 543 | void shl_pty_bridge_remove(int bridge, struct shl_pty *pty) 544 | { 545 | if (bridge < 0 || !shl_pty_is_open(pty)) 546 | return; 547 | 548 | epoll_ctl(bridge, 549 | EPOLL_CTL_DEL, 550 | shl_pty_get_fd(pty), 551 | NULL); 552 | } 553 | -------------------------------------------------------------------------------- /libtsm/src/tsm/libtsm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Main Header 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef TSM_LIBTSM_H 27 | #define TSM_LIBTSM_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /** 39 | * @mainpage 40 | * 41 | * TSM is a Terminal-emulator State Machine. It implements all common DEC-VT100 42 | * to DEC-VT520 control codes and features. A state-machine is used to parse TTY 43 | * input and saved in a virtual screen. TSM does not provide any rendering, 44 | * glyph/font handling or anything more advanced. TSM is just a simple 45 | * state-machine for control-codes handling. 46 | * The main use-case for TSM are terminal-emulators. TSM has no dependencies 47 | * other than an ISO-C99 compiler and C-library. Any terminal emulator for any 48 | * window-environment or rendering-pipline can make use of TSM. However, TSM can 49 | * also be used for control-code validation, TTY-screen-capturing or other 50 | * advanced users of terminal escape-sequences. 51 | */ 52 | 53 | /** 54 | * @defgroup misc Miscellaneous Definitions 55 | * Miscellaneous definitions 56 | * 57 | * This section contains several miscellaneous definitions of small helpers and 58 | * constants. These are shared between other parts of the API and have common 59 | * semantics/syntax. 60 | * 61 | * @{ 62 | */ 63 | 64 | /** 65 | * Logging Callback 66 | * 67 | * @data: user-provided data 68 | * @file: Source code file where the log message originated or NULL 69 | * @line: Line number in source code or 0 70 | * @func: C function name or NULL 71 | * @subs: Subsystem where the message came from or NULL 72 | * @sev: Kernel-style severity between 0=FATAL and 7=DEBUG 73 | * @format: printf-formatted message 74 | * @args: arguments for printf-style @format 75 | * 76 | * This is the type of a logging callback function. You can always pass NULL 77 | * instead of such a function to disable logging. 78 | */ 79 | typedef void (*tsm_log_t) (void *data, 80 | const char *file, 81 | int line, 82 | const char *func, 83 | const char *subs, 84 | unsigned int sev, 85 | const char *format, 86 | va_list args); 87 | 88 | /** @} */ 89 | 90 | /** 91 | * @defgroup symbols Unicode Helpers 92 | * Unicode helpers 93 | * 94 | * Unicode uses 32bit types to uniquely represent symbols. However, combining 95 | * characters allow modifications of such symbols but require additional space. 96 | * To avoid passing around allocated strings, TSM provides a symbol-table which 97 | * can store combining-characters with their base-symbol to create a new symbol. 98 | * This way, only the symbol-identifiers have to be passed around (which are 99 | * simple integers). No string allocation is needed by the API user. 100 | * 101 | * The symbol table is currently not exported. Once the API is fixed, we will 102 | * provide it to outside users. 103 | * 104 | * Additionally, this contains some general UTF8/UCS4 helpers. 105 | * 106 | * @{ 107 | */ 108 | 109 | /* UCS4 helpers */ 110 | 111 | #define TSM_UCS4_MAX (0x7fffffffUL) 112 | #define TSM_UCS4_INVALID (TSM_UCS4_MAX + 1) 113 | #define TSM_UCS4_REPLACEMENT (0xfffdUL) 114 | 115 | /* ucs4 to utf8 converter */ 116 | 117 | unsigned int tsm_ucs4_get_width(uint32_t ucs4); 118 | size_t tsm_ucs4_to_utf8(uint32_t ucs4, char *out); 119 | char *tsm_ucs4_to_utf8_alloc(const uint32_t *ucs4, size_t len, size_t *len_out); 120 | 121 | /* symbols */ 122 | 123 | typedef uint32_t tsm_symbol_t; 124 | 125 | /** @} */ 126 | 127 | /** 128 | * @defgroup screen Terminal Screens 129 | * Virtual terminal-screen implementation 130 | * 131 | * A TSM screen respresents the real screen of a terminal/application. It does 132 | * not render anything, but only provides a table of cells. Each cell contains 133 | * the stored symbol, attributes and more. Applications iterate a screen to 134 | * render each cell on their framebuffer. 135 | * 136 | * Screens provide all features that are expected from terminals. They include 137 | * scroll-back buffers, alternate screens, cursor positions and selection 138 | * support. Thus, it needs event-input from applications to drive these 139 | * features. Most of them are optional, though. 140 | * 141 | * @{ 142 | */ 143 | 144 | struct tsm_screen; 145 | typedef uint_fast32_t tsm_age_t; 146 | 147 | #define TSM_SCREEN_INSERT_MODE 0x01 148 | #define TSM_SCREEN_AUTO_WRAP 0x02 149 | #define TSM_SCREEN_REL_ORIGIN 0x04 150 | #define TSM_SCREEN_INVERSE 0x08 151 | #define TSM_SCREEN_HIDE_CURSOR 0x10 152 | #define TSM_SCREEN_FIXED_POS 0x20 153 | #define TSM_SCREEN_ALTERNATE 0x40 154 | 155 | struct tsm_screen_attr { 156 | int8_t fccode; /* foreground color code or <0 for rgb */ 157 | int8_t bccode; /* background color code or <0 for rgb */ 158 | uint8_t fr; /* foreground red */ 159 | uint8_t fg; /* foreground green */ 160 | uint8_t fb; /* foreground blue */ 161 | uint8_t br; /* background red */ 162 | uint8_t bg; /* background green */ 163 | uint8_t bb; /* background blue */ 164 | unsigned int bold : 1; /* bold character */ 165 | unsigned int underline : 1; /* underlined character */ 166 | unsigned int inverse : 1; /* inverse colors */ 167 | unsigned int protect : 1; /* cannot be erased */ 168 | unsigned int blink : 1; /* blinking character */ 169 | }; 170 | 171 | typedef int (*tsm_screen_draw_cb) (struct tsm_screen *con, 172 | uint32_t id, 173 | const uint32_t *ch, 174 | size_t len, 175 | unsigned int width, 176 | unsigned int posx, 177 | unsigned int posy, 178 | const struct tsm_screen_attr *attr, 179 | tsm_age_t age, 180 | void *data); 181 | 182 | int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data); 183 | void tsm_screen_ref(struct tsm_screen *con); 184 | void tsm_screen_unref(struct tsm_screen *con); 185 | 186 | unsigned int tsm_screen_get_width(struct tsm_screen *con); 187 | unsigned int tsm_screen_get_height(struct tsm_screen *con); 188 | int tsm_screen_resize(struct tsm_screen *con, unsigned int x, 189 | unsigned int y); 190 | int tsm_screen_set_margins(struct tsm_screen *con, 191 | unsigned int top, unsigned int bottom); 192 | void tsm_screen_set_max_sb(struct tsm_screen *con, unsigned int max); 193 | void tsm_screen_clear_sb(struct tsm_screen *con); 194 | 195 | void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num); 196 | void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num); 197 | void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num); 198 | void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num); 199 | void tsm_screen_sb_reset(struct tsm_screen *con); 200 | 201 | void tsm_screen_set_def_attr(struct tsm_screen *con, 202 | const struct tsm_screen_attr *attr); 203 | void tsm_screen_reset(struct tsm_screen *con); 204 | void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags); 205 | void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags); 206 | unsigned int tsm_screen_get_flags(struct tsm_screen *con); 207 | 208 | unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con); 209 | unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con); 210 | 211 | void tsm_screen_set_tabstop(struct tsm_screen *con); 212 | void tsm_screen_reset_tabstop(struct tsm_screen *con); 213 | void tsm_screen_reset_all_tabstops(struct tsm_screen *con); 214 | 215 | void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch, 216 | const struct tsm_screen_attr *attr); 217 | void tsm_screen_newline(struct tsm_screen *con); 218 | void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num); 219 | void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num); 220 | void tsm_screen_move_to(struct tsm_screen *con, unsigned int x, 221 | unsigned int y); 222 | void tsm_screen_move_up(struct tsm_screen *con, unsigned int num, 223 | bool scroll); 224 | void tsm_screen_move_down(struct tsm_screen *con, unsigned int num, 225 | bool scroll); 226 | void tsm_screen_move_left(struct tsm_screen *con, unsigned int num); 227 | void tsm_screen_move_right(struct tsm_screen *con, unsigned int num); 228 | void tsm_screen_move_line_end(struct tsm_screen *con); 229 | void tsm_screen_move_line_home(struct tsm_screen *con); 230 | void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num); 231 | void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num); 232 | void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num); 233 | void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num); 234 | void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num); 235 | void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num); 236 | void tsm_screen_erase_cursor(struct tsm_screen *con); 237 | void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num); 238 | void tsm_screen_erase_cursor_to_end(struct tsm_screen *con, 239 | bool protect); 240 | void tsm_screen_erase_home_to_cursor(struct tsm_screen *con, 241 | bool protect); 242 | void tsm_screen_erase_current_line(struct tsm_screen *con, 243 | bool protect); 244 | void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con, 245 | bool protect); 246 | void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con, 247 | bool protect); 248 | void tsm_screen_erase_screen(struct tsm_screen *con, bool protect); 249 | 250 | void tsm_screen_selection_reset(struct tsm_screen *con); 251 | void tsm_screen_selection_start(struct tsm_screen *con, 252 | unsigned int posx, 253 | unsigned int posy); 254 | void tsm_screen_selection_target(struct tsm_screen *con, 255 | unsigned int posx, 256 | unsigned int posy); 257 | int tsm_screen_selection_copy(struct tsm_screen *con, char **out); 258 | 259 | tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, 260 | void *data); 261 | 262 | /** @} */ 263 | 264 | /** 265 | * @defgroup vte State Machine 266 | * Virtual terminal emulation with state machine 267 | * 268 | * A TSM VTE object provides the terminal state machine. It takes input from the 269 | * application (which usually comes from a TTY/PTY from a client), parses it, 270 | * modifies the attach screen or returns data which has to be written back to 271 | * the client. 272 | * 273 | * Furthermore, VTE objects accept keyboard or mouse input from the application 274 | * which is interpreted compliant to DEV-VTs. 275 | * 276 | * @{ 277 | */ 278 | 279 | /* virtual terminal emulator */ 280 | 281 | struct tsm_vte; 282 | 283 | /* keep in sync with shl_xkb_mods */ 284 | enum tsm_vte_modifier { 285 | TSM_SHIFT_MASK = (1 << 0), 286 | TSM_LOCK_MASK = (1 << 1), 287 | TSM_CONTROL_MASK = (1 << 2), 288 | TSM_ALT_MASK = (1 << 3), 289 | TSM_LOGO_MASK = (1 << 4), 290 | }; 291 | 292 | enum tsm_vte_color { 293 | TSM_COLOR_BLACK, 294 | TSM_COLOR_RED, 295 | TSM_COLOR_GREEN, 296 | TSM_COLOR_YELLOW, 297 | TSM_COLOR_BLUE, 298 | TSM_COLOR_MAGENTA, 299 | TSM_COLOR_CYAN, 300 | TSM_COLOR_LIGHT_GREY, 301 | TSM_COLOR_DARK_GREY, 302 | TSM_COLOR_LIGHT_RED, 303 | TSM_COLOR_LIGHT_GREEN, 304 | TSM_COLOR_LIGHT_YELLOW, 305 | TSM_COLOR_LIGHT_BLUE, 306 | TSM_COLOR_LIGHT_MAGENTA, 307 | TSM_COLOR_LIGHT_CYAN, 308 | TSM_COLOR_WHITE, 309 | TSM_COLOR_FOREGROUND, 310 | TSM_COLOR_BACKGROUND, 311 | TSM_COLOR_NUM 312 | }; 313 | 314 | /* keep in sync with TSM_INPUT_INVALID */ 315 | #define TSM_VTE_INVALID 0xffffffff 316 | 317 | typedef void (*tsm_vte_write_cb) (struct tsm_vte *vte, 318 | const char *u8, 319 | size_t len, 320 | void *data); 321 | 322 | int tsm_vte_new(struct tsm_vte **out, struct tsm_screen *con, 323 | tsm_vte_write_cb write_cb, void *data, 324 | tsm_log_t log, void *log_data); 325 | void tsm_vte_ref(struct tsm_vte *vte); 326 | void tsm_vte_unref(struct tsm_vte *vte); 327 | 328 | int tsm_vte_set_palette(struct tsm_vte *vte, const char *palette); 329 | int tsm_vte_set_palette_colors(struct tsm_vte *vte, const uint8_t (*colors)[3], int num_colors); 330 | void tsm_vte_get_def_attr(struct tsm_vte *vte, struct tsm_screen_attr *out); 331 | 332 | void tsm_vte_reset(struct tsm_vte *vte); 333 | void tsm_vte_hard_reset(struct tsm_vte *vte); 334 | void tsm_vte_input(struct tsm_vte *vte, const char *u8, size_t len); 335 | bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, 336 | uint32_t ascii, unsigned int mods, 337 | uint32_t unicode); 338 | 339 | /** @} */ 340 | 341 | #ifdef __cplusplus 342 | } 343 | #endif 344 | 345 | #endif /* TSM_LIBTSM_H */ 346 | -------------------------------------------------------------------------------- /libtsm/external/wcwidth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an implementation of wcwidth() and wcswidth() (defined in 3 | * IEEE Std 1002.1-2001) for Unicode. 4 | * 5 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html 6 | * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html 7 | * 8 | * In fixed-width output devices, Latin characters all occupy a single 9 | * "cell" position of equal width, whereas ideographic CJK characters 10 | * occupy two such cells. Interoperability between terminal-line 11 | * applications and (teletype-style) character terminals using the 12 | * UTF-8 encoding requires agreement on which character should advance 13 | * the cursor by how many cell positions. No established formal 14 | * standards exist at present on which Unicode character shall occupy 15 | * how many cell positions on character terminals. These routines are 16 | * a first attempt of defining such behavior based on simple rules 17 | * applied to data provided by the Unicode Consortium. 18 | * 19 | * For some graphical characters, the Unicode standard explicitly 20 | * defines a character-cell width via the definition of the East Asian 21 | * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. 22 | * In all these cases, there is no ambiguity about which width a 23 | * terminal shall use. For characters in the East Asian Ambiguous (A) 24 | * class, the width choice depends purely on a preference of backward 25 | * compatibility with either historic CJK or Western practice. 26 | * Choosing single-width for these characters is easy to justify as 27 | * the appropriate long-term solution, as the CJK practice of 28 | * displaying these characters as double-width comes from historic 29 | * implementation simplicity (8-bit encoded characters were displayed 30 | * single-width and 16-bit ones double-width, even for Greek, 31 | * Cyrillic, etc.) and not any typographic considerations. 32 | * 33 | * Much less clear is the choice of width for the Not East Asian 34 | * (Neutral) class. Existing practice does not dictate a width for any 35 | * of these characters. It would nevertheless make sense 36 | * typographically to allocate two character cells to characters such 37 | * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be 38 | * represented adequately with a single-width glyph. The following 39 | * routines at present merely assign a single-cell width to all 40 | * neutral characters, in the interest of simplicity. This is not 41 | * entirely satisfactory and should be reconsidered before 42 | * establishing a formal standard in this area. At the moment, the 43 | * decision which Not East Asian (Neutral) characters should be 44 | * represented by double-width glyphs cannot yet be answered by 45 | * applying a simple rule from the Unicode database content. Setting 46 | * up a proper standard for the behavior of UTF-8 character terminals 47 | * will require a careful analysis not only of each Unicode character, 48 | * but also of each presentation form, something the author of these 49 | * routines has avoided to do so far. 50 | * 51 | * http://www.unicode.org/unicode/reports/tr11/ 52 | * 53 | * Markus Kuhn -- 2007-05-26 (Unicode 5.0) 54 | * 55 | * Permission to use, copy, modify, and distribute this software 56 | * for any purpose and without fee is hereby granted. The author 57 | * disclaims all warranties with regard to this software. 58 | * 59 | * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c 60 | */ 61 | 62 | #include 63 | 64 | struct interval { 65 | int first; 66 | int last; 67 | }; 68 | 69 | /* auxiliary function for binary search in interval table */ 70 | static int bisearch(wchar_t ucs, const struct interval *table, int max) { 71 | int min = 0; 72 | int mid; 73 | 74 | if (ucs < table[0].first || ucs > table[max].last) 75 | return 0; 76 | while (max >= min) { 77 | mid = (min + max) / 2; 78 | if (ucs > table[mid].last) 79 | min = mid + 1; 80 | else if (ucs < table[mid].first) 81 | max = mid - 1; 82 | else 83 | return 1; 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | 90 | /* The following two functions define the column width of an ISO 10646 91 | * character as follows: 92 | * 93 | * - The null character (U+0000) has a column width of 0. 94 | * 95 | * - Other C0/C1 control characters and DEL will lead to a return 96 | * value of -1. 97 | * 98 | * - Non-spacing and enclosing combining characters (general 99 | * category code Mn or Me in the Unicode database) have a 100 | * column width of 0. 101 | * 102 | * - SOFT HYPHEN (U+00AD) has a column width of 1. 103 | * 104 | * - Other format characters (general category code Cf in the Unicode 105 | * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. 106 | * 107 | * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) 108 | * have a column width of 0. 109 | * 110 | * - Spacing characters in the East Asian Wide (W) or East Asian 111 | * Full-width (F) category as defined in Unicode Technical 112 | * Report #11 have a column width of 2. 113 | * 114 | * - All remaining characters (including all printable 115 | * ISO 8859-1 and WGL4 characters, Unicode control characters, 116 | * etc.) have a column width of 1. 117 | * 118 | * This implementation assumes that wchar_t characters are encoded 119 | * in ISO 10646. 120 | */ 121 | 122 | int mk_wcwidth(wchar_t ucs) 123 | { 124 | /* sorted list of non-overlapping intervals of non-spacing characters */ 125 | /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ 126 | static const struct interval combining[] = { 127 | { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, 128 | { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, 129 | { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, 130 | { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, 131 | { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, 132 | { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, 133 | { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, 134 | { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, 135 | { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, 136 | { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, 137 | { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, 138 | { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, 139 | { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, 140 | { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, 141 | { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, 142 | { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, 143 | { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, 144 | { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, 145 | { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, 146 | { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, 147 | { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, 148 | { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, 149 | { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, 150 | { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, 151 | { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, 152 | { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, 153 | { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, 154 | { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, 155 | { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, 156 | { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, 157 | { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, 158 | { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, 159 | { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, 160 | { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, 161 | { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, 162 | { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, 163 | { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, 164 | { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, 165 | { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, 166 | { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, 167 | { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, 168 | { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, 169 | { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, 170 | { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, 171 | { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, 172 | { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, 173 | { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, 174 | { 0xE0100, 0xE01EF } 175 | }; 176 | 177 | /* test for 8-bit control characters */ 178 | if (ucs == 0) 179 | return 0; 180 | if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) 181 | return -1; 182 | 183 | /* binary search in table of non-spacing characters */ 184 | if (bisearch(ucs, combining, 185 | sizeof(combining) / sizeof(struct interval) - 1)) 186 | return 0; 187 | 188 | /* if we arrive here, ucs is not a combining or C0/C1 control character */ 189 | 190 | return 1 + 191 | (ucs >= 0x1100 && 192 | (ucs <= 0x115f || /* Hangul Jamo init. consonants */ 193 | ucs == 0x2329 || ucs == 0x232a || 194 | (ucs >= 0x2e80 && ucs <= 0xa4cf && 195 | ucs != 0x303f) || /* CJK ... Yi */ 196 | (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ 197 | (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ 198 | (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ 199 | (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ 200 | (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ 201 | (ucs >= 0xffe0 && ucs <= 0xffe6) || 202 | (ucs >= 0x20000 && ucs <= 0x2fffd) || 203 | (ucs >= 0x30000 && ucs <= 0x3fffd))); 204 | } 205 | 206 | 207 | int mk_wcswidth(const wchar_t *pwcs, size_t n) 208 | { 209 | int w, width = 0; 210 | 211 | for (;*pwcs && n-- > 0; pwcs++) 212 | if ((w = mk_wcwidth(*pwcs)) < 0) 213 | return -1; 214 | else 215 | width += w; 216 | 217 | return width; 218 | } 219 | 220 | 221 | /* 222 | * The following functions are the same as mk_wcwidth() and 223 | * mk_wcswidth(), except that spacing characters in the East Asian 224 | * Ambiguous (A) category as defined in Unicode Technical Report #11 225 | * have a column width of 2. This variant might be useful for users of 226 | * CJK legacy encodings who want to migrate to UCS without changing 227 | * the traditional terminal character-width behaviour. It is not 228 | * otherwise recommended for general use. 229 | */ 230 | int mk_wcwidth_cjk(wchar_t ucs) 231 | { 232 | /* sorted list of non-overlapping intervals of East Asian Ambiguous 233 | * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ 234 | static const struct interval ambiguous[] = { 235 | { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, 236 | { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, 237 | { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, 238 | { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, 239 | { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, 240 | { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, 241 | { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, 242 | { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, 243 | { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, 244 | { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, 245 | { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, 246 | { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, 247 | { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, 248 | { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, 249 | { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, 250 | { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, 251 | { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, 252 | { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, 253 | { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, 254 | { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, 255 | { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, 256 | { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, 257 | { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, 258 | { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, 259 | { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, 260 | { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, 261 | { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, 262 | { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, 263 | { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, 264 | { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, 265 | { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, 266 | { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, 267 | { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, 268 | { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, 269 | { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, 270 | { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, 271 | { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, 272 | { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, 273 | { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, 274 | { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, 275 | { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, 276 | { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, 277 | { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, 278 | { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, 279 | { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, 280 | { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, 281 | { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, 282 | { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, 283 | { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, 284 | { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, 285 | { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, 286 | { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } 287 | }; 288 | 289 | /* binary search in table of non-spacing characters */ 290 | if (bisearch(ucs, ambiguous, 291 | sizeof(ambiguous) / sizeof(struct interval) - 1)) 292 | return 2; 293 | 294 | return mk_wcwidth(ucs); 295 | } 296 | 297 | 298 | int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) 299 | { 300 | int w, width = 0; 301 | 302 | for (;*pwcs && n-- > 0; pwcs++) 303 | if ((w = mk_wcwidth_cjk(*pwcs)) < 0) 304 | return -1; 305 | else 306 | width += w; 307 | 308 | return width; 309 | } 310 | --------------------------------------------------------------------------------