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