aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore22
-rw-r--r--AUTHORS1
-rw-r--r--ChangeLog10
-rw-r--r--Makefile54
-rw-r--r--README.md51
-rw-r--r--contrib/distribute_version.sh4
-rw-r--r--contrib/get_version.sh18
-rw-r--r--meson.build52
-rw-r--r--resources/logo.asc10
-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
38 files changed, 3520 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore
index 64b3197..8f8cabf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,32 @@
1.version
2
3aclocal.m4
4autom4te.cache/
5build-aux/
6
7config.log
8config.status
9configure
10configure~
11
12libtool
13m4/
14
15Makefile.in
16Makefile
17
1# IDE specific files: 18# IDE specific files:
2.cproject 19.cproject
3.project 20.project
4.settings/ 21.settings/
22.cache/
23.vscode/
5 24
6# Binary files: 25# Binary files:
7*.o 26*.o
8 27
9# Application file: 28# Application file:
10messenger-cli 29messenger-cli
30
31# Tar files
32messenger-cli-*.tar.gz
diff --git a/AUTHORS b/AUTHORS
index 3bff6be..cffc383 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,2 @@
1Tobias Frisch <thejackimonster@gmail.com> 1Tobias Frisch <thejackimonster@gmail.com>
2Marcos Marado <mindboosternoori@gmail.com>
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..ef9fb5e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,10 @@
1## Version 0.1.1
2* Replaces build system finally with Meson increasing platform support
3* Fixes issues regarding changes in header usage from GNUnet 0.19.0
4
5## Version 0.1.0
6* It is possible to create direct chats and group chats via lobbies, shared keys or invitations
7* Members of a chats can be observed
8* Chats allow sending text messages or files
9* Messages can be deleted in any chat locally
10* Switching between different accounts can be done during runtime
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 364c1fc..0000000
--- a/Makefile
+++ /dev/null
@@ -1,54 +0,0 @@
1
2SOURCE_DIR = src/
3INSTALL_DIR = /usr/local/
4
5BINARY = messenger-cli
6SOURCES = messenger_cli.c
7HEADERS =
8
9LIBRARIES = gnunetchat
10
11CC = gcc
12LD = gcc
13RM = rm
14
15CFLAGS = -pedantic -Wall -Wextra -march=native -ggdb3
16LDFLAGS =
17
18DEBUGFLAGS = -O0 -D _DEBUG
19RELEASEFLAGS = -O2 -D NDEBUG -fwhole-program
20
21SOURCE_FILES = $(addprefix $(SOURCE_DIR), $(SOURCES))
22OBJECT_FILES = $(SOURCE_FILES:%.c=%.o)
23HEADER_FILES = $(addprefix $(SOURCE_DIR), $(HEADERS))
24LIBRARY_FLAGS = $(addprefix -l, $(LIBRARIES))
25
26all: $(BINARY)
27
28debug: CFLAGS += $(DEBUGFLAGS)
29debug: $(BINARY)
30
31release: CFLAGS += $(RELEASEFLAGS)
32release: $(BINARY)
33
34%.o: %.c
35 $(CC) $(CFLAGS) -c $< -o $@ $(LIBRARY_FLAGS)
36
37$(BINARY): $(OBJECT_FILES)
38 $(LD) $(LDFLAGS) $^ -o $@ $(LIBRARY_FLAGS)
39
40.PHONY: install
41
42install:
43 install -m 755 $(BINARY) $(addprefix $(INSTALL_DIR), bin/)
44
45.PHONY: uninstall
46
47uninstall:
48 $(RM) -f $(addsuffix $(BINARY), $(addprefix $(INSTALL_DIR), bin/))
49
50.PHONY: clean
51
52clean:
53 $(RM) -f $(BINARY)
54 $(RM) -f $(OBJECT_FILES) \ No newline at end of file
diff --git a/README.md b/README.md
index 7057794..4a5bf35 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,51 @@
2 2
3A CLI for the Messenger service of GNUnet. 3A CLI for the Messenger service of GNUnet.
4 4
5## (Planned) Dependencies 5```
6
7 o o
8 ooo oo
9 ooooo ooooo
10 oo ooo
11 oo ooo
12 ooooooo
13 oooooo
14 oooo
15
16
17```
18
19## Build & Installation
20
21The following dependencies are required and need to be installed to build the application:
22
23 - [gnunet](https://git.gnunet.org/gnunet.git/): For using general GNUnet datatypes
24 - [libgnunetchat](https://git.gnunet.org/libgnunetchat.git/): For chatting via GNUnet messenger
25 - [ncurses](https://www.gnu.org/software/ncurses/): For the general UI visualization
26
27Then you can simply use [Meson](https://mesonbuild.com/) as follows:
28```
29meson build # Configure the build files for your system
30ninja -C build # Build the application using those build files
31ninja -C build install # Install the application
32```
33
34Here is a list of some useful build commands using Meson and [Ninja](https://ninja-build.org/):
35
36 - `meson compile -C build` to just compile everything with configured parameters
37 - `rm -r build` to cleanup build files in case you want to recompile
38 - `meson install -C build` to install the compiled files (you might need sudo permissions to install)
39 - `meson dist -C build` to create a tar file for distribution
40
41If you want to change the installation location, use the `--prefix=` parameter in the initial meson command. Also you can enable optimized release builds by adding `--buildtype=release` as parameter.
42
43## Contribution
44
45If you want to contribute to this project as well, the following options are available:
46
47 - Contribute directly to the [source code](https://git.gnunet.org/messenger-cli.git/) with patches to fix issues, implement new features or improve the usability.
48 - Open issues in the [bug tracker](https://bugs.gnunet.org/bug_report_page.php) to report bugs, issues or missing features.
49 - Contact the authors of the software if you need any help to contribute (testing is always an option).
50
51The list of all previous authors can be viewed in the provided [file](AUTHORS).
6 52
7 - [gnunet](https://git.gnunet.org/gnunet.git/)
8 - [libgnunetchat](https://git.gnunet.org/libgnunetchat.git/)
9 - [ncurses](https://www.gnu.org/software/ncurses/)
diff --git a/contrib/distribute_version.sh b/contrib/distribute_version.sh
new file mode 100644
index 0000000..110d311
--- /dev/null
+++ b/contrib/distribute_version.sh
@@ -0,0 +1,4 @@
1#!/bin/sh
2VERSION=$1
3cd "${MESON_DIST_ROOT}"
4echo $VERSION > .version \ No newline at end of file
diff --git a/contrib/get_version.sh b/contrib/get_version.sh
new file mode 100644
index 0000000..2a41ed4
--- /dev/null
+++ b/contrib/get_version.sh
@@ -0,0 +1,18 @@
1#!/bin/sh
2# Gets the version number from git, or from the contents of .version
3VERSION=
4if test -f ".version"
5then
6 VERSION=$(cat .version)
7fi
8if test -d "./.git"
9then
10 VERSION=$(git describe --tags)
11 VERSION=${VERSION#v}
12 echo $VERSION > .version
13fi
14if test "x$VERSION" = "x"
15then
16 VERSION="unknown"
17fi
18echo "$VERSION"
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..d46f2f9
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,52 @@
1#
2# This file is part of GNUnet.
3# Copyright (C) 2023--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
21project(
22 'messenger-cli',
23 'c',
24 license: 'AGPL3.0-or-later',
25 version: run_command('contrib/get_version.sh').stdout().strip(),
26)
27
28meson.add_dist_script('contrib/distribute_version.sh', meson.project_version())
29
30src_directory = include_directories('src')
31
32messenger_cli_deps = [
33 dependency('gnunetchat'),
34 dependency('gnunetutil'),
35 dependency('ncurses'),
36]
37
38messenger_cli_args = [
39 '-DMESSENGER_CLI_BINARY="@0@"'.format(meson.project_name()),
40 '-DMESSENGER_CLI_VERSION="@0@"'.format(meson.project_version()),
41]
42
43subdir('src')
44
45messenger_cli_exec = executable(
46 'messenger-cli',
47 messenger_cli_sources,
48 c_args: messenger_cli_args,
49 install: true,
50 dependencies: messenger_cli_deps,
51 include_directories: src_directory,
52)
diff --git a/resources/logo.asc b/resources/logo.asc
new file mode 100644
index 0000000..ddc43ad
--- /dev/null
+++ b/resources/logo.asc
@@ -0,0 +1,10 @@
1
2 o o
3 ooo oo
4 ooooo ooooo
5 oo ooo
6 oo ooo
7 ooooooo
8 oooooo
9 oooo
10
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..1dea885
--- /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:
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_ */