aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore14
-rw-r--r--src/application.c163
-rw-r--r--src/application.h114
-rw-r--r--src/chat.c382
-rw-r--r--src/chat.h95
-rw-r--r--src/meson.build28
-rw-r--r--src/messenger_cli.c19
-rw-r--r--src/ui/account_create_dialog.c75
-rw-r--r--src/ui/account_create_dialog.h67
-rw-r--r--src/ui/accounts.c134
-rw-r--r--src/ui/accounts.h85
-rw-r--r--src/ui/chat.h40
-rw-r--r--src/ui/chat_open_dialog.c75
-rw-r--r--src/ui/chat_open_dialog.h67
-rw-r--r--src/ui/chats.c239
-rw-r--r--src/ui/chats.h89
-rw-r--r--src/ui/list_input.h137
-rw-r--r--src/ui/lobby_create_dialog.c147
-rw-r--r--src/ui/lobby_create_dialog.h79
-rw-r--r--src/ui/lobby_enter_dialog.c101
-rw-r--r--src/ui/lobby_enter_dialog.h69
-rw-r--r--src/ui/members.c182
-rw-r--r--src/ui/members.h127
-rw-r--r--src/ui/meson.build33
-rw-r--r--src/ui/messages.c456
-rw-r--r--src/ui/messages.h139
-rw-r--r--src/ui/text_input.h89
-rw-r--r--src/util.c59
-rw-r--r--src/util.h57
29 files changed, 3356 insertions, 5 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..937a67b
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,14 @@
1.deps/
2.libs/
3
4Makefile.in
5Makefile
6
7stamp-h1
8
9messenger_cli_config.h
10messenger_cli_config.h.in
11messenger_cli_config.h.in~
12
13*.o
14*.a
diff --git a/src/application.c b/src/application.c
new file mode 100644
index 0000000..1b2bdbd
--- /dev/null
+++ b/src/application.c
@@ -0,0 +1,163 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file application.c
23 */
24
25#include "application.h"
26
27#include "util.h"
28
29#ifndef MESSENGER_CLI_BINARY
30#define MESSENGER_CLI_BINARY "messenger_cli"
31#endif
32
33#ifndef MESSENGER_CLI_VERSION
34#define MESSENGER_CLI_VERSION "unknown"
35#endif
36
37#ifndef MESSENGER_CLI_DESC
38#define MESSENGER_CLI_DESC "A CLI for the Messenger service of GNUnet."
39#endif
40
41void
42application_clear(MESSENGER_Application *app)
43{
44 app->accounts.window = NULL;
45 app->chats.window = NULL;
46 app->current.members.window = NULL;
47 app->current.messages.window = NULL;
48}
49
50void
51application_init(MESSENGER_Application *app,
52 int argc,
53 char **argv)
54{
55 const struct GNUNET_GETOPT_CommandLineOption options [] = {
56 GNUNET_GETOPT_option_version(MESSENGER_CLI_VERSION),
57 GNUNET_GETOPT_option_help(MESSENGER_CLI_DESC),
58 GNUNET_GETOPT_OPTION_END
59 };
60
61 memset(app, 0, sizeof(*app));
62
63 app->argc = argc;
64 app->argv = argv;
65
66 const int parsing = GNUNET_GETOPT_run(
67 MESSENGER_CLI_BINARY,
68 options,
69 app->argc,
70 app->argv
71 );
72
73 if (parsing <= 0)
74 {
75 app->window = NULL;
76 app->status = GNUNET_SYSERR == parsing? GNUNET_SYSERR : GNUNET_OK;
77 return;
78 }
79
80 app->window = initscr();
81
82 if (!(app->window))
83 {
84 app->status = GNUNET_SYSERR;
85 return;
86 }
87
88 application_refresh(app);
89
90 noecho();
91
92 keypad(app->window, TRUE);
93 wtimeout(app->window, 10);
94}
95
96void
97application_refresh(MESSENGER_Application *app)
98{
99 if (app->ui.logo) delwin(app->ui.logo);
100 if (app->ui.main) delwin(app->ui.main);
101 if (app->ui.left) delwin(app->ui.left);
102 if (app->ui.right) delwin(app->ui.right);
103 if (app->ui.input) delwin(app->ui.input);
104
105 memset(&(app->ui), 0, sizeof(app->ui));
106
107 curs_set(0);
108}
109
110static void
111run (void *cls,
112 UNUSED char* const* args,
113 UNUSED const char *cfgfile,
114 const struct GNUNET_CONFIGURATION_Handle *cfg)
115{
116 MESSENGER_Application *app = cls;
117
118 if (!(app->window))
119 return;
120
121 chat_start(&(app->chat), app, cfg);
122}
123
124void
125application_run(MESSENGER_Application *app)
126{
127 const struct GNUNET_GETOPT_CommandLineOption options [] = {
128 GNUNET_GETOPT_OPTION_END
129 };
130
131 app->status = GNUNET_PROGRAM_run(
132 1,
133 app->argv,
134 MESSENGER_CLI_BINARY,
135 gettext_noop(MESSENGER_CLI_DESC),
136 options,
137 &run,
138 app
139 );
140
141 members_clear(&(app->current.members));
142 messages_clear(&(app->current.messages));
143
144 application_clear(app);
145
146 if (app->window)
147 delwin(app->window);
148
149 if (ERR == endwin())
150 {
151 app->status = GNUNET_SYSERR;
152 return;
153 }
154}
155
156int
157application_status(MESSENGER_Application *app)
158{
159 if (app->status != GNUNET_OK)
160 return EXIT_FAILURE;
161 else
162 return EXIT_SUCCESS;
163}
diff --git a/src/application.h b/src/application.h
new file mode 100644
index 0000000..2ed9ea4
--- /dev/null
+++ b/src/application.h
@@ -0,0 +1,114 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file application.h
23 */
24
25#ifndef APPLICATION_H_
26#define APPLICATION_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31#include "chat.h"
32
33#include "ui/accounts.h"
34#include "ui/chat.h"
35#include "ui/chats.h"
36
37/**
38 * @struct MESSENGER_Application
39 */
40typedef struct MESSENGER_Application
41{
42 char **argv;
43 int argc;
44
45 int status;
46 WINDOW *window;
47
48 struct {
49 WINDOW *logo;
50 WINDOW *main;
51 WINDOW *left;
52 WINDOW *right;
53 WINDOW *input;
54 } ui;
55
56 MESSENGER_Chat chat;
57
58 UI_ACCOUNTS_Handle accounts;
59 UI_CHATS_Handle chats;
60 UI_CHAT_Handle current;
61} MESSENGER_Application;
62
63/**
64 * Clears the application handle to reset all views
65 * which might have been in use.
66 *
67 * @param[out] app Application handle
68 */
69void
70application_clear(MESSENGER_Application *app);
71
72/**
73 * Initializes the application handle with the program
74 * arguments provided from the main function.
75 *
76 * @param[out] app Application handle
77 * @param[in] argc Argument count
78 * @param[in] argv Argument array
79 */
80void
81application_init(MESSENGER_Application *app,
82 int argc,
83 char **argv);
84
85/**
86 * Refreshes the application handle freeing all temporary
87 * WINDOW handles to manage the different views which
88 * were used.
89 *
90 * @param[out] app Application handle
91 */
92void
93application_refresh(MESSENGER_Application *app);
94
95/**
96 * Starts the main loop of the application which will
97 * be processed via GNUnet and its callback management.
98 *
99 * @param[in,out] app Application handle
100 */
101void
102application_run(MESSENGER_Application *app);
103
104/**
105 * Returns the status code by the given application
106 * at current state.
107 *
108 * @param[in] app Application handle
109 * @return #EXIT_FAILURE on failure, otherwise #EXIT_SUCCESS
110 */
111int
112application_status(MESSENGER_Application *app);
113
114#endif /* APPLICATION_H_ */
diff --git a/src/chat.c b/src/chat.c
new file mode 100644
index 0000000..792f8eb
--- /dev/null
+++ b/src/chat.c
@@ -0,0 +1,382 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file chat.c
23 */
24
25#include "chat.h"
26
27#include "application.h"
28#include "util.h"
29#include <curses.h>
30#include <gnunet/gnunet_chat_lib.h>
31
32#ifndef MESSENGER_CLI_BINARY
33#define MESSENGER_CLI_BINARY "messenger_cli"
34#endif
35
36#ifndef MESSENGER_CLI_VERSION
37#define MESSENGER_CLI_VERSION "unknown"
38#endif
39
40static void
41_chat_refresh(MESSENGER_Application *app)
42{
43 application_clear(app);
44 chat_update_layout(&(app->chat), app);
45
46 accounts_print(&(app->accounts), app);
47 chats_print(&(app->chats), app);
48 members_print(&(app->current.members));
49 messages_print(&(app->current.messages));
50
51 if (!app->ui.logo)
52 return;
53
54 werase(app->ui.logo);
55 wmove(app->ui.logo, 0, 0);
56
57 util_print_logo(app->ui.logo);
58
59 int x = getcurx(app->ui.logo);
60 int y = getcury(app->ui.logo);
61
62 util_print_info(app->ui.logo, MESSENGER_CLI_VERSION);
63
64 wmove(app->ui.logo, --y, x);
65 util_print_info(app->ui.logo, MESSENGER_CLI_BINARY);
66}
67
68static bool
69_chat_event(MESSENGER_Application *app,
70 int key)
71{
72 if (key < 0)
73 goto refresh;
74
75 const struct GNUNET_CHAT_Account *account = GNUNET_CHAT_get_connected(
76 app->chat.handle
77 );
78
79 if (!account)
80 accounts_event(&(app->accounts), app, key);
81 else if (app->chat.context)
82 {
83 if (app->chat.show_members)
84 members_event(&(app->current.members), app, key);
85 else
86 messages_event(&(app->current.messages), app, key);
87 }
88 else
89 chats_event(&(app->chats), app, key);
90
91 if (app->chat.quit)
92 return TRUE;
93
94refresh:
95 _chat_refresh(app);
96 return FALSE;
97}
98
99static int
100_chat_message(void *cls,
101 struct GNUNET_CHAT_Context *context,
102 const struct GNUNET_CHAT_Message *message)
103{
104 MESSENGER_Application *app = cls;
105
106 chat_process_message(&(app->chat), context, message);
107
108 _chat_event(app, KEY_RESIZE);
109 return GNUNET_YES;
110}
111
112static void
113_chat_idle(void *cls)
114{
115 MESSENGER_Application *app = cls;
116 app->chat.idle = NULL;
117
118 if (app->chat.quit)
119 return;
120
121 if (_chat_event(app, wgetch(app->window)))
122 {
123 chat_stop(&(app->chat));
124 return;
125 }
126
127 app->chat.idle = GNUNET_SCHEDULER_add_delayed_with_priority(
128 GNUNET_TIME_relative_multiply(
129 GNUNET_TIME_relative_get_millisecond_(),
130 wgetdelay(app->window)
131 ),
132 GNUNET_SCHEDULER_PRIORITY_IDLE,
133 &_chat_idle,
134 app
135 );
136}
137
138void
139chat_start(MESSENGER_Chat *chat,
140 struct MESSENGER_Application *app,
141 const struct GNUNET_CONFIGURATION_Handle *cfg)
142{
143 chat->handle = GNUNET_CHAT_start(
144 cfg,
145 &_chat_message,
146 app
147 );
148
149 chat->context = NULL;
150
151 chat->idle = GNUNET_SCHEDULER_add_now(
152 &_chat_idle,
153 app
154 );
155
156 chat->quit = FALSE;
157}
158
159void
160chat_stop(MESSENGER_Chat *chat)
161{
162 if (chat->idle)
163 {
164 GNUNET_SCHEDULER_cancel(chat->idle);
165 chat->idle = NULL;
166 }
167
168 GNUNET_CHAT_stop(chat->handle);
169 chat->handle = NULL;
170
171 chat->quit = TRUE;
172}
173
174void
175_chat_update_layout_accounts(struct MESSENGER_Application *app)
176{
177 int rows, cols;
178 getmaxyx(app->window, rows, cols);
179
180 if (rows >= UTIL_LOGO_ROWS + UI_ACCOUNTS_ROWS_MIN)
181 {
182 const int offset = UTIL_LOGO_ROWS + 1;
183
184 app->ui.logo = subwin(app->window, UTIL_LOGO_ROWS, cols, 0, 0);
185 app->ui.main = subwin(app->window, rows - offset, cols, offset, 0);
186
187 wmove(app->window, UTIL_LOGO_ROWS, 0);
188 whline(app->window, ACS_HLINE, cols);
189 }
190 else
191 app->ui.main = subwin(app->window, rows, cols, 0, 0);
192
193 app->accounts.window = app->ui.main;
194}
195
196void
197_chat_update_layout_chats(struct MESSENGER_Application *app)
198{
199 int rows, cols;
200 getmaxyx(app->window, rows, cols);
201
202 int min_rows = UI_CHATS_ROWS_MIN;
203 int offset_x = 0;
204 int offset_y = 0;
205
206 if (cols >= UI_ACCOUNTS_COLS_MIN + UI_CHATS_COLS_MIN)
207 {
208 offset_x = UI_ACCOUNTS_COLS_MIN + 1;
209
210 if (UI_ACCOUNTS_ROWS_MIN > min_rows) min_rows = UI_ACCOUNTS_ROWS_MIN;
211 }
212
213 if (rows >= UTIL_LOGO_ROWS + min_rows)
214 {
215 offset_y = UTIL_LOGO_ROWS + 1;
216
217 app->ui.logo = subwin(app->window, UTIL_LOGO_ROWS, cols, 0, 0);
218
219 wmove(app->window, UTIL_LOGO_ROWS, 0);
220 whline(app->window, ACS_HLINE, cols);
221 }
222
223 if (offset_x > 0)
224 {
225 app->ui.left = subwin(
226 app->window,
227 rows - offset_y,
228 UI_ACCOUNTS_COLS_MIN,
229 offset_y,
230 0
231 );
232
233 wmove(app->window, offset_y > 0? offset_y : 0, UI_ACCOUNTS_COLS_MIN);
234 wvline(app->window, ACS_VLINE, rows - offset_y);
235
236 if (offset_y > 0)
237 {
238 wmove(app->window, offset_y - 1, UI_ACCOUNTS_COLS_MIN);
239 waddch(app->window, ACS_TTEE);
240 }
241 }
242
243 app->ui.main = subwin(
244 app->window,
245 rows - offset_y,
246 cols - offset_x,
247 offset_y,
248 offset_x
249 );
250
251 app->accounts.window = app->ui.left;
252 app->chats.window = app->ui.main;
253}
254
255void
256_chat_update_layout_messages(struct MESSENGER_Application *app)
257{
258 int rows, cols;
259 getmaxyx(app->window, rows, cols);
260
261 const int cols_min_left = (UTIL_LOGO_COLS > UI_CHATS_COLS_MIN?
262 UTIL_LOGO_COLS : UI_CHATS_COLS_MIN
263 );
264
265 int offset_x, cut_x;
266 cut_x = 0;
267
268 if (cols >= cols_min_left + UI_MESSAGES_COLS_MIN)
269 offset_x = cols_min_left + 1;
270 else
271 {
272 offset_x = 0;
273 goto skip_left_split;
274 }
275
276 if (rows >= UTIL_LOGO_ROWS + UI_CHATS_ROWS_MIN)
277 {
278 const int offset = UTIL_LOGO_ROWS + 1;
279
280 app->ui.logo = subwin(app->window, UTIL_LOGO_ROWS, cols_min_left, 0, 0);
281 app->ui.left = subwin(app->window, rows - offset, cols_min_left, offset, 0);
282
283 wmove(app->window, UTIL_LOGO_ROWS, 0);
284 whline(app->window, ACS_HLINE, cols_min_left);
285 }
286 else
287 app->ui.left = subwin(app->window, rows, cols_min_left, 0, 0);
288
289 if (cols >= cols_min_left + UI_MESSAGES_COLS_MIN + UI_MEMBERS_COLS_MIN)
290 {
291 cut_x = UI_MEMBERS_COLS_MIN + 1;
292
293 app->ui.right = subwin(
294 app->window,
295 rows,
296 UI_MEMBERS_COLS_MIN,
297 0,
298 cols - UI_MEMBERS_COLS_MIN
299 );
300
301 wmove(app->window, 0, cols - cut_x);
302 wvline(app->window, ACS_VLINE, rows);
303 }
304
305 wmove(app->window, 0, cols_min_left);
306 wvline(app->window, ACS_VLINE, rows);
307
308skip_left_split:
309 app->ui.main = subwin(
310 app->window,
311 rows,
312 cols - offset_x - cut_x,
313 0,
314 offset_x
315 );
316
317 app->chats.window = app->ui.left;
318
319 if (app->ui.right)
320 {
321 app->current.members.window = app->ui.right;
322 app->current.messages.window = app->ui.main;
323 return;
324 }
325
326 if (app->chat.show_members)
327 app->current.members.window = app->ui.main;
328 else
329 app->current.messages.window = app->ui.main;
330}
331
332void
333chat_update_layout(MESSENGER_Chat *chat,
334 struct MESSENGER_Application *app)
335{
336 const struct GNUNET_CHAT_Account *account = GNUNET_CHAT_get_connected(
337 chat->handle
338 );
339
340 application_refresh(app);
341
342 if (!account)
343 _chat_update_layout_accounts(app);
344 else if (app->chat.context)
345 _chat_update_layout_messages(app);
346 else
347 _chat_update_layout_chats(app);
348}
349
350void
351chat_process_message(UNUSED MESSENGER_Chat *chat,
352 struct GNUNET_CHAT_Context *context,
353 const struct GNUNET_CHAT_Message *message)
354{
355 enum GNUNET_CHAT_MessageKind kind = GNUNET_CHAT_message_get_kind(message);
356
357 struct GNUNET_CHAT_Contact *sender = GNUNET_CHAT_message_get_sender(message);
358
359 UI_CHAT_Handle *current = (UI_CHAT_Handle*) (
360 GNUNET_CHAT_context_get_user_pointer(context)
361 );
362
363 if (!current)
364 return;
365
366 bool new_member = FALSE;
367
368 if (GNUNET_CHAT_KIND_LEAVE == kind)
369 members_remove(&(current->members), sender);
370 else if (GNUNET_CHAT_KIND_JOIN == kind)
371 new_member = members_add(&(current->members), sender);
372
373 if (GNUNET_CHAT_KIND_DELETION == kind)
374 messages_remove(
375 &(current->messages),
376 GNUNET_CHAT_message_get_target(message)
377 );
378 else if (GNUNET_YES == GNUNET_CHAT_message_is_deleted(message))
379 messages_remove(&(current->messages), message);
380 else if ((GNUNET_CHAT_KIND_JOIN != kind) || (new_member))
381 messages_add(&(current->messages), message);
382}
diff --git a/src/chat.h b/src/chat.h
new file mode 100644
index 0000000..00c01f1
--- /dev/null
+++ b/src/chat.h
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file chat.h
23 */
24
25#ifndef CHAT_H_
26#define CHAT_H_
27
28#include <gnunet/gnunet_chat_lib.h>
29#include <gnunet/gnunet_util_lib.h>
30
31struct MESSENGER_Application;
32
33/**
34 * @struct MESSENGER_Chat
35 */
36typedef struct MESSENGER_Chat
37{
38 struct GNUNET_CHAT_Handle *handle;
39 struct GNUNET_CHAT_Context *context;
40
41 struct GNUNET_SCHEDULER_Task *idle;
42
43 bool show_members;
44 bool quit;
45} MESSENGER_Chat;
46
47/**
48 * Starts the processing of the given applications
49 * chat handle.
50 *
51 * @param[out] chat Application chat handle
52 * @param[in,out] app Application handle
53 * @param[in] cfg Configuration
54 */
55void
56chat_start(MESSENGER_Chat *chat,
57 struct MESSENGER_Application *app,
58 const struct GNUNET_CONFIGURATION_Handle *cfg);
59
60/**
61 * Stops the processing of the given applications
62 * chat handle.
63 *
64 * @param[in,out] chat Application chat handle
65 */
66void
67chat_stop(MESSENGER_Chat *chat);
68
69/**
70 * Updates the layout of the applications views depending
71 * on the main windows resolution and the current state
72 * of the applications chat handle.
73 *
74 * @param[in] chat Application chat handle
75 * @param[out] app Application handle
76 */
77void
78chat_update_layout(MESSENGER_Chat *chat,
79 struct MESSENGER_Application *app);
80
81/**
82 * Processes a chat message to update the list of
83 * required resources to handle visual representation
84 * of current members in a chat or the messages.
85 *
86 * @param[in,out] chat Application chat handle
87 * @param[in] context Chat context of the message
88 * @param[in] message Chat message
89 */
90void
91chat_process_message(MESSENGER_Chat *chat,
92 struct GNUNET_CHAT_Context *context,
93 const struct GNUNET_CHAT_Message *message);
94
95#endif /* CHAT_H_ */
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..1d98bea
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,28 @@
1#
2# This file is part of GNUnet.
3# Copyright (C) 2023 GNUnet e.V.
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20
21subdir('ui')
22
23messenger_cli_sources = files([
24 'application.c', 'application.h',
25 'chat.c', 'chat.h',
26 'util.c', 'util.h',
27 'messenger_cli.c',
28]) + messenger_cli_ui_sources
diff --git a/src/messenger_cli.c b/src/messenger_cli.c
index cee3020..7127514 100644
--- a/src/messenger_cli.c
+++ b/src/messenger_cli.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 Copyright (C) 2021 GNUnet e.V. 3 Copyright (C) 2021--2022 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published 6 under the terms of the GNU Affero General Public License as published
@@ -22,9 +22,18 @@
22 * @file messenger_cli.c 22 * @file messenger_cli.c
23 */ 23 */
24 24
25#include <gnunet/gnunet_chat_lib.h> 25#include "application.h"
26 26
27int main(int argc, const char** argv) { 27int
28 GNUNET_CHAT_test("Hello world"); 28main (int argc, char** argv)
29 return 0; 29{
30 MESSENGER_Application app;
31
32 application_init(&app, argc, argv);
33 application_run(&app);
34
35 const int status = application_status(&app);
36
37 exit_curses(status);
38 return status;
30} 39}
diff --git a/src/ui/account_create_dialog.c b/src/ui/account_create_dialog.c
new file mode 100644
index 0000000..6845a1d
--- /dev/null
+++ b/src/ui/account_create_dialog.c
@@ -0,0 +1,75 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/account_create_dialog.c
23 */
24
25#include "account_create_dialog.h"
26
27#include <gnunet/gnunet_chat_lib.h>
28#include <gnunet/gnunet_util_lib.h>
29
30#include "text_input.h"
31#include "../application.h"
32
33void
34account_create_dialog_event(UI_ACCOUNT_CREATE_DIALOG_Handle *create_dialog,
35 struct MESSENGER_Application *app,
36 int key)
37{
38 switch (key)
39 {
40 case 27:
41 case KEY_EXIT:
42 create_dialog->window = NULL;
43 break;
44 case '\n':
45 case KEY_ENTER:
46 if (create_dialog->name_len > 0)
47 GNUNET_CHAT_account_create(app->chat.handle, create_dialog->name);
48
49 create_dialog->name_len = 0;
50 create_dialog->window = NULL;
51 break;
52 default:
53 break;
54 }
55
56 text_input_event(create_dialog->name, key);
57}
58
59void
60account_create_dialog_print(UI_ACCOUNT_CREATE_DIALOG_Handle *create_dialog)
61{
62 if (!(create_dialog->window))
63 return;
64
65 WINDOW *window = *(create_dialog->window);
66
67 werase(window);
68 wmove(window, 0, 0);
69
70 wprintw(window, "%s", create_dialog->name);
71 wmove(window, 0, create_dialog->name_pos);
72
73 wcursyncup(window);
74 curs_set(1);
75}
diff --git a/src/ui/account_create_dialog.h b/src/ui/account_create_dialog.h
new file mode 100644
index 0000000..93c8318
--- /dev/null
+++ b/src/ui/account_create_dialog.h
@@ -0,0 +1,67 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/account_create_dialog.h
23 */
24
25#ifndef UI_ACCOUNT_CREATE_DIALOG_H_
26#define UI_ACCOUNT_CREATE_DIALOG_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31struct MESSENGER_Application;
32
33/**
34 * @struct UI_ACCOUNT_CREATE_DIALOG_Handle
35 */
36typedef struct UI_ACCOUNT_CREATE_DIALOG_Handle
37{
38 WINDOW **window;
39
40 char name [256];
41 int name_len;
42 int name_pos;
43} UI_ACCOUNT_CREATE_DIALOG_Handle;
44
45/**
46 * Processes the current key event by the dialog
47 * to create a new account.
48 *
49 * @param[in,out] create_dialog Account creation dialog
50 * @param[in,out] app Application handle
51 * @param[in] key Key
52 */
53void
54account_create_dialog_event(UI_ACCOUNT_CREATE_DIALOG_Handle *create_dialog,
55 struct MESSENGER_Application *app,
56 int key);
57
58/**
59 * Prints the content of the dialog to create a new
60 * account to its selected window view.
61 *
62 * @param[in] create_dialog Account creation dialog
63 */
64void
65account_create_dialog_print(UI_ACCOUNT_CREATE_DIALOG_Handle *create_dialog);
66
67#endif /* UI_ACCOUNT_CREATE_DIALOG_H_ */
diff --git a/src/ui/accounts.c b/src/ui/accounts.c
new file mode 100644
index 0000000..bdc5086
--- /dev/null
+++ b/src/ui/accounts.c
@@ -0,0 +1,134 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/accounts.c
23 */
24
25#include "accounts.h"
26
27#include "list_input.h"
28#include "../application.h"
29#include "../util.h"
30
31int
32_accounts_iterate(void *cls,
33 UNUSED const struct GNUNET_CHAT_Handle *handle,
34 struct GNUNET_CHAT_Account *account)
35{
36 UI_ACCOUNTS_Handle *accounts = cls;
37 list_input_select(accounts, 1, account);
38 return GNUNET_YES;
39}
40
41void
42accounts_event(UI_ACCOUNTS_Handle *accounts,
43 MESSENGER_Application *app,
44 int key)
45{
46 if (accounts->create_dialog.window)
47 {
48 account_create_dialog_event(&(accounts->create_dialog), app, key);
49 return;
50 }
51
52 list_input_reset(accounts);
53
54 GNUNET_CHAT_iterate_accounts(
55 app->chat.handle,
56 &_accounts_iterate,
57 accounts
58 );
59
60 list_input_select(accounts, 1, NULL);
61
62 switch (key)
63 {
64 case 27:
65 case KEY_EXIT:
66 app->chat.quit = TRUE;
67 break;
68 case '\n':
69 case KEY_ENTER:
70 if (accounts->selected)
71 GNUNET_CHAT_connect(app->chat.handle, accounts->selected);
72 else
73 accounts->create_dialog.window = &(accounts->window);
74 break;
75 default:
76 break;
77 }
78
79 list_input_event(accounts, key);
80}
81
82static int
83_accounts_print_entry(UI_ACCOUNTS_Handle *accounts,
84 char type,
85 const char *text)
86{
87 list_input_print_gnunet(accounts, 1);
88
89 const int attrs_select = A_BOLD;
90
91 if (selected) wattron(accounts->window, attrs_select);
92
93 wmove(accounts->window, y, 0);
94 wprintw(accounts->window, "[%c] %s", selected? type : ' ', text);
95
96 if (selected) wattroff(accounts->window, attrs_select);
97
98 return GNUNET_YES;
99}
100
101int
102_accounts_iterate_print(void *cls,
103 UNUSED const struct GNUNET_CHAT_Handle *handle,
104 struct GNUNET_CHAT_Account *account)
105{
106 UI_ACCOUNTS_Handle *accounts = cls;
107 const char *name = GNUNET_CHAT_account_get_name(account);
108 return _accounts_print_entry(accounts, 'x', name);
109}
110
111void
112accounts_print(UI_ACCOUNTS_Handle *accounts,
113 MESSENGER_Application *app)
114{
115 if (accounts->create_dialog.window)
116 {
117 account_create_dialog_print(&(accounts->create_dialog));
118 return;
119 }
120
121 if (!(accounts->window))
122 return;
123
124 list_input_reset(accounts);
125 werase(accounts->window);
126
127 GNUNET_CHAT_iterate_accounts(
128 app->chat.handle,
129 &_accounts_iterate_print,
130 accounts
131 );
132
133 _accounts_print_entry(accounts, '+', "Add account");
134}
diff --git a/src/ui/accounts.h b/src/ui/accounts.h
new file mode 100644
index 0000000..392f476
--- /dev/null
+++ b/src/ui/accounts.h
@@ -0,0 +1,85 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/accounts.h
23 */
24
25#ifndef UI_ACCOUNTS_H_
26#define UI_ACCOUNTS_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31#include <gnunet/gnunet_chat_lib.h>
32#include <gnunet/gnunet_util_lib.h>
33
34#include "account_create_dialog.h"
35
36struct MESSENGER_Application;
37
38/**
39 * @struct UI_ACCOUNTS_Handle
40 */
41typedef struct UI_ACCOUNTS_Handle
42{
43 WINDOW *window;
44
45 int line_prev;
46 int line_next;
47
48 int line_index;
49 int line_offset;
50 int line_selected;
51
52 struct GNUNET_CHAT_Account *selected;
53
54 UI_ACCOUNT_CREATE_DIALOG_Handle create_dialog;
55} UI_ACCOUNTS_Handle;
56
57#define UI_ACCOUNTS_ROWS_MIN 5
58#define UI_ACCOUNTS_COLS_MIN 30
59
60/**
61 * Processes the current key event by the view
62 * to show the list of chat accounts.
63 *
64 * @param[in,out] accounts Chat accounts view
65 * @param[in,out] app Application handle
66 * @param[in] key Key
67 */
68void
69accounts_event(UI_ACCOUNTS_Handle *accounts,
70 struct MESSENGER_Application *app,
71 int key);
72
73/**
74 * Prints the content of the view to show
75 * the list of chat accounts to its selected
76 * window view on screen.
77 *
78 * @param[in,out] accounts Chat accounts view
79 * @param[in] app Application handle
80 */
81void
82accounts_print(UI_ACCOUNTS_Handle *accounts,
83 struct MESSENGER_Application *app);
84
85#endif /* UI_ACCOUNTS_H_ */
diff --git a/src/ui/chat.h b/src/ui/chat.h
new file mode 100644
index 0000000..582ba66
--- /dev/null
+++ b/src/ui/chat.h
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/chat.h
23 */
24
25#ifndef UI_CHAT_H_
26#define UI_CHAT_H_
27
28#include "members.h"
29#include "messages.h"
30
31/**
32 * @struct UI_CHAT_Handle
33 */
34typedef struct UI_CHAT_Handle
35{
36 UI_MEMBERS_Handle members;
37 UI_MESSAGES_Handle messages;
38} UI_CHAT_Handle;
39
40#endif /* UI_CHAT_H_ */
diff --git a/src/ui/chat_open_dialog.c b/src/ui/chat_open_dialog.c
new file mode 100644
index 0000000..1ea5863
--- /dev/null
+++ b/src/ui/chat_open_dialog.c
@@ -0,0 +1,75 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/chat_open_dialog.c
23 */
24
25#include "chat_open_dialog.h"
26
27#include <gnunet/gnunet_chat_lib.h>
28#include <gnunet/gnunet_util_lib.h>
29
30#include "text_input.h"
31#include "../application.h"
32
33void
34chat_open_dialog_event(UI_CHAT_OPEN_DIALOG_Handle *open_dialog,
35 struct MESSENGER_Application *app,
36 int key)
37{
38 switch (key)
39 {
40 case 27:
41 case KEY_EXIT:
42 open_dialog->window = NULL;
43 break;
44 case '\n':
45 case KEY_ENTER:
46 if (open_dialog->topic_len > 0)
47 GNUNET_CHAT_group_create(app->chat.handle, open_dialog->topic);
48
49 open_dialog->topic_len = 0;
50 open_dialog->window = NULL;
51 break;
52 default:
53 break;
54 }
55
56 text_input_event(open_dialog->topic, key);
57}
58
59void
60chat_open_dialog_print(UI_CHAT_OPEN_DIALOG_Handle *open_dialog)
61{
62 if (!(open_dialog->window))
63 return;
64
65 WINDOW *window = *(open_dialog->window);
66
67 werase(window);
68 wmove(window, 0, 0);
69
70 wprintw(window, "%s", open_dialog->topic);
71 wmove(window, 0, open_dialog->topic_pos);
72
73 wcursyncup(window);
74 curs_set(1);
75}
diff --git a/src/ui/chat_open_dialog.h b/src/ui/chat_open_dialog.h
new file mode 100644
index 0000000..413d15e
--- /dev/null
+++ b/src/ui/chat_open_dialog.h
@@ -0,0 +1,67 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/chat_open_dialog.h
23 */
24
25#ifndef UI_CHAT_OPEN_DIALOG_H_
26#define UI_CHAT_OPEN_DIALOG_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31struct MESSENGER_Application;
32
33/**
34 * @struct UI_CHAT_OPEN_DIALOG_Handle
35 */
36typedef struct UI_CHAT_OPEN_DIALOG_Handle
37{
38 WINDOW **window;
39
40 char topic [256];
41 int topic_len;
42 int topic_pos;
43} UI_CHAT_OPEN_DIALOG_Handle;
44
45/**
46 * Processes the current key event by the dialog
47 * to open a chat.
48 *
49 * @param[in,out] open_dialog Chat opening dialog
50 * @param[in,out] app Application handle
51 * @param[in] key Key
52 */
53void
54chat_open_dialog_event(UI_CHAT_OPEN_DIALOG_Handle *open_dialog,
55 struct MESSENGER_Application *app,
56 int key);
57
58/**
59 * Prints the content of the dialog to open a
60 * chat to its selected window view.
61 *
62 * @param[in] open_dialog Chat opening dialog
63 */
64void
65chat_open_dialog_print(UI_CHAT_OPEN_DIALOG_Handle *open_dialog);
66
67#endif /* UI_CHAT_OPEN_DIALOG_H_ */
diff --git a/src/ui/chats.c b/src/ui/chats.c
new file mode 100644
index 0000000..5889dd4
--- /dev/null
+++ b/src/ui/chats.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/chats.c
23 */
24
25#include "chats.h"
26
27#include "list_input.h"
28#include "../application.h"
29#include "../util.h"
30#include <gnunet/gnunet_chat_lib.h>
31#include <gnunet/gnunet_common.h>
32
33static enum GNUNET_GenericReturnValue
34_chats_iterate_group(void *cls,
35 UNUSED struct GNUNET_CHAT_Handle *handle,
36 struct GNUNET_CHAT_Group *group)
37{
38 UI_CHATS_Handle *chats = cls;
39 list_input_select(chats, 1, GNUNET_CHAT_group_get_context(group));
40 return GNUNET_YES;
41}
42
43static enum GNUNET_GenericReturnValue
44_chats_iterate_contact(void *cls,
45 UNUSED struct GNUNET_CHAT_Handle *handle,
46 struct GNUNET_CHAT_Contact *contact)
47{
48 UI_CHATS_Handle *chats = cls;
49
50 if (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
51 return GNUNET_YES;
52
53 list_input_select(chats, 1, GNUNET_CHAT_contact_get_context(contact));
54 return GNUNET_YES;
55}
56
57static enum GNUNET_GenericReturnValue
58_chats_iterate_messages(void *cls,
59 struct GNUNET_CHAT_Context *context,
60 const struct GNUNET_CHAT_Message *message)
61{
62 MESSENGER_Chat *chat = cls;
63 chat_process_message(chat, context, message);
64 return GNUNET_YES;
65}
66
67void
68chats_event(UI_CHATS_Handle *chats,
69 MESSENGER_Application *app,
70 int key)
71{
72 if (chats->open_dialog.window)
73 {
74 chat_open_dialog_event(&(chats->open_dialog), app, key);
75 return;
76 }
77 else if (chats->create_dialog.win)
78 {
79 lobby_create_dialog_event(&(chats->create_dialog), app, key);
80 return;
81 }
82 else if (chats->enter_dialog.window)
83 {
84 lobby_enter_dialog_event(&(chats->enter_dialog), app, key);
85 return;
86 }
87
88 list_input_reset(chats);
89
90 GNUNET_CHAT_iterate_groups(
91 app->chat.handle,
92 &_chats_iterate_group,
93 chats
94 );
95
96 GNUNET_CHAT_iterate_contacts(
97 app->chat.handle,
98 &_chats_iterate_contact,
99 chats
100 );
101
102 list_input_select(chats, 1, NULL);
103 list_input_select(chats, 1, NULL);
104 list_input_select(chats, 1, NULL);
105
106 const int count = chats->line_index;
107
108 switch (key)
109 {
110 case 27:
111 case KEY_EXIT:
112 GNUNET_CHAT_disconnect(app->chat.handle);
113 break;
114 case '\n':
115 case KEY_ENTER:
116 if (chats->selected)
117 {
118 GNUNET_CHAT_context_request(chats->selected);
119
120 members_clear(&(app->current.members));
121 messages_clear(&(app->current.messages));
122
123 GNUNET_CHAT_context_set_user_pointer(
124 chats->selected,
125 &(app->current)
126 );
127
128 GNUNET_CHAT_context_iterate_messages(
129 chats->selected,
130 &_chats_iterate_messages,
131 &(app->chat)
132 );
133
134 app->chat.context = chats->selected;
135 }
136 else if (chats->line_selected == count - 3)
137 chats->open_dialog.window = &(chats->window);
138 else if (chats->line_selected == count - 2)
139 chats->create_dialog.win = &(chats->window);
140 else if (chats->line_selected == count - 1)
141 chats->enter_dialog.window = &(chats->window);
142 break;
143 default:
144 break;
145 }
146
147 list_input_event(chats, key);
148}
149
150static int
151_chats_print_entry(UI_CHATS_Handle *chats,
152 char type,
153 char chat_type,
154 const char *text)
155{
156 list_input_print_gnunet(chats, 1);
157
158 const int attrs_select = A_BOLD;
159
160 if (selected) wattron(chats->window, attrs_select);
161
162 wmove(chats->window, y, 0);
163
164 if (chat_type)
165 wprintw(chats->window, "[%c][%c] %s", type, chat_type, text);
166 else
167 wprintw(chats->window, "[%c] %s", type, text);
168
169 if (selected) wattroff(chats->window, attrs_select);
170
171 return GNUNET_YES;
172}
173
174enum GNUNET_GenericReturnValue
175_chats_iterate_print_group(void *cls,
176 UNUSED struct GNUNET_CHAT_Handle *handle,
177 struct GNUNET_CHAT_Group *group)
178{
179 UI_CHATS_Handle *chats = cls;
180 const char *name = GNUNET_CHAT_group_get_name(group);
181 return _chats_print_entry(chats, 'x', 'G', name);
182}
183
184enum GNUNET_GenericReturnValue
185_chats_iterate_print_contact(void *cls,
186 UNUSED struct GNUNET_CHAT_Handle *handle,
187 struct GNUNET_CHAT_Contact *contact)
188{
189 UI_CHATS_Handle *chats = cls;
190
191 if (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
192 return GNUNET_YES;
193
194 const char *name = GNUNET_CHAT_contact_get_name(contact);
195 return _chats_print_entry(chats, 'x', 'C', name);
196}
197
198void
199chats_print(UI_CHATS_Handle *chats,
200 MESSENGER_Application *app)
201{
202 if (chats->open_dialog.window)
203 {
204 chat_open_dialog_print(&(chats->open_dialog));
205 return;
206 }
207 else if (chats->create_dialog.win)
208 {
209 lobby_create_dialog_print(&(chats->create_dialog));
210 return;
211 }
212 else if (chats->enter_dialog.window)
213 {
214 lobby_enter_dialog_print(&(chats->enter_dialog));
215 return;
216 }
217
218 if (!(chats->window))
219 return;
220
221 list_input_reset(chats);
222 werase(chats->window);
223
224 GNUNET_CHAT_iterate_groups(
225 app->chat.handle,
226 &_chats_iterate_print_group,
227 chats
228 );
229
230 GNUNET_CHAT_iterate_contacts(
231 app->chat.handle,
232 &_chats_iterate_print_contact,
233 chats
234 );
235
236 _chats_print_entry(chats, '+', '\0', "Add chat");
237 _chats_print_entry(chats, '+', '\0', "Open lobby");
238 _chats_print_entry(chats, '+', '\0', "Enter lobby");
239}
diff --git a/src/ui/chats.h b/src/ui/chats.h
new file mode 100644
index 0000000..eb0ba99
--- /dev/null
+++ b/src/ui/chats.h
@@ -0,0 +1,89 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/chats.h
23 */
24
25#ifndef UI_CHATS_H_
26#define UI_CHATS_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31#include <gnunet/gnunet_chat_lib.h>
32#include <gnunet/gnunet_util_lib.h>
33
34#include "chat_open_dialog.h"
35#include "lobby_create_dialog.h"
36#include "lobby_enter_dialog.h"
37
38struct MESSENGER_Application;
39
40/**
41 * @struct UI_CHATS_Handle
42 */
43typedef struct UI_CHATS_Handle
44{
45 WINDOW *window;
46
47 int line_prev;
48 int line_next;
49
50 int line_index;
51 int line_offset;
52 int line_selected;
53
54 struct GNUNET_CHAT_Context *selected;
55
56 UI_CHAT_OPEN_DIALOG_Handle open_dialog;
57 UI_LOBBY_CREATE_DIALOG_Handle create_dialog;
58 UI_LOBBY_ENTER_DIALOG_Handle enter_dialog;
59} UI_CHATS_Handle;
60
61#define UI_CHATS_ROWS_MIN 8
62#define UI_CHATS_COLS_MIN 30
63
64/**
65 * Processes the current key event by the view
66 * to show the list of available chats.
67 *
68 * @param[in,out] chats Chats view
69 * @param[in,out] app Application handle
70 * @param[in] key Key
71 */
72void
73chats_event(UI_CHATS_Handle *chats,
74 struct MESSENGER_Application *app,
75 int key);
76
77/**
78 * Prints the content of the view to show
79 * the list of available chats to its selected
80 * window view on screen.
81 *
82 * @param[in,out] chats Chats view
83 * @param[in] app Application handle
84 */
85void
86chats_print(UI_CHATS_Handle *chats,
87 struct MESSENGER_Application *app);
88
89#endif /* UI_CHATS_H_ */
diff --git a/src/ui/list_input.h b/src/ui/list_input.h
new file mode 100644
index 0000000..4fea1f4
--- /dev/null
+++ b/src/ui/list_input.h
@@ -0,0 +1,137 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/list_input.h
23 */
24
25#ifndef UI_LIST_INPUT_H_
26#define UI_LIST_INPUT_H_
27
28#include <stdbool.h>
29#include <stdlib.h>
30
31/**
32 * Resets the list controls.
33 */
34#define list_input_reset(list) { \
35 (list)->line_prev = 0; \
36 (list)->line_next = 0; \
37 (list)->line_index = 0; \
38 (list)->selected = 0; \
39}
40
41/**
42 * Handles the list controls selection and
43 * indices to move the selection via events.
44 */
45#define list_input_select(list, line_width, item) { \
46 const bool selected = ( \
47 ((list)->line_selected >= (list)->line_index) && \
48 ((list)->line_selected < (list)->line_index + (line_width)) \
49 ); \
50 \
51 if ((!selected) && ((list)->line_selected > (list)->line_index)) \
52 (list)->line_prev = (list)->line_index; \
53 \
54 (list)->line_index += (line_width); \
55 \
56 if (selected) { \
57 (list)->line_next = (list)->line_index; \
58 (list)->selected = item; \
59 } \
60}
61
62/**
63 * Handles the key event to move the selection
64 * of list controls and adjust its view area.
65 */
66#define list_input_event(list, key) { \
67 int count = (list)->line_index; \
68 \
69 switch (key) \
70 { \
71 case KEY_UP: \
72 { \
73 (list)->line_selected = (list)->line_prev; \
74 break; \
75 } \
76 case KEY_DOWN: \
77 { \
78 (list)->line_selected = (list)->line_next; \
79 break; \
80 } \
81 default: \
82 break; \
83 } \
84 \
85 if ((list)->line_selected < 0) \
86 (list)->line_selected = 0; \
87 else if ((list)->line_selected >= count) \
88 (list)->line_selected = count - 1; \
89 \
90 if ((list)->window) \
91 { \
92 const int height = getmaxy((list)->window); \
93 const int y = (list)->line_selected - (list)->line_offset; \
94 \
95 if (y < 0) \
96 (list)->line_offset += y; \
97 else if (y + 1 >= height) \
98 (list)->line_offset += y + 1 - height; \
99 \
100 if ((list)->line_offset < 0) \
101 (list)->line_offset = 0; \
102 else if ((list)->line_offset >= count) \
103 (list)->line_offset = count - 1; \
104 } \
105}
106
107/**
108 * Prepares for printing an item in list controls
109 * as well as providing its selection and its
110 * y location in the current view.
111 */
112#define list_input_print_(list, line_width, yes_res, no_res) \
113 const bool selected = ( \
114 ((list)->line_selected >= (list)->line_index) && \
115 ((list)->line_selected < (list)->line_index + (line_width)) \
116 ); \
117 \
118 const int y = (list)->line_index - (list)->line_offset; { \
119 \
120 (list)->line_index += (line_width); \
121 \
122 if (y + (line_width) < 1) \
123 return yes_res; \
124 \
125 const int height = getmaxy((list)->window); \
126 \
127 if (y >= height) \
128 return no_res; \
129}
130
131#define list_input_print_gnunet(list, line_width) \
132 list_input_print_(list, line_width, GNUNET_YES, GNUNET_NO)
133
134#define list_input_print(list, line_width) \
135 list_input_print_(list, line_width, , )
136
137#endif /* UI_LIST_INPUT_H_ */
diff --git a/src/ui/lobby_create_dialog.c b/src/ui/lobby_create_dialog.c
new file mode 100644
index 0000000..61c1a4c
--- /dev/null
+++ b/src/ui/lobby_create_dialog.c
@@ -0,0 +1,147 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/lobby_create_dialog.c
23 */
24
25#include "lobby_create_dialog.h"
26
27#include "list_input.h"
28#include "../application.h"
29#include "../util.h"
30
31void
32_lobby_open_with_uri(void *cls,
33 const struct GNUNET_CHAT_Uri *uri)
34{
35 UI_LOBBY_CREATE_DIALOG_Handle *create_dialog = cls;
36
37 if (create_dialog->uri)
38 GNUNET_free(create_dialog->uri);
39
40 create_dialog->uri = GNUNET_CHAT_uri_to_string(uri);
41}
42
43void
44lobby_create_dialog_event(UI_LOBBY_CREATE_DIALOG_Handle *create_dialog,
45 UNUSED struct MESSENGER_Application *app,
46 int key)
47{
48 create_dialog->window = *(create_dialog->win);
49
50 list_input_reset(create_dialog);
51
52 if (create_dialog->uri)
53 list_input_select(create_dialog, 1, 0)
54 else
55 {
56 list_input_select(create_dialog, 1, 30);
57 list_input_select(create_dialog, 1, 5 * 60);
58 list_input_select(create_dialog, 1, 60 * 60);
59 list_input_select(create_dialog, 1, 8 * 60 * 60);
60 list_input_select(create_dialog, 1, 24 * 60 * 60);
61 list_input_select(create_dialog, 1, 7 * 24 * 60 * 60);
62 list_input_select(create_dialog, 1, 4 * 7 * 60 * 60);
63 list_input_select(create_dialog, 1, 0);
64 }
65
66 switch (key)
67 {
68 case 27:
69 case KEY_EXIT:
70 if (create_dialog->lobby)
71 GNUNET_CHAT_lobby_close(create_dialog->lobby);
72
73 create_dialog->lobby = NULL;
74 create_dialog->win = NULL;
75 break;
76 case '\n':
77 case KEY_ENTER:
78 if (create_dialog->uri)
79 {
80 GNUNET_free(create_dialog->uri);
81
82 create_dialog->lobby = NULL;
83 create_dialog->win = NULL;
84 }
85 else if (!(create_dialog->lobby))
86 create_dialog->lobby = GNUNET_CHAT_lobby_open(
87 app->chat.handle,
88 GNUNET_TIME_relative_multiply(
89 GNUNET_TIME_relative_get_second_(),
90 create_dialog->selected
91 ),
92 _lobby_open_with_uri,
93 create_dialog
94 );
95
96 break;
97 default:
98 break;
99 }
100
101 if (!(create_dialog->lobby))
102 list_input_event(create_dialog, key)
103 else
104 list_input_event(create_dialog, KEY_RESIZE);
105}
106
107static void
108_lobby_iterate_print(UI_LOBBY_CREATE_DIALOG_Handle *create_dialog,
109 const char *label)
110{
111 list_input_print(create_dialog, 1);
112
113 const int attrs_select = A_BOLD;
114
115 if (selected) wattron(create_dialog->window, attrs_select);
116
117 wmove(create_dialog->window, y, 0);
118 wprintw(create_dialog->window, "%s", label);
119
120 if (selected) wattroff(create_dialog->window, attrs_select);
121}
122
123void
124lobby_create_dialog_print(UI_LOBBY_CREATE_DIALOG_Handle *create_dialog)
125{
126 if (!(create_dialog->win))
127 return;
128
129 create_dialog->window = *(create_dialog->win);
130
131 list_input_reset(create_dialog);
132 werase(create_dialog->window);
133
134 if (create_dialog->uri)
135 _lobby_iterate_print(create_dialog, create_dialog->uri);
136 else
137 {
138 _lobby_iterate_print(create_dialog, "30 seconds");
139 _lobby_iterate_print(create_dialog, "5 minutes");
140 _lobby_iterate_print(create_dialog, "1 hour");
141 _lobby_iterate_print(create_dialog, "8 hours");
142 _lobby_iterate_print(create_dialog, "1 day");
143 _lobby_iterate_print(create_dialog, "1 week");
144 _lobby_iterate_print(create_dialog, "4 weeks");
145 _lobby_iterate_print(create_dialog, "Off");
146 }
147}
diff --git a/src/ui/lobby_create_dialog.h b/src/ui/lobby_create_dialog.h
new file mode 100644
index 0000000..4ec5501
--- /dev/null
+++ b/src/ui/lobby_create_dialog.h
@@ -0,0 +1,79 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/lobby_create_dialog.h
23 */
24
25#ifndef UI_LOBBY_CREATE_DIALOG_H_
26#define UI_LOBBY_CREATE_DIALOG_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31#include <gnunet/gnunet_chat_lib.h>
32#include <gnunet/gnunet_util_lib.h>
33
34struct MESSENGER_Application;
35
36/**
37 * @struct UI_LOBBY_CREATE_DIALOG_Handle
38 */
39typedef struct UI_LOBBY_CREATE_DIALOG_Handle
40{
41 WINDOW *window;
42 WINDOW **win;
43
44 int line_prev;
45 int line_next;
46
47 int line_index;
48 int line_offset;
49 int line_selected;
50
51 uint64_t selected;
52
53 struct GNUNET_CHAT_Lobby *lobby;
54 char *uri;
55} UI_LOBBY_CREATE_DIALOG_Handle;
56
57/**
58 * Processes the current key event by the dialog
59 * to create a lobby.
60 *
61 * @param[in,out] create_dialog Lobby creation dialog
62 * @param[in,out] app Application handle
63 * @param[in] key Key
64 */
65void
66lobby_create_dialog_event(UI_LOBBY_CREATE_DIALOG_Handle *create_dialog,
67 struct MESSENGER_Application *app,
68 int key);
69
70/**
71 * Prints the content of the dialog to create a
72 * lobby to its selected window view.
73 *
74 * @param[in] create_dialog Lobby creation dialog
75 */
76void
77lobby_create_dialog_print(UI_LOBBY_CREATE_DIALOG_Handle *create_dialog);
78
79#endif /* UI_LOBBY_CREATE_DIALOG_H_ */
diff --git a/src/ui/lobby_enter_dialog.c b/src/ui/lobby_enter_dialog.c
new file mode 100644
index 0000000..a3ef356
--- /dev/null
+++ b/src/ui/lobby_enter_dialog.c
@@ -0,0 +1,101 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/lobby_enter_dialog.c
23 */
24
25#include "lobby_enter_dialog.h"
26
27#include <gnunet/gnunet_chat_lib.h>
28#include <gnunet/gnunet_util_lib.h>
29
30#include "text_input.h"
31#include "../application.h"
32
33void
34lobby_enter_dialog_event(UI_LOBBY_ENTER_DIALOG_Handle *enter_dialog,
35 struct MESSENGER_Application *app,
36 int key)
37{
38 struct GNUNET_CHAT_Uri *uri;
39
40 switch (key)
41 {
42 case 27:
43 case KEY_EXIT:
44 if (enter_dialog->error)
45 GNUNET_free(enter_dialog->error);
46
47 enter_dialog->error = NULL;
48 enter_dialog->window = NULL;
49 break;
50 case '\n':
51 case KEY_ENTER:
52 if (enter_dialog->uri_len > 0)
53 {
54 if (enter_dialog->error)
55 GNUNET_free(enter_dialog->error);
56
57 enter_dialog->error = NULL;
58 uri = GNUNET_CHAT_uri_parse(enter_dialog->uri, &(enter_dialog->error));
59
60 if (uri)
61 {
62 GNUNET_CHAT_lobby_join(app->chat.handle, uri);
63 GNUNET_CHAT_uri_destroy(uri);
64
65 enter_dialog->uri_len = 0;
66 enter_dialog->window = NULL;
67 }
68 }
69
70 break;
71 default:
72 break;
73 }
74
75 text_input_event(enter_dialog->uri, key);
76}
77
78void
79lobby_enter_dialog_print(UI_LOBBY_ENTER_DIALOG_Handle *enter_dialog)
80{
81 if (!(enter_dialog->window))
82 return;
83
84 WINDOW *window = *(enter_dialog->window);
85
86 werase(window);
87 wmove(window, 0, 0);
88
89 wprintw(window, "%s", enter_dialog->uri);
90
91 if (enter_dialog->error)
92 {
93 wmove(window, 1, 0);
94 wprintw(window, "ERROR: %s", enter_dialog->error);
95 }
96
97 wmove(window, 0, enter_dialog->uri_pos);
98
99 wcursyncup(window);
100 curs_set(1);
101}
diff --git a/src/ui/lobby_enter_dialog.h b/src/ui/lobby_enter_dialog.h
new file mode 100644
index 0000000..8e7cc05
--- /dev/null
+++ b/src/ui/lobby_enter_dialog.h
@@ -0,0 +1,69 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/lobby_enter_dialog.h
23 */
24
25#ifndef UI_LOBBY_ENTER_DIALOG_H_
26#define UI_LOBBY_ENTER_DIALOG_H_
27
28#include <stdlib.h>
29#include <curses.h>
30
31struct MESSENGER_Application;
32
33/**
34 * @struct UI_LOBBY_ENTER_DIALOG_Handle
35 */
36typedef struct UI_LOBBY_ENTER_DIALOG_Handle
37{
38 WINDOW **window;
39
40 char *error;
41
42 char uri [2048];
43 int uri_len;
44 int uri_pos;
45} UI_LOBBY_ENTER_DIALOG_Handle;
46
47/**
48 * Processes the current key event by the dialog
49 * to enter a lobby.
50 *
51 * @param[in,out] enter_dialog Lobby entry dialog
52 * @param[in,out] app Application handle
53 * @param[in] key Key
54 */
55void
56lobby_enter_dialog_event(UI_LOBBY_ENTER_DIALOG_Handle *enter_dialog,
57 struct MESSENGER_Application *app,
58 int key);
59
60/**
61 * Prints the content of the dialog to enter a
62 * lobby to its selected window view.
63 *
64 * @param[in] enter_dialog Lobby entry dialog
65 */
66void
67lobby_enter_dialog_print(UI_LOBBY_ENTER_DIALOG_Handle *enter_dialog);
68
69#endif /* UI_LOBBY_ENTER_DIALOG_H_ */
diff --git a/src/ui/members.c b/src/ui/members.c
new file mode 100644
index 0000000..7e3239f
--- /dev/null
+++ b/src/ui/members.c
@@ -0,0 +1,182 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/members.c
23 */
24
25#include "members.h"
26
27#include "list_input.h"
28#include "../application.h"
29#include "../util.h"
30
31void
32members_event(UI_MEMBERS_Handle *members,
33 struct MESSENGER_Application *app,
34 int key)
35{
36 list_input_reset(members);
37
38 UI_MEMBERS_List *element = members->head;
39 while (element)
40 {
41 list_input_select(members, 1, element->contact);
42 element = element->next;
43 }
44
45 switch (key)
46 {
47 case 27:
48 case KEY_EXIT:
49 case '\t':
50 app->chat.show_members = FALSE;
51 break;
52 case '\n':
53 case KEY_ENTER: {
54 struct GNUNET_CHAT_Context *context;
55
56 if (!(members->selected))
57 break;
58
59 context = GNUNET_CHAT_contact_get_context(members->selected);
60 GNUNET_CHAT_context_request(context);
61
62 app->chat.show_members = FALSE;
63 app->chat.context = context;
64 break;
65 }
66 default:
67 break;
68 }
69
70 list_input_event(members, key);
71}
72
73static void
74_members_iterate_print(UI_MEMBERS_Handle *members,
75 const struct GNUNET_CHAT_Contact *contact)
76{
77 list_input_print(members, 1);
78
79 const int attrs_select = A_BOLD;
80
81 if (selected) wattron(members->window, attrs_select);
82
83 wmove(members->window, y, 0);
84
85 const char *name = GNUNET_CHAT_contact_get_name(contact);
86 const char *key = GNUNET_CHAT_contact_get_key(contact);
87
88 size_t key_len = key? strlen(key) : 0;
89
90 if (key_len > 4)
91 wprintw(members->window, "[%s]: %s", key + (key_len - 4), name);
92 else
93 wprintw(members->window, "%s", name);
94
95 if (selected) wattroff(members->window, attrs_select);
96}
97
98void
99members_print(UI_MEMBERS_Handle *members)
100{
101 if (!(members->window))
102 return;
103
104 list_input_reset(members);
105 werase(members->window);
106
107 UI_MEMBERS_List *element = members->head;
108 while (element)
109 {
110 _members_iterate_print(members, element->contact);
111 element = element->next;
112 }
113}
114
115void
116members_clear(UI_MEMBERS_Handle *members)
117{
118 UI_MEMBERS_List *element;
119 while (members->head)
120 {
121 element = members->head;
122
123 GNUNET_CONTAINER_DLL_remove(
124 members->head,
125 members->tail,
126 element
127 );
128
129 GNUNET_free(element);
130 }
131
132 members->line_selected = 0;
133}
134
135bool
136members_add(UI_MEMBERS_Handle *members,
137 struct GNUNET_CHAT_Contact *contact)
138{
139 UI_MEMBERS_List *element = members->head;
140 while (element)
141 {
142 if (element->contact == contact)
143 break;
144
145 element = element->next;
146 }
147
148 if (element)
149 return FALSE;
150
151 element = GNUNET_new(UI_MEMBERS_List);
152 element->contact = contact;
153
154 GNUNET_CONTAINER_DLL_insert_tail(
155 members->head,
156 members->tail,
157 element
158 );
159
160 return TRUE;
161}
162
163void
164members_remove(UI_MEMBERS_Handle *members,
165 const struct GNUNET_CHAT_Contact *contact)
166{
167 UI_MEMBERS_List *element = members->head;
168 while (element)
169 {
170 if (element->contact == contact)
171 break;
172
173 element = element->next;
174 }
175
176 if (element)
177 GNUNET_CONTAINER_DLL_remove(
178 members->head,
179 members->tail,
180 element
181 );
182}
diff --git a/src/ui/members.h b/src/ui/members.h
new file mode 100644
index 0000000..9ff3078
--- /dev/null
+++ b/src/ui/members.h
@@ -0,0 +1,127 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/members.h
23 */
24
25#ifndef UI_MEMBERS_H_
26#define UI_MEMBERS_H_
27
28#include <stdbool.h>
29#include <stdlib.h>
30#include <curses.h>
31
32#include <gnunet/gnunet_chat_lib.h>
33#include <gnunet/gnunet_util_lib.h>
34
35struct MESSENGER_Application;
36
37/**
38 * @struct UI_MEMBERS_List
39 */
40typedef struct UI_MEMBERS_List
41{
42 struct GNUNET_CHAT_Contact *contact;
43
44 struct UI_MEMBERS_List *prev;
45 struct UI_MEMBERS_List *next;
46} UI_MEMBERS_List;
47
48/**
49 * @struct UI_MEMBERS_Handle
50 */
51typedef struct UI_MEMBERS_Handle
52{
53 WINDOW *window;
54
55 UI_MEMBERS_List *head;
56 UI_MEMBERS_List *tail;
57
58 int line_prev;
59 int line_next;
60
61 int line_index;
62 int line_offset;
63 int line_selected;
64
65 struct GNUNET_CHAT_Contact *selected;
66} UI_MEMBERS_Handle;
67
68#define UI_MEMBERS_COLS_MIN 30
69
70/**
71 * Processes the current key event by the view
72 * to show a chats list of members.
73 *
74 * @param[in,out] members Chat members view
75 * @param[in,out] app Application handle
76 * @param[in] key Key
77 */
78void
79members_event(UI_MEMBERS_Handle *members,
80 struct MESSENGER_Application *app,
81 int key);
82
83/**
84 * Prints the content of the view to show
85 * a chats list of members to its selected
86 * window view on screen.
87 *
88 * @param[in,out] members Chat members view
89 */
90void
91members_print(UI_MEMBERS_Handle *members);
92
93/**
94 * Clears the list of members the view
95 * would print to the screen.
96 *
97 * @param[out] members Chat members view
98 */
99void
100members_clear(UI_MEMBERS_Handle *members);
101
102/**
103 * Adds a new chat contact to the list of
104 * members the view will print to the
105 * screen.
106 *
107 * @param[in,out] members Chat members view
108 * @param[in] contact Chat contact
109 * @return #TRUE if the member is new, otherwise #FALSE
110 */
111bool
112members_add(UI_MEMBERS_Handle *members,
113 struct GNUNET_CHAT_Contact *contact);
114
115/**
116 * Removes a chat contact from the list of
117 * members the view would print to the
118 * screen.
119 *
120 * @param[in,out] members Chat members view
121 * @param[in] contact Chat contact
122 */
123void
124members_remove(UI_MEMBERS_Handle *members,
125 const struct GNUNET_CHAT_Contact *contact);
126
127#endif /* UI_MEMBERS_H_ */
diff --git a/src/ui/meson.build b/src/ui/meson.build
new file mode 100644
index 0000000..a57c670
--- /dev/null
+++ b/src/ui/meson.build
@@ -0,0 +1,33 @@
1#
2# This file is part of GNUnet.
3# Copyright (C) 2023 GNUnet e.V.
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20
21messenger_cli_ui_sources = files([
22 'account_create_dialog.c', 'account_create_dialog.h',
23 'accounts.c', 'accounts.h',
24 'chat_open_dialog.c', 'chat_open_dialog.h',
25 'chat.h',
26 'chats.c', 'chats.h',
27 'list_input.h',
28 'lobby_create_dialog.c', 'lobby_create_dialog.h',
29 'lobby_enter_dialog.c', 'lobby_enter_dialog.h',
30 'members.c', 'members.h',
31 'messages.c', 'messages.h',
32 'text_input.h',
33])
diff --git a/src/ui/messages.c b/src/ui/messages.c
new file mode 100644
index 0000000..d52ae5e
--- /dev/null
+++ b/src/ui/messages.c
@@ -0,0 +1,456 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/messages.c
23 */
24
25#include "messages.h"
26
27#include "list_input.h"
28#include "text_input.h"
29#include "../application.h"
30#include "../util.h"
31#include <gnunet/gnunet_chat_lib.h>
32#include <gnunet/gnunet_common.h>
33
34struct tm*
35_messages_new_day(time_t* current_time,
36 const time_t* timestamp)
37{
38 struct tm* ts = localtime(timestamp);
39
40 ts->tm_sec = 0;
41 ts->tm_min = 0;
42 ts->tm_hour = 0;
43
44 const time_t date_time = timelocal(ts);
45
46 if (date_time <= *current_time) {
47 return NULL;
48 }
49
50 *current_time = date_time;
51 return ts;
52}
53
54void
55_messages_handle_message(UI_MESSAGES_Handle *messages)
56{
57 switch (GNUNET_CHAT_message_get_kind(messages->selected))
58 {
59 case GNUNET_CHAT_KIND_INVITATION:
60 {
61 struct GNUNET_CHAT_Invitation *invitation = (
62 GNUNET_CHAT_message_get_invitation(messages->selected)
63 );
64
65 if (invitation)
66 GNUNET_CHAT_invitation_accept(invitation);
67 break;
68 }
69 case GNUNET_CHAT_KIND_FILE:
70 {
71 struct GNUNET_CHAT_File *file = GNUNET_CHAT_message_get_file(
72 messages->selected
73 );
74
75 if ((file) && (GNUNET_YES != GNUNET_CHAT_file_is_downloading(file)))
76 GNUNET_CHAT_file_start_download(file, NULL, NULL);
77 break;
78 default:
79 break;
80 }
81 }
82}
83
84void
85messages_event(UI_MESSAGES_Handle *messages,
86 MESSENGER_Application *app,
87 int key)
88{
89 list_input_reset(messages);
90 messages->line_time = 0;
91
92 UI_MESSAGES_List *element = messages->head;
93 while (element)
94 {
95 struct tm *ts = _messages_new_day(
96 &(messages->line_time),
97 &(element->timestamp)
98 );
99
100 list_input_select(messages, ts? 2 : 1, element->message);
101 element = element->next;
102 }
103
104 list_input_select(messages, 1, NULL);
105
106 switch (key)
107 {
108 case 27:
109 case KEY_EXIT:
110 app->chat.context = NULL;
111 break;
112 case '\t':
113 app->chat.show_members = TRUE;
114 break;
115 case '\n':
116 case KEY_ENTER:
117 if (messages->selected)
118 _messages_handle_message(messages);
119 else if (messages->text_len > 0)
120 {
121 if (0 != strncmp(messages->text,
122 UI_MESSAGES_FILE_PREFIX,
123 UI_MESSAGES_FILE_PREFIX_LEN))
124 goto write_text;
125
126 const char* filename = messages->text + 5;
127
128 if (0 != access(filename, R_OK | F_OK))
129 break;
130
131 GNUNET_CHAT_context_send_file(
132 app->chat.context,
133 filename,
134 NULL,
135 NULL
136 );
137
138 goto drop_text;
139
140 write_text:
141 GNUNET_CHAT_context_send_text(
142 app->chat.context,
143 messages->text
144 );
145
146 drop_text:
147 messages->text_len = 0;
148 }
149 break;
150 case KEY_BACKSPACE:
151 if (messages->selected)
152 GNUNET_CHAT_message_delete(
153 messages->selected,
154 GNUNET_TIME_relative_get_zero_()
155 );
156 break;
157 default:
158 break;
159 }
160
161 if (!(messages->selected))
162 text_input_event(messages->text, key);
163
164 list_input_event(messages, key);
165}
166
167void
168_messages_iterate_print(UI_MESSAGES_Handle *messages,
169 const time_t* timestamp,
170 const struct GNUNET_CHAT_Message *message)
171{
172 static const char *you = "you";
173
174 enum GNUNET_CHAT_MessageKind kind = GNUNET_CHAT_message_get_kind(message);
175
176 struct GNUNET_CHAT_Contact *sender = GNUNET_CHAT_message_get_sender(message);
177 struct GNUNET_CHAT_Contact *recipient = GNUNET_CHAT_message_get_recipient(message);
178
179 enum GNUNET_GenericReturnValue sent = GNUNET_CHAT_message_is_sent(message);
180 const char *msg_s = GNUNET_YES == sent? "" : "s";
181
182 enum GNUNET_GenericReturnValue recv = recipient?
183 GNUNET_CHAT_contact_is_owned(recipient) : GNUNET_NO;
184
185 const char *name = GNUNET_YES == sent? you : (
186 sender? GNUNET_CHAT_contact_get_name(sender) : NULL
187 );
188
189 const char *rcp = GNUNET_YES == recv? you : (
190 recipient? GNUNET_CHAT_contact_get_name(recipient) : you
191 );
192
193 const char *text = GNUNET_CHAT_message_get_text(message);
194
195 const struct GNUNET_CHAT_File *file = GNUNET_CHAT_message_get_file(message);
196
197 struct tm* ts = localtime(timestamp);
198 char time_buf [255];
199
200 strftime(time_buf, sizeof(time_buf), "%H:%M", ts);
201
202 ts = _messages_new_day(&(messages->line_time), timestamp);
203
204 list_input_print(messages, ts? 2 : 1);
205 wmove(messages->window, y, 0);
206
207 if (ts) {
208 char date_buf [255];
209
210 strftime(date_buf, sizeof(date_buf), "%x", ts);
211
212 const int width = getmaxx(messages->window);
213
214 whline(messages->window, '-', width);
215 wmove(messages->window, y, 8);
216
217 wprintw(messages->window, " %s ", date_buf);
218 wmove(messages->window, y+1, 0);
219 }
220
221 const int attrs_select = A_BOLD;
222
223 if (selected) wattron(messages->window, attrs_select);
224
225 wprintw(messages->window, " %s | ", time_buf);
226
227 switch (kind) {
228 case GNUNET_CHAT_KIND_JOIN:
229 wprintw(
230 messages->window,
231 "%s join%s the room.",
232 name,
233 msg_s
234 );
235 break;
236 case GNUNET_CHAT_KIND_LEAVE:
237 wprintw(
238 messages->window,
239 "%s leave%s the room.",
240 name,
241 msg_s
242 );
243 break;
244 case GNUNET_CHAT_KIND_INVITATION:
245 wprintw(
246 messages->window,
247 "%s invite%s %s to a room.",
248 name,
249 msg_s,
250 rcp
251 );
252 break;
253 case GNUNET_CHAT_KIND_TEXT:
254 wprintw(
255 messages->window,
256 "%s: %s",
257 name,
258 text
259 );
260 break;
261 case GNUNET_CHAT_KIND_FILE: {
262 const char *filename = GNUNET_CHAT_file_get_name(file);
263
264 const uint64_t localsize = GNUNET_CHAT_file_get_local_size(file);
265 const uint64_t filesize = GNUNET_CHAT_file_get_size(file);
266
267 wprintw(
268 messages->window,
269 "%s share%s the file '%s' (%lu / %lu).",
270 name,
271 msg_s,
272 filename,
273 localsize,
274 filesize
275 );
276 break;
277 }
278 default:
279 wprintw(
280 messages->window,
281 "[%d] %s: %s",
282 (int) kind,
283 name,
284 text
285 );
286 break;
287 }
288
289 if (selected) wattroff(messages->window, attrs_select);
290}
291
292void
293messages_print(UI_MESSAGES_Handle *messages)
294{
295 if (!(messages->window))
296 return;
297
298 list_input_reset(messages);
299 messages->line_time = 0;
300
301 werase(messages->window);
302
303 UI_MESSAGES_List *element = messages->head;
304 while (element)
305 {
306 _messages_iterate_print(messages, &(element->timestamp), element->message);
307 element = element->next;
308 }
309
310 const int count = messages->line_index;
311 const bool selected = (count == messages->line_selected);
312
313 const int width = getmaxx(messages->window);
314 const int height = getmaxy(messages->window);
315 const int line_height = height - 2;
316
317 wmove(messages->window, line_height, 0);
318 whline(messages->window, '-', width);
319
320 const bool is_file_text = (0 == strncmp(
321 messages->text,
322 UI_MESSAGES_FILE_PREFIX,
323 UI_MESSAGES_FILE_PREFIX_LEN
324 ));
325
326 const int attrs_select = A_BOLD | (is_file_text? A_ITALIC : A_NORMAL);
327
328 if (selected) wattron(messages->window, attrs_select);
329
330 wmove(messages->window, height - 1, 0);
331 wprintw(messages->window, "%s", messages->text);
332
333 if (selected) wattroff(messages->window, attrs_select);
334
335 wmove(messages->window, height - 1, messages->text_pos);
336
337 if (selected)
338 {
339 wcursyncup(messages->window);
340 curs_set(1);
341 }
342}
343
344void
345messages_clear(UI_MESSAGES_Handle *messages)
346{
347 UI_MESSAGES_List *element;
348 while (messages->head)
349 {
350 element = messages->head;
351
352 GNUNET_CONTAINER_DLL_remove(
353 messages->head,
354 messages->tail,
355 element
356 );
357
358 GNUNET_free(element);
359 }
360}
361
362static int
363_message_compare_timestamps(UNUSED void *cls,
364 UI_MESSAGES_List *list0,
365 UI_MESSAGES_List *list1)
366{
367 if ((!list0) || (!list1))
368 return 0;
369
370 if (list0->timestamp > list1->timestamp)
371 return -1;
372 else if (list0->timestamp < list1->timestamp)
373 return +1;
374 else
375 return 0;
376}
377
378void
379messages_add(UI_MESSAGES_Handle *messages,
380 const struct GNUNET_CHAT_Message *message)
381{
382 enum GNUNET_CHAT_MessageKind kind = GNUNET_CHAT_message_get_kind(message);
383
384 switch (kind) {
385 case GNUNET_CHAT_KIND_UPDATE_CONTEXT:
386 case GNUNET_CHAT_KIND_CONTACT:
387 case GNUNET_CHAT_KIND_DELETION:
388 return;
389 default:
390 break;
391 }
392
393 list_input_reset(messages);
394 messages->line_time = 0;
395
396 UI_MESSAGES_List *element = messages->head;
397 while (element)
398 {
399 struct tm *ts = _messages_new_day(
400 &(messages->line_time),
401 &(element->timestamp)
402 );
403
404 list_input_select(messages, ts? 2 : 1, element->message);
405 element = element->next;
406 }
407
408 list_input_select(messages, 1, NULL);
409
410 const struct GNUNET_TIME_Absolute abs_time = (
411 GNUNET_CHAT_message_get_timestamp(message)
412 );
413
414 const struct GNUNET_TIME_Timestamp timestamp = (
415 GNUNET_TIME_absolute_to_timestamp(abs_time)
416 );
417
418 element = GNUNET_new(UI_MESSAGES_List);
419 element->timestamp = GNUNET_TIME_timestamp_to_s(timestamp);
420 element->message = message;
421
422 GNUNET_CONTAINER_DLL_insert_sorted(
423 UI_MESSAGES_List,
424 _message_compare_timestamps,
425 NULL,
426 messages->head,
427 messages->tail,
428 element
429 );
430
431 list_input_select(messages, 1, NULL);
432
433 if (!(messages->selected))
434 list_input_event(messages, KEY_DOWN);
435}
436
437void
438messages_remove(UI_MESSAGES_Handle *messages,
439 const struct GNUNET_CHAT_Message *message)
440{
441 UI_MESSAGES_List *element = messages->head;
442 while (element)
443 {
444 if (element->message == message)
445 break;
446
447 element = element->next;
448 }
449
450 if (element)
451 GNUNET_CONTAINER_DLL_remove(
452 messages->head,
453 messages->tail,
454 element
455 );
456}
diff --git a/src/ui/messages.h b/src/ui/messages.h
new file mode 100644
index 0000000..bcb2336
--- /dev/null
+++ b/src/ui/messages.h
@@ -0,0 +1,139 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2023 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/messages.h
23 */
24
25#ifndef UI_MESSAGES_H_
26#define UI_MESSAGES_H_
27
28#include <stdlib.h>
29#include <time.h>
30#include <unistd.h>
31#include <curses.h>
32
33#include <gnunet/gnunet_chat_lib.h>
34#include <gnunet/gnunet_util_lib.h>
35
36struct MESSENGER_Application;
37
38/**
39 * @struct UI_MESSAGES_List
40 */
41typedef struct UI_MESSAGES_List
42{
43 time_t timestamp;
44
45 const struct GNUNET_CHAT_Message *message;
46
47 struct UI_MESSAGES_List *prev;
48 struct UI_MESSAGES_List *next;
49} UI_MESSAGES_List;
50
51#define TEXT_LEN_MAX 1024
52
53/**
54 * @struct UI_MESSAGES_Handle
55 */
56typedef struct UI_MESSAGES_Handle
57{
58 WINDOW *window;
59
60 UI_MESSAGES_List *head;
61 UI_MESSAGES_List *tail;
62
63 int line_prev;
64 int line_next;
65
66 int line_index;
67 int line_offset;
68 int line_selected;
69 time_t line_time;
70
71 const struct GNUNET_CHAT_Message *selected;
72
73 char text [1024];
74 int text_len;
75 int text_pos;
76} UI_MESSAGES_Handle;
77
78#define UI_MESSAGES_COLS_MIN 50
79
80#define UI_MESSAGES_FILE_PREFIX "file:"
81#define UI_MESSAGES_FILE_PREFIX_LEN 5
82
83/**
84 * Processes the current key event by the view
85 * to show a chats list of messages.
86 *
87 * @param[in,out] messages Chat messages view
88 * @param[in,out] app Application handle
89 * @param[in] key Key
90 */
91void
92messages_event(UI_MESSAGES_Handle *messages,
93 struct MESSENGER_Application *app,
94 int key);
95
96/**
97 * Prints the content of the view to show
98 * a chats list of messages to its selected
99 * window view on screen.
100 *
101 * @param[in,out] messages Chat messages view
102 */
103void
104messages_print(UI_MESSAGES_Handle *messages);
105
106/**
107 * Clears the list of messages the view
108 * would print to the screen.
109 *
110 * @param[out] messages Chat messages view
111 */
112void
113messages_clear(UI_MESSAGES_Handle *messages);
114
115/**
116 * Adds a new chat message to the list of
117 * messages the view will print to the
118 * screen.
119 *
120 * @param[in,out] messages Chat messages view
121 * @param[in] message Chat message
122 */
123void
124messages_add(UI_MESSAGES_Handle *messages,
125 const struct GNUNET_CHAT_Message *message);
126
127/**
128 * Removes a chat message from the list of
129 * messages the view would print to the
130 * screen.
131 *
132 * @param[in,out] messages Chat messages view
133 * @param[in] message Chat message
134 */
135void
136messages_remove(UI_MESSAGES_Handle *messages,
137 const struct GNUNET_CHAT_Message *message);
138
139#endif /* UI_MESSAGES_H_ */
diff --git a/src/ui/text_input.h b/src/ui/text_input.h
new file mode 100644
index 0000000..ec04ed5
--- /dev/null
+++ b/src/ui/text_input.h
@@ -0,0 +1,89 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file ui/text_input.h
23 */
24
25#ifndef UI_TEXT_INPUT_H_
26#define UI_TEXT_INPUT_H_
27
28#include <ctype.h>
29
30/**
31 * Handles the key event to move the cursor
32 * and adjust the content of text controls.
33 */
34#define text_input_event(text, key) { \
35 switch (key) \
36 { \
37 case KEY_LEFT: \
38 { \
39 text##_pos--; \
40 break; \
41 } \
42 case KEY_RIGHT: \
43 { \
44 text##_pos++; \
45 break; \
46 } \
47 case KEY_BACKSPACE: \
48 { \
49 if ((text##_pos < text##_len) && (text##_pos > 0)) \
50 for (int i = text##_pos; i < text##_len; i++) \
51 text[i - 1] = text[i]; \
52 \
53 if ((text##_pos > 0) && (text##_len > 0)) \
54 { \
55 text##_pos--; \
56 text##_len--; \
57 } \
58 \
59 break; \
60 } \
61 default: \
62 { \
63 if (!isprint(key)) \
64 break; \
65 \
66 for (int i = text##_len - 1; i >= text##_pos; i--) \
67 text[i + 1] = text[i]; \
68 \
69 text[text##_pos++] = (char) key; \
70 text##_len++; \
71 break; \
72 } \
73 } \
74 \
75 int max_len = (int) sizeof(text) - 1; \
76 \
77 if (text##_len > max_len) \
78 text##_len = max_len; \
79 \
80 text[text##_len] = '\0'; \
81 \
82 if (text##_pos < 0) \
83 text##_pos = 0; \
84 \
85 if (text##_pos > text##_len) \
86 text##_pos = text##_len; \
87}
88
89#endif /* UI_TEXT_INPUT_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..89155c8
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,59 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file util.c
23 */
24
25#include "util.h"
26#include <string.h>
27
28void
29util_print_logo(WINDOW *window)
30{
31 int x = getcurx(window);
32 int y = getcury(window);
33
34 wmove(window, y++, x); wprintw(window, " ");
35 wmove(window, y++, x); wprintw(window, " o/ \\o ");
36 wmove(window, y++, x); wprintw(window, " ooo oo ");
37 wmove(window, y++, x); wprintw(window, " \\oooo\\ /oooo/ ");
38 wmove(window, y++, x); wprintw(window, " oo ooo ");
39 wmove(window, y++, x); wprintw(window, " oo ooo ");
40 wmove(window, y++, x); wprintw(window, " ooooooo ");
41 wmove(window, y++, x); wprintw(window, " \\oooo/ ");
42 wmove(window, y++, x); wprintw(window, " oooo ");
43 wmove(window, y++, x); wprintw(window, " ");
44}
45
46void
47util_print_info(WINDOW *window,
48 const char *info)
49{
50 const int x = getmaxx(window) - strlen(info) - 1;
51 const int y = getcury(window);
52
53 if ((x + (y - 2) / 2 < UTIL_LOGO_COLS - 7) ||
54 ((y < 4) && (x < UTIL_LOGO_COLS)))
55 return;
56
57 wmove(window, y, x);
58 wprintw(window, "%s", info);
59}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..d1126a9
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,57 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022--2024 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @author Tobias Frisch
22 * @file util.h
23 */
24
25#ifndef UTIL_H_
26#define UTIL_H_
27
28#include <stdbool.h>
29#include <stdlib.h>
30#include <curses.h>
31
32#define UNUSED __attribute__((unused))
33
34#define UTIL_LOGO_ROWS 10
35#define UTIL_LOGO_COLS 28
36
37/**
38 * Prints the main logo of the application
39 * onto a specified view.
40 *
41 * @param[in,out] window Window view
42 */
43void
44util_print_logo(WINDOW *window);
45
46/**
47 * Print information on the right side of
48 * the application besides the main logo.
49 *
50 * @param[in,out] window Window view
51 * @param[in] info Information
52 */
53void
54util_print_info(WINDOW *window,
55 const char *info);
56
57#endif /* UTIL_H_ */