├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .gitmodules
├── CHANGELOG.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── cmake
└── GitVars.cmake
├── examples
├── CMakeLists.txt
├── emscripten0
│ ├── CMakeLists.txt
│ ├── index-tmpl.html
│ ├── main.cpp
│ └── style.css
├── hnterm
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── hn-state.cpp
│ ├── hn-state.h
│ ├── impl-emscripten.cpp
│ ├── impl-ncurses.cpp
│ ├── index-tmpl.html
│ ├── json.h
│ ├── main.cpp
│ └── style.css
├── imtui-common.h
├── imtui-demo.cpp
├── imtui-demo.h
├── ncurses0
│ ├── CMakeLists.txt
│ └── main.cpp
└── slack
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── index-tmpl.html
│ ├── logs.h
│ ├── main.cpp
│ └── style.css
├── include
└── imtui
│ ├── imtui-impl-emscripten.h
│ ├── imtui-impl-ncurses.h
│ ├── imtui-impl-text.h
│ └── imtui.h
├── src
├── CMakeLists.txt
├── imtui-impl-emscripten.cpp
├── imtui-impl-ncurses.cpp
└── imtui-impl-text.cpp
└── third-party
└── CMakeLists.txt
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push]
3 |
4 | jobs:
5 | ubuntu-20_04-gcc:
6 | runs-on: ubuntu-20.04
7 |
8 | strategy:
9 | matrix:
10 | build: [Debug, Release]
11 |
12 | steps:
13 | - name: Clone
14 | uses: actions/checkout@v1
15 | with:
16 | submodules: true
17 |
18 | - name: Dependencies
19 | run: sudo apt-get update && sudo apt-get install libncurses5-dev libcurl4-openssl-dev
20 |
21 | - name: Configure
22 | run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }}
23 |
24 | - name: Build
25 | run: make
26 |
27 | ubuntu-18_04-clang:
28 | runs-on: ubuntu-18.04
29 |
30 | strategy:
31 | matrix:
32 | build: [Debug, Release]
33 |
34 | steps:
35 | - name: Clone
36 | uses: actions/checkout@v1
37 | with:
38 | submodules: true
39 |
40 | - name: Dependencies
41 | run: sudo apt-get update && sudo apt-get install libncurses5-dev libcurl4-openssl-dev
42 |
43 | - name: Configure
44 | run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
45 |
46 | - name: Build
47 | run: make
48 |
49 | macOS-latest:
50 | runs-on: macOS-latest
51 |
52 | strategy:
53 | matrix:
54 | build: [Debug, Release]
55 |
56 | steps:
57 | - name: Clone
58 | uses: actions/checkout@v1
59 | with:
60 | submodules: true
61 |
62 | - name: Dependencies
63 | run: echo "tmp"
64 |
65 | - name: Configure
66 | run: cmake . -DCMAKE_BUILD_TYPE=${{ matrix.build }}
67 |
68 | - name: Build
69 | run: make
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | compile_commands.json
2 | build
3 | build-*
4 |
5 | .cache
6 | .clangd
7 | .vscode
8 | .cquery_cache
9 | *.code-workspace
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third-party/imgui/imgui"]
2 | path = third-party/imgui/imgui
3 | url = https://github.com/ggerganov/imgui
4 | branch = imtui
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [Unreleased]
4 |
5 | - "Columns & Tables" demo window (0986676)
6 |
7 | ## [1.0.4] - 2021-04-03
8 |
9 | - Update Dear ImGui to v1.81 ([#24](https://github.com/ggerganov/imtui/pull/24))
10 | - Fix CMake install targets
11 |
12 | ## [1.0.3] - 2021-01-09
13 |
14 | - Update Dear ImGui to v1.79
15 | - Add Windows support with MSYS2 + PDCurses (39f34ea)
16 |
17 | ## [1.0.0] - 2020-12-15
18 |
19 | - Initial release with Dear ImGui v1.77 backend
20 |
21 | [unreleased]: https://github.com/ggerganov/imtui/compare/v1.0.4...HEAD
22 | [1.0.4]: https://github.com/ggerganov/imtui/releases/tag/v1.0.4
23 | [1.0.3]: https://github.com/ggerganov/imtui/releases/tag/v1.0.3
24 | [1.0.0]: https://github.com/ggerganov/imtui/releases/tag/v1.0.0
25 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.10)
2 | project(imtui VERSION 1.0.3)
3 |
4 | set(CMAKE_EXPORT_COMPILE_COMMANDS "on")
5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
6 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
7 |
8 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
9 | set(IMTUI_STANDALONE ON)
10 | include(cmake/GitVars.cmake)
11 | include(GNUInstallDirs)
12 | else()
13 | set(IMTUI_STANDALONE OFF)
14 | endif()
15 |
16 | if (EMSCRIPTEN)
17 | set(IMTUI_SUPPORT_NCURSES_DEFAULT OFF)
18 | set(IMTUI_SUPPORT_CURL_DEFAULT OFF)
19 | set(IMTUI_LIBRARY_TYPE STATIC)
20 | else()
21 | set(IMTUI_SUPPORT_NCURSES_DEFAULT ON)
22 | set(IMTUI_SUPPORT_CURL_DEFAULT ON)
23 | if (BUILD_SHARED_LIBS)
24 | set(IMTUI_LIBRARY_TYPE SHARED)
25 | else ()
26 | set(IMTUI_LIBRARY_TYPE STATIC)
27 | endif()
28 | endif()
29 |
30 | # options
31 |
32 | option(IMTUI_ALL_WARNINGS "imtui: enable all compiler warnings" ${IMTUI_STANDALONE})
33 | option(IMTUI_ALL_WARNINGS_3RD_PARTY "imtui: enable all compiler warnings in 3rd party libs" OFF)
34 |
35 | option(IMTUI_SANITIZE_THREAD "imtui: enable thread sanitizer" OFF)
36 | option(IMTUI_SANITIZE_ADDRESS "imtui: enable address sanitizer" OFF)
37 | option(IMTUI_SANITIZE_UNDEFINED "imtui: enable undefined sanitizer" OFF)
38 |
39 | option(IMTUI_SUPPORT_NCURSES "imtui: support for libncurses" ${IMTUI_SUPPORT_NCURSES_DEFAULT})
40 | option(IMTUI_SUPPORT_CURL "imtui: support for libcurl" ${IMTUI_SUPPORT_CURL_DEFAULT})
41 |
42 | option(IMTUI_BUILD_EXAMPLES "imtui: build examples" ${IMTUI_STANDALONE})
43 |
44 | # sanitizers
45 |
46 | if (IMTUI_SANITIZE_THREAD)
47 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
49 | endif()
50 |
51 | if (IMTUI_SANITIZE_ADDRESS)
52 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
53 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
54 | endif()
55 |
56 | if (IMTUI_SANITIZE_UNDEFINED)
57 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
59 | endif()
60 |
61 | # dependencies
62 |
63 | if (IMTUI_SUPPORT_NCURSES)
64 | if (MINGW)
65 | set(CURSES_LIBRARIES pdcurses)
66 | else()
67 | find_package(Curses REQUIRED)
68 | CHECK_LIBRARY_EXISTS("${CURSES_NCURSES_LIBRARY}"
69 | nodelay "" CURSES_NCURSES_HAS_NODELAY)
70 | if(NOT CURSES_NCURSES_HAS_NODELAY)
71 | find_library(CURSES_EXTRA_LIBRARY tinfo)
72 | CHECK_LIBRARY_EXISTS("${CURSES_EXTRA_LIBRARY}"
73 | nodelay "" CURSES_TINFO_HAS_NODELAY)
74 | endif()
75 | if(CURSES_EXTRA_LIBRARY)
76 | set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_EXTRA_LIBRARY})
77 | endif()
78 | endif()
79 | endif()
80 |
81 | # main
82 |
83 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
84 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
85 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo")
86 | endif ()
87 |
88 | add_subdirectory(third-party)
89 |
90 | if (IMTUI_ALL_WARNINGS)
91 | if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
92 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
93 | else()
94 | # todo : windows
95 | endif()
96 | endif()
97 |
98 | add_subdirectory(src)
99 |
100 | if (IMTUI_STANDALONE AND IMTUI_BUILD_EXAMPLES)
101 | add_subdirectory(examples)
102 | endif()
103 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Georgi Gerganov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | imtui
2 | =====
3 | [](https://github.com/ggerganov/imtui/actions)
4 | [![ImTui v1.0.4 badge][changelog-badge]][changelog]
5 | [![Dear ImGui version badge][imgui-version-badge]](https://github.com/ocornut/imgui)
6 |
7 | ImTui is an immediate mode text-based user interface library. Supports 256 ANSI colors and mouse/keyboard input.
8 |
9 |
7 |
8 | ImTui::TScreen * g_screen = nullptr;
9 |
10 | extern "C" {
11 | EMSCRIPTEN_KEEPALIVE
12 | void render_frame() {
13 | ImTui_ImplText_NewFrame();
14 | ImTui_ImplEmscripten_NewFrame();
15 |
16 | ImGui::NewFrame();
17 |
18 | ImGui::SetNextWindowPos(ImVec2(8, 28), ImGuiCond_Once);
19 | ImGui::SetNextWindowSize(ImVec2(50.0, 10.0), ImGuiCond_Once);
20 | ImGui::Begin("Hello, world!");
21 | ImGui::Text("mx = %g, my = %g", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y);
22 | ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
23 | ImGui::End();
24 |
25 | bool showDemoWindow = true;
26 | ImTui::ShowDemoWindow(&showDemoWindow);
27 |
28 | ImGui::Render();
29 |
30 | ImTui_ImplText_RenderDrawData(ImGui::GetDrawData(), g_screen);
31 | }
32 | }
33 |
34 | int main() {
35 | IMGUI_CHECKVERSION();
36 | ImGui::CreateContext();
37 |
38 | ImTui_ImplText_Init();
39 | g_screen = ImTui_ImplEmscripten_Init(true);
40 |
41 | return 0;
42 | }
43 |
--------------------------------------------------------------------------------
/examples/emscripten0/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0; background-color: black;
3 | -webkit-font-smoothing: subpixel-antialiased;
4 | font-smoothing: subpixel-antialiased;
5 | }
6 | #screen {
7 | margin: 0;
8 | padding: 0;
9 | font-size: 13px;
10 | height: 100%;
11 | font: sans-serif;
12 | }
13 | .no-sel {
14 | outline: none;
15 | -moz-user-select: none;
16 | -webkit-user-select: none;
17 | -webkit-touch-callout: none;
18 | -ms-user-select:none;
19 | user-select:none;
20 | -o-user-select:none;
21 | }
22 | .cell {
23 | pointer-events: none;
24 | }
25 | .cell-version {
26 | padding-left: 4px;
27 | padding-top: 0.5em;
28 | text-align: left;
29 | display: inline-block;
30 | float: left;
31 | color: rgba(255, 255, 255, 0.75);
32 | }
33 | .cell-about {
34 | padding-right: 24px;
35 | padding-top: 0.5em;
36 | text-align: right;
37 | display: inline-block;
38 | float: right;
39 | }
40 | .nav-link {
41 | text-decoration: none;
42 | color: rgba(255, 255, 255, 1.0);
43 | }
44 | svg {
45 | -webkit-filter: invert(100%); /* safari 6.0 - 9.0 */
46 | filter: invert(100%);
47 | }
48 |
49 | .f0 {color:#000000}
50 | .f1 {color:#800000}
51 | .f2 {color:#008000}
52 | .f3 {color:#808000}
53 | .f4 {color:#000080}
54 | .f5 {color:#800080}
55 | .f6 {color:#008080}
56 | .f7 {color:#c0c0c0}
57 | .f8 {color:#808080}
58 | .f9 {color:#ff0000}
59 | .f10 {color:#00ff00}
60 | .f11 {color:#ffff00}
61 | .f12 {color:#0000ff}
62 | .f13 {color:#ff00ff}
63 | .f14 {color:#00ffff}
64 | .f15 {color:#ffffff}
65 | .f16 {color:#000000}
66 | .f17 {color:#00005f}
67 | .f18 {color:#000087}
68 | .f19 {color:#0000af}
69 | .f20 {color:#0000d7}
70 | .f21 {color:#0000ff}
71 | .f22 {color:#005f00}
72 | .f23 {color:#005f5f}
73 | .f24 {color:#005f87}
74 | .f25 {color:#005faf}
75 | .f26 {color:#005fd7}
76 | .f27 {color:#005fff}
77 | .f28 {color:#008700}
78 | .f29 {color:#00875f}
79 | .f30 {color:#008787}
80 | .f31 {color:#0087af}
81 | .f32 {color:#0087d7}
82 | .f33 {color:#0087ff}
83 | .f34 {color:#00af00}
84 | .f35 {color:#00af5f}
85 | .f36 {color:#00af87}
86 | .f37 {color:#00afaf}
87 | .f38 {color:#00afd7}
88 | .f39 {color:#00afff}
89 | .f40 {color:#00d700}
90 | .f41 {color:#00d75f}
91 | .f42 {color:#00d787}
92 | .f43 {color:#00d7af}
93 | .f44 {color:#00d7d7}
94 | .f45 {color:#00d7ff}
95 | .f46 {color:#00ff00}
96 | .f47 {color:#00ff5f}
97 | .f48 {color:#00ff87}
98 | .f49 {color:#00ffaf}
99 | .f50 {color:#00ffd7}
100 | .f51 {color:#00ffff}
101 | .f52 {color:#5f0000}
102 | .f53 {color:#5f005f}
103 | .f54 {color:#5f0087}
104 | .f55 {color:#5f00af}
105 | .f56 {color:#5f00d7}
106 | .f57 {color:#5f00ff}
107 | .f58 {color:#5f5f00}
108 | .f59 {color:#5f5f5f}
109 | .f60 {color:#5f5f87}
110 | .f61 {color:#5f5faf}
111 | .f62 {color:#5f5fd7}
112 | .f63 {color:#5f5fff}
113 | .f64 {color:#5f8700}
114 | .f65 {color:#5f875f}
115 | .f66 {color:#5f8787}
116 | .f67 {color:#5f87af}
117 | .f68 {color:#5f87d7}
118 | .f69 {color:#5f87ff}
119 | .f70 {color:#5faf00}
120 | .f71 {color:#5faf5f}
121 | .f72 {color:#5faf87}
122 | .f73 {color:#5fafaf}
123 | .f74 {color:#5fafd7}
124 | .f75 {color:#5fafff}
125 | .f76 {color:#5fd700}
126 | .f77 {color:#5fd75f}
127 | .f78 {color:#5fd787}
128 | .f79 {color:#5fd7af}
129 | .f80 {color:#5fd7d7}
130 | .f81 {color:#5fd7ff}
131 | .f82 {color:#5fff00}
132 | .f83 {color:#5fff5f}
133 | .f84 {color:#5fff87}
134 | .f85 {color:#5fffaf}
135 | .f86 {color:#5fffd7}
136 | .f87 {color:#5fffff}
137 | .f88 {color:#870000}
138 | .f89 {color:#87005f}
139 | .f90 {color:#870087}
140 | .f91 {color:#8700af}
141 | .f92 {color:#8700d7}
142 | .f93 {color:#8700ff}
143 | .f94 {color:#875f00}
144 | .f95 {color:#875f5f}
145 | .f96 {color:#875f87}
146 | .f97 {color:#875faf}
147 | .f98 {color:#875fd7}
148 | .f99 {color:#875fff}
149 | .f100 {color:#878700}
150 | .f101 {color:#87875f}
151 | .f102 {color:#878787}
152 | .f103 {color:#8787af}
153 | .f104 {color:#8787d7}
154 | .f105 {color:#8787ff}
155 | .f106 {color:#87af00}
156 | .f107 {color:#87af5f}
157 | .f108 {color:#87af87}
158 | .f109 {color:#87afaf}
159 | .f110 {color:#87afd7}
160 | .f111 {color:#87afff}
161 | .f112 {color:#87d700}
162 | .f113 {color:#87d75f}
163 | .f114 {color:#87d787}
164 | .f115 {color:#87d7af}
165 | .f116 {color:#87d7d7}
166 | .f117 {color:#87d7ff}
167 | .f118 {color:#87ff00}
168 | .f119 {color:#87ff5f}
169 | .f120 {color:#87ff87}
170 | .f121 {color:#87ffaf}
171 | .f122 {color:#87ffd7}
172 | .f123 {color:#87ffff}
173 | .f124 {color:#af0000}
174 | .f125 {color:#af005f}
175 | .f126 {color:#af0087}
176 | .f127 {color:#af00af}
177 | .f128 {color:#af00d7}
178 | .f129 {color:#af00ff}
179 | .f130 {color:#af5f00}
180 | .f131 {color:#af5f5f}
181 | .f132 {color:#af5f87}
182 | .f133 {color:#af5faf}
183 | .f134 {color:#af5fd7}
184 | .f135 {color:#af5fff}
185 | .f136 {color:#af8700}
186 | .f137 {color:#af875f}
187 | .f138 {color:#af8787}
188 | .f139 {color:#af87af}
189 | .f140 {color:#af87d7}
190 | .f141 {color:#af87ff}
191 | .f142 {color:#afaf00}
192 | .f143 {color:#afaf5f}
193 | .f144 {color:#afaf87}
194 | .f145 {color:#afafaf}
195 | .f146 {color:#afafd7}
196 | .f147 {color:#afafff}
197 | .f148 {color:#afd700}
198 | .f149 {color:#afd75f}
199 | .f150 {color:#afd787}
200 | .f151 {color:#afd7af}
201 | .f152 {color:#afd7d7}
202 | .f153 {color:#afd7ff}
203 | .f154 {color:#afff00}
204 | .f155 {color:#afff5f}
205 | .f156 {color:#afff87}
206 | .f157 {color:#afffaf}
207 | .f158 {color:#afffd7}
208 | .f159 {color:#afffff}
209 | .f160 {color:#d70000}
210 | .f161 {color:#d7005f}
211 | .f162 {color:#d70087}
212 | .f163 {color:#d700af}
213 | .f164 {color:#d700d7}
214 | .f165 {color:#d700ff}
215 | .f166 {color:#d75f00}
216 | .f167 {color:#d75f5f}
217 | .f168 {color:#d75f87}
218 | .f169 {color:#d75faf}
219 | .f170 {color:#d75fd7}
220 | .f171 {color:#d75fff}
221 | .f172 {color:#d78700}
222 | .f173 {color:#d7875f}
223 | .f174 {color:#d78787}
224 | .f175 {color:#d787af}
225 | .f176 {color:#d787d7}
226 | .f177 {color:#d787ff}
227 | .f178 {color:#d7af00}
228 | .f179 {color:#d7af5f}
229 | .f180 {color:#d7af87}
230 | .f181 {color:#d7afaf}
231 | .f182 {color:#d7afd7}
232 | .f183 {color:#d7afff}
233 | .f184 {color:#d7d700}
234 | .f185 {color:#d7d75f}
235 | .f186 {color:#d7d787}
236 | .f187 {color:#d7d7af}
237 | .f188 {color:#d7d7d7}
238 | .f189 {color:#d7d7ff}
239 | .f190 {color:#d7ff00}
240 | .f191 {color:#d7ff5f}
241 | .f192 {color:#d7ff87}
242 | .f193 {color:#d7ffaf}
243 | .f194 {color:#d7ffd7}
244 | .f195 {color:#d7ffff}
245 | .f196 {color:#ff0000}
246 | .f197 {color:#ff005f}
247 | .f198 {color:#ff0087}
248 | .f199 {color:#ff00af}
249 | .f200 {color:#ff00d7}
250 | .f201 {color:#ff00ff}
251 | .f202 {color:#ff5f00}
252 | .f203 {color:#ff5f5f}
253 | .f204 {color:#ff5f87}
254 | .f205 {color:#ff5faf}
255 | .f206 {color:#ff5fd7}
256 | .f207 {color:#ff5fff}
257 | .f208 {color:#ff8700}
258 | .f209 {color:#ff875f}
259 | .f210 {color:#ff8787}
260 | .f211 {color:#ff87af}
261 | .f212 {color:#ff87d7}
262 | .f213 {color:#ff87ff}
263 | .f214 {color:#ffaf00}
264 | .f215 {color:#ffaf5f}
265 | .f216 {color:#ffaf87}
266 | .f217 {color:#ffafaf}
267 | .f218 {color:#ffafd7}
268 | .f219 {color:#ffafff}
269 | .f220 {color:#ffd700}
270 | .f221 {color:#ffd75f}
271 | .f222 {color:#ffd787}
272 | .f223 {color:#ffd7af}
273 | .f224 {color:#ffd7d7}
274 | .f225 {color:#ffd7ff}
275 | .f226 {color:#ffff00}
276 | .f227 {color:#ffff5f}
277 | .f228 {color:#ffff87}
278 | .f229 {color:#ffffaf}
279 | .f230 {color:#ffffd7}
280 | .f231 {color:#ffffff}
281 | .f232 {color:#080808}
282 | .f233 {color:#121212}
283 | .f234 {color:#1c1c1c}
284 | .f235 {color:#262626}
285 | .f236 {color:#303030}
286 | .f237 {color:#3a3a3a}
287 | .f238 {color:#444444}
288 | .f239 {color:#4e4e4e}
289 | .f240 {color:#585858}
290 | .f241 {color:#626262}
291 | .f242 {color:#6c6c6c}
292 | .f243 {color:#767676}
293 | .f244 {color:#808080}
294 | .f245 {color:#8a8a8a}
295 | .f246 {color:#949494}
296 | .f247 {color:#9e9e9e}
297 | .f248 {color:#a8a8a8}
298 | .f249 {color:#b2b2b2}
299 | .f250 {color:#bcbcbc}
300 | .f251 {color:#c6c6c6}
301 | .f252 {color:#d0d0d0}
302 | .f253 {color:#dadada}
303 | .f254 {color:#e4e4e4}
304 | .f255 {color:#eeeeee}
305 | .b1 {background-color:#800000}
306 | .b2 {background-color:#008000}
307 | .b3 {background-color:#808000}
308 | .b4 {background-color:#000080}
309 | .b5 {background-color:#800080}
310 | .b6 {background-color:#008080}
311 | .b7 {background-color:#c0c0c0}
312 | .b8 {background-color:#808080}
313 | .b9 {background-color:#ff0000}
314 | .b10 {background-color:#00ff00}
315 | .b11 {background-color:#ffff00}
316 | .b12 {background-color:#0000ff}
317 | .b13 {background-color:#ff00ff}
318 | .b14 {background-color:#00ffff}
319 | .b15 {background-color:#ffffff}
320 | .b16 {background-color:#000000}
321 | .b17 {background-color:#00005f}
322 | .b18 {background-color:#000087}
323 | .b19 {background-color:#0000af}
324 | .b20 {background-color:#0000d7}
325 | .b21 {background-color:#0000ff}
326 | .b22 {background-color:#005f00}
327 | .b23 {background-color:#005f5f}
328 | .b24 {background-color:#005f87}
329 | .b25 {background-color:#005faf}
330 | .b26 {background-color:#005fd7}
331 | .b27 {background-color:#005fff}
332 | .b28 {background-color:#008700}
333 | .b29 {background-color:#00875f}
334 | .b30 {background-color:#008787}
335 | .b31 {background-color:#0087af}
336 | .b32 {background-color:#0087d7}
337 | .b33 {background-color:#0087ff}
338 | .b34 {background-color:#00af00}
339 | .b35 {background-color:#00af5f}
340 | .b36 {background-color:#00af87}
341 | .b37 {background-color:#00afaf}
342 | .b38 {background-color:#00afd7}
343 | .b39 {background-color:#00afff}
344 | .b40 {background-color:#00d700}
345 | .b41 {background-color:#00d75f}
346 | .b42 {background-color:#00d787}
347 | .b43 {background-color:#00d7af}
348 | .b44 {background-color:#00d7d7}
349 | .b45 {background-color:#00d7ff}
350 | .b46 {background-color:#00ff00}
351 | .b47 {background-color:#00ff5f}
352 | .b48 {background-color:#00ff87}
353 | .b49 {background-color:#00ffaf}
354 | .b50 {background-color:#00ffd7}
355 | .b51 {background-color:#00ffff}
356 | .b52 {background-color:#5f0000}
357 | .b53 {background-color:#5f005f}
358 | .b54 {background-color:#5f0087}
359 | .b55 {background-color:#5f00af}
360 | .b56 {background-color:#5f00d7}
361 | .b57 {background-color:#5f00ff}
362 | .b58 {background-color:#5f5f00}
363 | .b59 {background-color:#5f5f5f}
364 | .b60 {background-color:#5f5f87}
365 | .b61 {background-color:#5f5faf}
366 | .b62 {background-color:#5f5fd7}
367 | .b63 {background-color:#5f5fff}
368 | .b64 {background-color:#5f8700}
369 | .b65 {background-color:#5f875f}
370 | .b66 {background-color:#5f8787}
371 | .b67 {background-color:#5f87af}
372 | .b68 {background-color:#5f87d7}
373 | .b69 {background-color:#5f87ff}
374 | .b70 {background-color:#5faf00}
375 | .b71 {background-color:#5faf5f}
376 | .b72 {background-color:#5faf87}
377 | .b73 {background-color:#5fafaf}
378 | .b74 {background-color:#5fafd7}
379 | .b75 {background-color:#5fafff}
380 | .b76 {background-color:#5fd700}
381 | .b77 {background-color:#5fd75f}
382 | .b78 {background-color:#5fd787}
383 | .b79 {background-color:#5fd7af}
384 | .b80 {background-color:#5fd7d7}
385 | .b81 {background-color:#5fd7ff}
386 | .b82 {background-color:#5fff00}
387 | .b83 {background-color:#5fff5f}
388 | .b84 {background-color:#5fff87}
389 | .b85 {background-color:#5fffaf}
390 | .b86 {background-color:#5fffd7}
391 | .b87 {background-color:#5fffff}
392 | .b88 {background-color:#870000}
393 | .b89 {background-color:#87005f}
394 | .b90 {background-color:#870087}
395 | .b91 {background-color:#8700af}
396 | .b92 {background-color:#8700d7}
397 | .b93 {background-color:#8700ff}
398 | .b94 {background-color:#875f00}
399 | .b95 {background-color:#875f5f}
400 | .b96 {background-color:#875f87}
401 | .b97 {background-color:#875faf}
402 | .b98 {background-color:#875fd7}
403 | .b99 {background-color:#875fff}
404 | .b100 {background-color:#878700}
405 | .b101 {background-color:#87875f}
406 | .b102 {background-color:#878787}
407 | .b103 {background-color:#8787af}
408 | .b104 {background-color:#8787d7}
409 | .b105 {background-color:#8787ff}
410 | .b106 {background-color:#87af00}
411 | .b107 {background-color:#87af5f}
412 | .b108 {background-color:#87af87}
413 | .b109 {background-color:#87afaf}
414 | .b110 {background-color:#87afd7}
415 | .b111 {background-color:#87afff}
416 | .b112 {background-color:#87d700}
417 | .b113 {background-color:#87d75f}
418 | .b114 {background-color:#87d787}
419 | .b115 {background-color:#87d7af}
420 | .b116 {background-color:#87d7d7}
421 | .b117 {background-color:#87d7ff}
422 | .b118 {background-color:#87ff00}
423 | .b119 {background-color:#87ff5f}
424 | .b120 {background-color:#87ff87}
425 | .b121 {background-color:#87ffaf}
426 | .b122 {background-color:#87ffd7}
427 | .b123 {background-color:#87ffff}
428 | .b124 {background-color:#af0000}
429 | .b125 {background-color:#af005f}
430 | .b126 {background-color:#af0087}
431 | .b127 {background-color:#af00af}
432 | .b128 {background-color:#af00d7}
433 | .b129 {background-color:#af00ff}
434 | .b130 {background-color:#af5f00}
435 | .b131 {background-color:#af5f5f}
436 | .b132 {background-color:#af5f87}
437 | .b133 {background-color:#af5faf}
438 | .b134 {background-color:#af5fd7}
439 | .b135 {background-color:#af5fff}
440 | .b136 {background-color:#af8700}
441 | .b137 {background-color:#af875f}
442 | .b138 {background-color:#af8787}
443 | .b139 {background-color:#af87af}
444 | .b140 {background-color:#af87d7}
445 | .b141 {background-color:#af87ff}
446 | .b142 {background-color:#afaf00}
447 | .b143 {background-color:#afaf5f}
448 | .b144 {background-color:#afaf87}
449 | .b145 {background-color:#afafaf}
450 | .b146 {background-color:#afafd7}
451 | .b147 {background-color:#afafff}
452 | .b148 {background-color:#afd700}
453 | .b149 {background-color:#afd75f}
454 | .b150 {background-color:#afd787}
455 | .b151 {background-color:#afd7af}
456 | .b152 {background-color:#afd7d7}
457 | .b153 {background-color:#afd7ff}
458 | .b154 {background-color:#afff00}
459 | .b155 {background-color:#afff5f}
460 | .b156 {background-color:#afff87}
461 | .b157 {background-color:#afffaf}
462 | .b158 {background-color:#afffd7}
463 | .b159 {background-color:#afffff}
464 | .b160 {background-color:#d70000}
465 | .b161 {background-color:#d7005f}
466 | .b162 {background-color:#d70087}
467 | .b163 {background-color:#d700af}
468 | .b164 {background-color:#d700d7}
469 | .b165 {background-color:#d700ff}
470 | .b166 {background-color:#d75f00}
471 | .b167 {background-color:#d75f5f}
472 | .b168 {background-color:#d75f87}
473 | .b169 {background-color:#d75faf}
474 | .b170 {background-color:#d75fd7}
475 | .b171 {background-color:#d75fff}
476 | .b172 {background-color:#d78700}
477 | .b173 {background-color:#d7875f}
478 | .b174 {background-color:#d78787}
479 | .b175 {background-color:#d787af}
480 | .b176 {background-color:#d787d7}
481 | .b177 {background-color:#d787ff}
482 | .b178 {background-color:#d7af00}
483 | .b179 {background-color:#d7af5f}
484 | .b180 {background-color:#d7af87}
485 | .b181 {background-color:#d7afaf}
486 | .b182 {background-color:#d7afd7}
487 | .b183 {background-color:#d7afff}
488 | .b184 {background-color:#d7d700}
489 | .b185 {background-color:#d7d75f}
490 | .b186 {background-color:#d7d787}
491 | .b187 {background-color:#d7d7af}
492 | .b188 {background-color:#d7d7d7}
493 | .b189 {background-color:#d7d7ff}
494 | .b190 {background-color:#d7ff00}
495 | .b191 {background-color:#d7ff5f}
496 | .b192 {background-color:#d7ff87}
497 | .b193 {background-color:#d7ffaf}
498 | .b194 {background-color:#d7ffd7}
499 | .b195 {background-color:#d7ffff}
500 | .b196 {background-color:#ff0000}
501 | .b197 {background-color:#ff005f}
502 | .b198 {background-color:#ff0087}
503 | .b199 {background-color:#ff00af}
504 | .b200 {background-color:#ff00d7}
505 | .b201 {background-color:#ff00ff}
506 | .b202 {background-color:#ff5f00}
507 | .b203 {background-color:#ff5f5f}
508 | .b204 {background-color:#ff5f87}
509 | .b205 {background-color:#ff5faf}
510 | .b206 {background-color:#ff5fd7}
511 | .b207 {background-color:#ff5fff}
512 | .b208 {background-color:#ff8700}
513 | .b209 {background-color:#ff875f}
514 | .b210 {background-color:#ff8787}
515 | .b211 {background-color:#ff87af}
516 | .b212 {background-color:#ff87d7}
517 | .b213 {background-color:#ff87ff}
518 | .b214 {background-color:#ffaf00}
519 | .b215 {background-color:#ffaf5f}
520 | .b216 {background-color:#ffaf87}
521 | .b217 {background-color:#ffafaf}
522 | .b218 {background-color:#ffafd7}
523 | .b219 {background-color:#ffafff}
524 | .b220 {background-color:#ffd700}
525 | .b221 {background-color:#ffd75f}
526 | .b222 {background-color:#ffd787}
527 | .b223 {background-color:#ffd7af}
528 | .b224 {background-color:#ffd7d7}
529 | .b225 {background-color:#ffd7ff}
530 | .b226 {background-color:#ffff00}
531 | .b227 {background-color:#ffff5f}
532 | .b228 {background-color:#ffff87}
533 | .b229 {background-color:#ffffaf}
534 | .b230 {background-color:#ffffd7}
535 | .b231 {background-color:#ffffff}
536 | .b232 {background-color:#080808}
537 | .b233 {background-color:#121212}
538 | .b234 {background-color:#1c1c1c}
539 | .b235 {background-color:#262626}
540 | .b236 {background-color:#303030}
541 | .b237 {background-color:#3a3a3a}
542 | .b238 {background-color:#444444}
543 | .b239 {background-color:#4e4e4e}
544 | .b240 {background-color:#585858}
545 | .b241 {background-color:#626262}
546 | .b242 {background-color:#6c6c6c}
547 | .b243 {background-color:#767676}
548 | .b244 {background-color:#808080}
549 | .b245 {background-color:#8a8a8a}
550 | .b246 {background-color:#949494}
551 | .b247 {background-color:#9e9e9e}
552 | .b248 {background-color:#a8a8a8}
553 | .b249 {background-color:#b2b2b2}
554 | .b250 {background-color:#bcbcbc}
555 | .b251 {background-color:#c6c6c6}
556 | .b252 {background-color:#d0d0d0}
557 | .b253 {background-color:#dadada}
558 | .b254 {background-color:#e4e4e4}
559 | .b255 {background-color:#eeeeee}
560 |
561 |
--------------------------------------------------------------------------------
/examples/hnterm/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(CMAKE_CXX_STANDARD 17)
2 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
3 |
4 | set(TARGET hnterm)
5 |
6 | option(HNTERM_ENABLE_API_CACHE "hnterm: enable API caching" OFF)
7 |
8 | if (EMSCRIPTEN)
9 | set (CMAKE_CXX_FLAGS "-s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
10 |
11 | add_executable(${TARGET}
12 | main.cpp
13 | hn-state.cpp
14 | impl-emscripten.cpp
15 | )
16 |
17 | target_include_directories(${TARGET} PRIVATE
18 | ..
19 | )
20 |
21 | target_link_libraries(${TARGET} PRIVATE
22 | imtui
23 | imtui-examples-common
24 | imtui-emscripten
25 | )
26 |
27 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
28 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/style.css @ONLY)
29 | else()
30 | add_executable(${TARGET}
31 | main.cpp
32 | hn-state.cpp
33 | impl-ncurses.cpp
34 | )
35 |
36 | target_include_directories(${TARGET} PRIVATE
37 | ..
38 | ${CURL_INCLUDE_DIR}
39 | )
40 |
41 | target_link_libraries(${TARGET} PRIVATE
42 | imtui-ncurses
43 | imtui-examples-common
44 | ${CURL_LIBRARIES}
45 | )
46 | endif()
47 |
48 | if (HNTERM_ENABLE_API_CACHE)
49 | target_compile_definitions(${TARGET} PRIVATE ENABLE_API_CACHE)
50 | endif()
51 |
--------------------------------------------------------------------------------
/examples/hnterm/README.md:
--------------------------------------------------------------------------------
1 | *Note: this example has been moved in a standalone repository - https://github.com/ggerganov/hnterm*
2 |
3 | *Future development will continue there*
4 |
5 | # hnterm
6 |
7 | Interactive browsing of [Hacker News](https://news.ycombinator.com/news) in the terminal
8 |
9 | [](https://asciinema.org/a/gJakwNnEcgmGzZYiYIzFA1n8R)
10 |
11 | 
12 |
13 | ## Live demo in the browser
14 |
15 | The Emscripten port of HNTerm uses Emscripten's Fetch API instead of `libcurl` to perform requests to the [HN API](https://github.com/HackerNews/API).
16 |
17 | Demo: [hnterm.ggerganov.com](https://hnterm.ggerganov.com/) *(not suitable for mobile devices)*
18 |
19 | ## Details
20 |
21 | HNTerm is a small console application written in C++ for browsing [Hacker News](https://news.ycombinator.com/news). It queries the official [HN API](https://github.com/HackerNews/API) and interactively displays the current stories and comments. It uses `libcurl` to perform the GET requests to the API. The UI is rendered with [ImTui](https://github.com/ggerganov/imtui). HNTerm fetches only the content that is currently visible on the screen. The window splits allow browsing multiple stories/comment sections at the same time.
22 |
23 | ## Building
24 |
25 | ### Linux and Mac:
26 |
27 | ```bash
28 | git clone https://github.com/ggerganov/imtui --recursive
29 | cd imtui
30 | mkdir build && cd build
31 | cmake ..
32 | make
33 |
34 | ./bin/hnterm
35 | ```
36 |
--------------------------------------------------------------------------------
/examples/hnterm/hn-state.cpp:
--------------------------------------------------------------------------------
1 | /*! \file hn-state.cpp
2 | * \brief HN::State
3 | */
4 |
5 | #include "hn-state.h"
6 |
7 | #include "json.h"
8 |
9 | extern void requestJSONForURI_impl(std::string uri);
10 | extern std::string getJSONForURI_impl(const std::string & uri);
11 | extern uint64_t getTotalBytesDownloaded();
12 | extern int getNFetches();
13 | extern void updateRequests_impl();
14 | extern uint64_t t_s();
15 |
16 | namespace {
17 |
18 | void replaceAll(std::string & s, const std::string & search, const std::string & replace) {
19 | for (size_t pos = 0; ; pos += replace.length()) {
20 | pos = s.find(search, pos);
21 | if (pos == std::string::npos) break;
22 | s.erase(pos, search.length());
23 | s.insert(pos, replace);
24 | }
25 | }
26 |
27 | std::string getJSONForURI(const std::string & uri) {
28 | auto responseString = getJSONForURI_impl(uri);
29 | if (responseString == "") return "";
30 |
31 | return responseString;
32 | }
33 |
34 | }
35 |
36 | namespace HN {
37 |
38 | // todo : optimize this
39 | std::string parseHTML(std::string str) {
40 | ::replaceAll(str, "", "\n");
41 |
42 | std::string res;
43 | bool inTag = false;
44 | for (auto & ch : str) {
45 | if (ch == '<') {
46 | inTag = true;
47 | } else if (ch == '>') {
48 | inTag = false;
49 | } else {
50 | if (inTag == false) {
51 | res += ch;
52 | }
53 | }
54 | }
55 |
56 | ::replaceAll(res, "/", "/");
57 | ::replaceAll(res, "'", "'");
58 | ::replaceAll(res, ">", ">");
59 | ::replaceAll(res, "–", "-");
60 | ::replaceAll(res, "“", "\"");
61 | ::replaceAll(res, "”", "\"");
62 | ::replaceAll(res, "‘", "'");
63 | ::replaceAll(res, "’", "'");
64 | ::replaceAll(res, "„", "'");
65 | ::replaceAll(res, """, "\"");
66 | ::replaceAll(res, "&", "&");
67 | ::replaceAll(res, "—", "-");
68 |
69 | return res;
70 | }
71 |
72 | URI getItemURI(ItemId id) {
73 | return kAPIItem + std::to_string(id) + ".json";
74 | }
75 |
76 | ItemType getItemType(const ItemData & itemData) {
77 | if (itemData.find("type") == itemData.end()) return ItemType::Unknown;
78 |
79 | auto strType = itemData.at("type");
80 |
81 | if (strType == "story") return ItemType::Story;
82 | if (strType == "comment") return ItemType::Comment;
83 | if (strType == "job") return ItemType::Job;
84 | if (strType == "poll") return ItemType::Poll;
85 | if (strType == "pollopt") return ItemType::PollOpt;
86 |
87 | return ItemType::Unknown;
88 | }
89 |
90 | void parseStory(const ItemData & data, Story & res) {
91 | try {
92 | res.by = data.at("by");
93 | } catch (...) {
94 | res.by = "[deleted]";
95 | }
96 | try {
97 | res.descendants = std::stoi(data.at("descendants"));
98 | } catch (...) {
99 | res.descendants = 0;
100 | }
101 | try {
102 | res.id = std::stoi(data.at("id"));
103 | } catch (...) {
104 | res.id = 0;
105 | }
106 | try {
107 | res.kids = JSON::parseIntArray(data.at("kids"));
108 | } catch (...) {
109 | res.kids.clear();
110 | }
111 | try {
112 | res.score = std::stoi(data.at("score"));
113 | } catch (...) {
114 | res.score = 0;
115 | }
116 | try {
117 | res.time = std::stoll(data.at("time"));
118 | } catch (...) {
119 | res.time = 0;
120 | }
121 | try {
122 | res.text = parseHTML(data.at("text"));
123 | } catch (...) {
124 | res.text = "";
125 | }
126 | try {
127 | res.title = parseHTML(data.at("title"));
128 | } catch (...) {
129 | res.title = "";
130 | }
131 | try {
132 | res.url = data.at("url");
133 | res.domain = "";
134 | int slash = 0;
135 | for (auto & ch : res.url) {
136 | if (ch == '/') {
137 | ++slash;
138 | continue;
139 | }
140 | if (slash > 2) break;
141 | if (slash > 1) res.domain += ch;
142 | }
143 | } catch (...) {
144 | res.url = "";
145 | res.domain = "";
146 | }
147 | }
148 |
149 | void parseComment(const ItemData & data, Comment & res) {
150 | try {
151 | res.by = data.at("by");
152 | } catch (...) {
153 | res.by = "[deleted]";
154 | }
155 | try {
156 | res.id = std::stoi(data.at("id"));
157 | } catch (...) {
158 | res.id = 0;
159 | }
160 | try {
161 | res.kids = JSON::parseIntArray(data.at("kids"));
162 | } catch (...) {
163 | res.kids.clear();
164 | }
165 | try {
166 | res.parent = std::stoi(data.at("parent"));
167 | } catch (...) {
168 | res.parent = 0;
169 | }
170 | try {
171 | res.text = parseHTML(data.at("text"));
172 | } catch (...) {
173 | res.text = "";
174 | }
175 | try {
176 | res.time = std::stoll(data.at("time"));
177 | } catch (...) {
178 | res.time = 0;
179 | }
180 | }
181 |
182 | void parseJob(const ItemData & data, Job & res) {
183 | try {
184 | res.by = data.at("by");
185 | } catch (...) {
186 | res.by = "[deleted]";
187 | }
188 | try {
189 | res.id = std::stoi(data.at("id"));
190 | } catch (...) {
191 | res.id = 0;
192 | }
193 | try {
194 | res.score = std::stoi(data.at("score"));
195 | } catch (...) {
196 | res.score = 0;
197 | }
198 | try {
199 | res.time = std::stoll(data.at("time"));
200 | } catch (...) {
201 | res.time = 0;
202 | }
203 | try {
204 | res.title = parseHTML(data.at("title"));
205 | } catch (...) {
206 | res.title = "";
207 | }
208 | try {
209 | res.url = data.at("url");
210 | res.domain = "";
211 | int slash = 0;
212 | for (auto & ch : res.url) {
213 | if (ch == '/') {
214 | ++slash;
215 | continue;
216 | }
217 | if (slash > 2) break;
218 | if (slash > 1) res.domain += ch;
219 | }
220 | } catch (...) {
221 | res.url = "";
222 | res.domain = "";
223 | }
224 | }
225 |
226 | ItemIds getStoriesIds(const URI & uri) {
227 | return JSON::parseIntArray(getJSONForURI(uri));
228 | }
229 |
230 | ItemIds getChangedItemsIds() {
231 | auto data = JSON::parseJSONMap(getJSONForURI(HN::kAPIUpdates));
232 | return JSON::parseIntArray(data["items"]);
233 | }
234 |
235 |
236 | bool State::update(const ItemIds & toRefresh) {
237 | bool updated = false;
238 |
239 | auto now = ::t_s();
240 |
241 | if (timeout(now, lastUpdatePoll_s)) {
242 | requestJSONForURI(HN::kAPITopStories);
243 | //requestJSONForURI(HN::kAPIBestStories);
244 | requestJSONForURI(HN::kAPIShowStories);
245 | requestJSONForURI(HN::kAPIAskStories);
246 | requestJSONForURI(HN::kAPINewStories);
247 | requestJSONForURI(HN::kAPIUpdates);
248 |
249 | lastUpdatePoll_s = ::t_s();
250 | updated = true;
251 | } else {
252 | nextUpdate = lastUpdatePoll_s + 30 - now;
253 | }
254 |
255 | {
256 | {
257 | auto ids = HN::getStoriesIds(HN::kAPITopStories);
258 | if (ids.empty() == false) {
259 | idsTop = std::move(ids);
260 | updated = true;
261 | }
262 | }
263 |
264 | //{
265 | // auto ids = HN::getStoriesIds(HN::kAPIBestStories);
266 | // if (ids.empty() == false) {
267 | // idsBest = std::move(ids);
268 | // }
269 | //}
270 |
271 | {
272 | auto ids = HN::getStoriesIds(HN::kAPIShowStories);
273 | if (ids.empty() == false) {
274 | idsShow = std::move(ids);
275 | updated = true;
276 | }
277 | }
278 |
279 | {
280 | auto ids = HN::getStoriesIds(HN::kAPIAskStories);
281 | if (ids.empty() == false) {
282 | idsAsk = std::move(ids);
283 | updated = true;
284 | }
285 | }
286 |
287 | {
288 | auto ids = HN::getStoriesIds(HN::kAPINewStories);
289 | if (ids.empty() == false) {
290 | idsNew = std::move(ids);
291 | updated = true;
292 | }
293 | }
294 | }
295 |
296 | {
297 | auto ids = HN::getChangedItemsIds();
298 | for (auto id : ids) {
299 | if (items.find(id) == items.end()) continue;
300 | items[id].needUpdate = true;
301 | items[id].needRequest = true;
302 | }
303 | }
304 |
305 | for (auto id : toRefresh) {
306 | if (items[id].needRequest == false) continue;
307 |
308 | requestJSONForURI(getItemURI(id));
309 | items[id].needRequest = false;
310 | updated = true;
311 | }
312 |
313 | for (auto id : toRefresh) {
314 | if (items[id].needUpdate == false) continue;
315 |
316 | const auto json = getJSONForURI(getItemURI(id));
317 | if (json == "") continue;
318 |
319 | const auto data = JSON::parseJSONMap(json);
320 | const auto type = getItemType(data);
321 | auto & item = items[id];
322 | switch (type) {
323 | case ItemType::Unknown:
324 | {
325 | }
326 | break;
327 | case ItemType::Story:
328 | {
329 | if (std::holds_alternative(item.data) == false) {
330 | item.data = Story();
331 | }
332 |
333 | Story & story = std::get(item.data);
334 | if (item.needUpdate) {
335 | parseStory(data, story);
336 | item.needUpdate = false;
337 | updated = true;
338 | }
339 | }
340 | break;
341 | case ItemType::Comment:
342 | {
343 | if (std::holds_alternative(item.data) == false) {
344 | item.data = Comment();
345 | }
346 |
347 | Comment & story = std::get(item.data);
348 | if (item.needUpdate) {
349 | parseComment(data, story);
350 | item.needUpdate = false;
351 | updated = true;
352 | }
353 | }
354 | break;
355 | case ItemType::Job:
356 | {
357 | if (std::holds_alternative(item.data) == false) {
358 | item.data = Job();
359 | }
360 |
361 | Job & job = std::get(item.data);
362 | if (item.needUpdate) {
363 | parseJob(data, job);
364 | item.needUpdate = false;
365 | updated = true;
366 | }
367 | }
368 | break;
369 | case ItemType::Poll:
370 | {
371 | // temp
372 | item.needUpdate = false;
373 | }
374 | break;
375 | case ItemType::PollOpt:
376 | {
377 | // temp
378 | item.needUpdate = false;
379 | }
380 | break;
381 | };
382 |
383 | break;
384 | }
385 |
386 | nFetches = getNFetches();
387 | totalBytesDownloaded = getTotalBytesDownloaded();
388 |
389 | updateRequests_impl();
390 |
391 | return updated;
392 | }
393 |
394 | void State::forceUpdate(const ItemIds & toUpdate) {
395 | auto tNow_s = t_s();
396 | for (auto id : toUpdate) {
397 | if (items.find(id) == items.end()) continue;
398 | if (tNow_s - items[id].lastForceUpdate_s > 60) {
399 | items[id].needUpdate = true;
400 | items[id].needRequest = true;
401 |
402 | items[id].lastForceUpdate_s = tNow_s;
403 | }
404 | }
405 | }
406 |
407 | bool State::timeout(uint64_t now, uint64_t last) const {
408 | return now - last > 30;
409 | }
410 |
411 | std::string State::timeSince(uint64_t t) const {
412 | auto delta = t_s() - t;
413 | if (delta < 60) return std::to_string(delta) + " seconds";
414 | if (delta < 3600) return std::to_string(delta/60) + " minutes";
415 | if (delta < 24*3600) return std::to_string(delta/3600) + " hours";
416 | return std::to_string(delta/24/3600) + " days";
417 | }
418 |
419 | void State::requestJSONForURI(std::string uri) {
420 | snprintf(curURI, 512, "%s", uri.c_str());
421 |
422 | requestJSONForURI_impl(std::move(uri));
423 | }
424 |
425 | }
426 |
--------------------------------------------------------------------------------
/examples/hnterm/hn-state.h:
--------------------------------------------------------------------------------
1 | /*! \file hn-state.h
2 | * \brief HN::State
3 | */
4 |
5 | #pragma once
6 |
7 | #include