new_lobby.c (10079B)
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/new_lobby.c 23 */ 24 25 #include "new_lobby.h" 26 27 #include "../application.h" 28 #include "../ui.h" 29 #include <gnunet/gnunet_chat_lib.h> 30 31 static void 32 handle_warning_info_bar_close(GtkInfoBar *info_bar, 33 UNUSED gpointer user_data) 34 { 35 g_assert(info_bar); 36 37 gtk_info_bar_set_revealed(info_bar, FALSE); 38 } 39 40 static void 41 handle_warning_info_bar_response(GtkInfoBar *info_bar, 42 UNUSED int response_id, 43 UNUSED gpointer user_data) 44 { 45 g_assert(info_bar); 46 47 gtk_info_bar_set_revealed(info_bar, FALSE); 48 } 49 50 static void 51 handle_cancel_button_click(UNUSED GtkButton *button, 52 gpointer user_data) 53 { 54 g_assert(user_data); 55 56 GtkDialog *dialog = GTK_DIALOG(user_data); 57 gtk_window_close(GTK_WINDOW(dialog)); 58 } 59 60 void 61 handle_lobby_opened_and_uri_generated(void *cls, 62 const struct GNUNET_CHAT_Uri *uri) 63 { 64 g_assert(cls); 65 66 MESSENGER_Application *app = (MESSENGER_Application*) cls; 67 68 if (app->ui.new_lobby.qr) 69 QRcode_free(app->ui.new_lobby.qr); 70 71 if (!uri) 72 { 73 if (app->ui.new_lobby.preview_stack) 74 gtk_stack_set_visible_child( 75 app->ui.new_lobby.preview_stack, 76 app->ui.new_lobby.fail_box 77 ); 78 79 app->ui.new_lobby.qr = NULL; 80 return; 81 } 82 83 gchar *uri_string = GNUNET_CHAT_uri_to_string(uri); 84 85 app->ui.new_lobby.qr = QRcode_encodeString( 86 uri_string, 87 0, 88 QR_ECLEVEL_L, 89 QR_MODE_8, 90 0 91 ); 92 93 if (app->ui.new_lobby.id_drawing_area) 94 gtk_widget_queue_draw(GTK_WIDGET(app->ui.new_lobby.id_drawing_area)); 95 96 if (app->ui.new_lobby.preview_stack) 97 gtk_stack_set_visible_child( 98 app->ui.new_lobby.preview_stack, 99 GTK_WIDGET(app->ui.new_lobby.id_drawing_area) 100 ); 101 102 if (app->ui.new_lobby.id_entry) 103 gtk_entry_set_text(app->ui.new_lobby.id_entry, uri_string); 104 105 GNUNET_free(uri_string); 106 107 if (!(app->ui.new_lobby.id_entry)) 108 return; 109 110 const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); 111 112 gtk_widget_set_sensitive( 113 GTK_WIDGET(app->ui.new_lobby.copy_button), 114 id_length > 0? TRUE : FALSE 115 ); 116 } 117 118 static void 119 handle_generate_button_click(UNUSED GtkButton *button, 120 gpointer user_data) 121 { 122 g_assert(user_data); 123 124 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 125 126 GtkTreeModel *model = gtk_combo_box_get_model( 127 app->ui.new_lobby.expiration_combo_box 128 ); 129 130 gulong delay = 0; 131 132 GtkTreeIter iter; 133 if (gtk_combo_box_get_active_iter(app->ui.new_lobby.expiration_combo_box, &iter)) 134 gtk_tree_model_get(model, &iter, 1, &delay, -1); 135 136 gtk_stack_set_visible_child( 137 app->ui.new_lobby.preview_stack, 138 GTK_WIDGET(app->ui.new_lobby.loading_spinner) 139 ); 140 141 gtk_stack_set_visible_child( 142 app->ui.new_lobby.stack, 143 app->ui.new_lobby.copy_box 144 ); 145 146 gtk_widget_set_sensitive(GTK_WIDGET(app->ui.new_lobby.copy_button), FALSE); 147 148 gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.generate_button), FALSE); 149 gtk_widget_set_visible(GTK_WIDGET(app->ui.new_lobby.copy_button), TRUE); 150 151 application_chat_lock(app); 152 GNUNET_CHAT_lobby_open( 153 app->chat.messenger.handle, 154 GNUNET_CHAT_LOBBY_TYPE_UNKNOWN, 155 delay, 156 handle_lobby_opened_and_uri_generated, 157 app 158 ); 159 application_chat_unlock(app); 160 } 161 162 static void 163 handle_copy_button_click(UNUSED GtkButton *button, 164 gpointer user_data) 165 { 166 g_assert(user_data); 167 168 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 169 170 const gint id_length = gtk_entry_get_text_length(app->ui.new_lobby.id_entry); 171 const gchar *id_text = gtk_entry_get_text(app->ui.new_lobby.id_entry); 172 173 GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 174 175 if ((clipboard) && (id_length > 0)) 176 gtk_clipboard_set_text(clipboard, id_text, id_length); 177 178 gtk_window_close(GTK_WINDOW(app->ui.new_lobby.dialog)); 179 } 180 181 static void 182 handle_dialog_destroy(UNUSED GtkWidget *window, 183 gpointer user_data) 184 { 185 g_assert(user_data); 186 187 ui_new_lobby_dialog_cleanup((UI_NEW_LOBBY_Handle*) user_data); 188 } 189 190 static gboolean 191 handle_id_drawing_area_draw(GtkWidget* drawing_area, 192 cairo_t* cairo, 193 gpointer user_data) 194 { 195 g_assert((drawing_area) && (cairo) && (user_data)); 196 197 UI_NEW_LOBBY_Handle *handle = (UI_NEW_LOBBY_Handle*) user_data; 198 199 GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); 200 201 if (!context) 202 return FALSE; 203 204 const guint width = gtk_widget_get_allocated_width(drawing_area); 205 const guint height = gtk_widget_get_allocated_height(drawing_area); 206 207 gtk_render_background(context, cairo, 0, 0, width, height); 208 209 if ((!(handle->qr)) || (handle->qr->width <= 0)) 210 return FALSE; 211 212 const guint m = 3; 213 const guint w = handle->qr->width; 214 const guint w2 = w + m * 2; 215 216 guchar *pixels = (guchar*) g_malloc(sizeof(guchar) * w2 * w2 * 3); 217 218 guint x, y, z; 219 for (y = 0; y < w2; y++) 220 for (x = 0; x < w2; x++) 221 { 222 guchar value; 223 224 if ((x >= m) && (y >= m) && (x - m < w) && (y - m < w)) 225 value = ((handle->qr->data[(y - m) * w + x - m]) & 1); 226 else 227 value = 0; 228 229 for (z = 0; z < 3; z++) 230 pixels[(y * w2 + x) * 3 + z] = value? 0x00 : 0xff; 231 } 232 233 GdkPixbuf *image = gdk_pixbuf_new_from_data( 234 pixels, 235 GDK_COLORSPACE_RGB, 236 FALSE, 237 8, 238 w2, 239 w2, 240 w2 * 3, 241 NULL, 242 NULL 243 ); 244 245 if (!image) 246 return FALSE; 247 248 int dwidth = gdk_pixbuf_get_width(image); 249 int dheight = gdk_pixbuf_get_height(image); 250 251 double ratio_width = 1.0 * width / dwidth; 252 double ratio_height = 1.0 * height / dheight; 253 254 const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; 255 256 dwidth = (int) (dwidth * ratio); 257 dheight = (int) (dheight * ratio); 258 259 double dx = (width - dwidth) * 0.5; 260 double dy = (height - dheight) * 0.5; 261 262 const int interp_type = (ratio >= 1.0? 263 GDK_INTERP_NEAREST : 264 GDK_INTERP_BILINEAR 265 ); 266 267 GdkPixbuf* scaled = gdk_pixbuf_scale_simple( 268 image, 269 dwidth, 270 dheight, 271 interp_type 272 ); 273 274 gtk_render_icon(context, cairo, scaled, dx, dy); 275 276 cairo_fill(cairo); 277 278 g_object_unref(scaled); 279 g_object_unref(image); 280 281 g_free(pixels); 282 283 return FALSE; 284 } 285 286 void 287 ui_new_lobby_dialog_init(MESSENGER_Application *app, 288 UI_NEW_LOBBY_Handle *handle) 289 { 290 g_assert((app) && (handle)); 291 292 handle->builder = ui_builder_from_resource( 293 application_get_resource_path(app, "ui/new_lobby.ui") 294 ); 295 296 handle->dialog = GTK_DIALOG( 297 gtk_builder_get_object(handle->builder, "new_lobby_dialog") 298 ); 299 300 gtk_window_set_transient_for( 301 GTK_WINDOW(handle->dialog), 302 GTK_WINDOW(app->ui.messenger.main_window) 303 ); 304 305 handle->warning_info_bar = GTK_INFO_BAR( 306 gtk_builder_get_object(handle->builder, "warning_info_bar") 307 ); 308 309 g_signal_connect( 310 handle->warning_info_bar, 311 "close", 312 G_CALLBACK(handle_warning_info_bar_close), 313 NULL 314 ); 315 316 g_signal_connect( 317 handle->warning_info_bar, 318 "response", 319 G_CALLBACK(handle_warning_info_bar_response), 320 NULL 321 ); 322 323 handle->stack = GTK_STACK( 324 gtk_builder_get_object(handle->builder, "new_lobby_stack") 325 ); 326 327 handle->generate_box = GTK_WIDGET( 328 gtk_builder_get_object(handle->builder, "generate_box") 329 ); 330 331 handle->copy_box = GTK_WIDGET( 332 gtk_builder_get_object(handle->builder, "copy_box") 333 ); 334 335 handle->expiration_combo_box = GTK_COMBO_BOX( 336 gtk_builder_get_object(handle->builder, "expiration_combo_box") 337 ); 338 339 handle->preview_stack = GTK_STACK( 340 gtk_builder_get_object(handle->builder, "preview_stack") 341 ); 342 343 handle->fail_box = GTK_WIDGET( 344 gtk_builder_get_object(handle->builder, "fail_box") 345 ); 346 347 handle->loading_spinner = GTK_SPINNER( 348 gtk_builder_get_object(handle->builder, "loading_spinner") 349 ); 350 351 handle->id_drawing_area = GTK_DRAWING_AREA( 352 gtk_builder_get_object(handle->builder, "id_drawing_area") 353 ); 354 355 handle->id_draw_signal = g_signal_connect( 356 handle->id_drawing_area, 357 "draw", 358 G_CALLBACK(handle_id_drawing_area_draw), 359 handle 360 ); 361 362 handle->id_entry = GTK_ENTRY( 363 gtk_builder_get_object(handle->builder, "id_entry") 364 ); 365 366 handle->cancel_button = GTK_BUTTON( 367 gtk_builder_get_object(handle->builder, "cancel_button") 368 ); 369 370 g_signal_connect( 371 handle->cancel_button, 372 "clicked", 373 G_CALLBACK(handle_cancel_button_click), 374 handle->dialog 375 ); 376 377 handle->generate_button = GTK_BUTTON( 378 gtk_builder_get_object(handle->builder, "generate_button") 379 ); 380 381 g_signal_connect( 382 handle->generate_button, 383 "clicked", 384 G_CALLBACK(handle_generate_button_click), 385 app 386 ); 387 388 handle->copy_button = GTK_BUTTON( 389 gtk_builder_get_object(handle->builder, "copy_button") 390 ); 391 392 g_signal_connect( 393 handle->copy_button, 394 "clicked", 395 G_CALLBACK(handle_copy_button_click), 396 app 397 ); 398 399 g_signal_connect( 400 handle->dialog, 401 "destroy", 402 G_CALLBACK(handle_dialog_destroy), 403 handle 404 ); 405 } 406 407 void 408 ui_new_lobby_dialog_cleanup(UI_NEW_LOBBY_Handle *handle) 409 { 410 g_assert(handle); 411 412 g_signal_handler_disconnect( 413 handle->id_drawing_area, 414 handle->id_draw_signal 415 ); 416 417 g_object_unref(handle->builder); 418 419 if (handle->qr) 420 QRcode_free(handle->qr); 421 422 memset(handle, 0, sizeof(*handle)); 423 }