mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 02:46:52 +01:00 
			
		
		
		
	Compare commits
	
		
			36 Commits
		
	
	
		
			v0.9.0
			...
			TokTok-mas
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ae94bc593b | ||
|  | 81eb58532e | ||
|  | 8464ea9a7a | ||
|  | b77bff35a1 | ||
|  | eb964b64c2 | ||
|  | 2ff9d29491 | ||
|  | 2640919318 | ||
|  | 2fcbc4fa1c | ||
|  | 4330bf5867 | ||
|  | 3f1b7cdd26 | ||
|  | 1e985c1456 | ||
|  | 61740bda85 | ||
|  | 0d8e6d713e | ||
|  | 39e4ff8bd6 | ||
|  | 0434ac186a | ||
|  | 8d9d51640c | ||
|  | c4c0c0d1f4 | ||
|  | 3f2826bd66 | ||
|  | 7b7ea0e386 | ||
|  | d35a38735b | ||
|  | f0c4906fdc | ||
|  | 56ba61e061 | ||
|  | 898d89e95a | ||
|  | 1fd1e27bdf | ||
|  | 8e84ac58d4 | ||
|  | 9d65997871 | ||
|  | da2889f3ab | ||
|  | 312b38d253 | ||
|  | 0554bf0240 | ||
|  | 53a7530e8a | ||
|  | 41be04a142 | ||
|  | 31f36318a2 | ||
|  | f882fdf608 | ||
|  | 7e1e410307 | ||
|  | c135c812c2 | ||
|  | 6c239193ab | 
| @@ -59,6 +59,8 @@ Run `make doc` in the build directory after editing the asciidoc files to regene | |||||||
|   * `DISABLE_QRPNG` → Disable support for exporting QR as PNG |   * `DISABLE_QRPNG` → Disable support for exporting QR as PNG | ||||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support |   * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support | ||||||
|   * `ENABLE_PYTHON=1` → Build toxic with Python scripting support |   * `ENABLE_PYTHON=1` → Build toxic with Python scripting support | ||||||
|  |   * `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations | ||||||
|  |   * `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled | ||||||
|  |  | ||||||
| * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg") | * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg") | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Makefile
									
									
									
									
									
								
							| @@ -5,8 +5,7 @@ CFG_DIR = $(BASE_DIR)/cfg | |||||||
|  |  | ||||||
| LIBS = toxcore ncursesw libconfig libcurl | LIBS = toxcore ncursesw libconfig libcurl | ||||||
|  |  | ||||||
| CFLAGS ?= -g | CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces | ||||||
| CFLAGS += -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wmissing-field-initializers -Wno-missing-braces |  | ||||||
| CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||||
| CFLAGS += ${USER_CFLAGS} | CFLAGS += ${USER_CFLAGS} | ||||||
| @@ -18,6 +17,22 @@ OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o con | |||||||
| OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | ||||||
| OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | ||||||
|  |  | ||||||
|  | # Check if debug build is enabled | ||||||
|  | RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||||
|  | ifneq ($(RELEASE), enabled) | ||||||
|  | 	CFLAGS += -O0 -g -DDEBUG | ||||||
|  | 	LDFLAGS += -O0 | ||||||
|  | else | ||||||
|  | 	CFLAGS += -O2 -flto | ||||||
|  | 	LDFLAGS += -O2 -flto | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if LLVM Address Sanitizer is enabled | ||||||
|  | ENABLE_ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||||
|  | ifneq ($(ENABLE_ASAN), disabled) | ||||||
|  | 	CFLAGS += -fsanitize=address -fno-omit-frame-pointer | ||||||
|  | endif | ||||||
|  |  | ||||||
| # Check on wich system we are running | # Check on wich system we are running | ||||||
| UNAME_S = $(shell uname -s) | UNAME_S = $(shell uname -s) | ||||||
| ifeq ($(UNAME_S), Linux) | ifeq ($(UNAME_S), Linux) | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|  |  | ||||||
| Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. | Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. | ||||||
|  |  | ||||||
| [](https://i.imgur.com/san99Z2.png) | [](https://i.imgur.com/TwYA8L0.png) | ||||||
|  |  | ||||||
| ## Installation | ## Installation | ||||||
| [See the install instructions](/INSTALL.md) | [See the install instructions](/INSTALL.md) | ||||||
|   | |||||||
| @@ -55,9 +55,9 @@ author = 'Jakob Kreuze' | |||||||
| # built documents. | # built documents. | ||||||
| # | # | ||||||
| # The short X.Y version. | # The short X.Y version. | ||||||
| version = '0.9.0' | version = '0.10.0' | ||||||
| # The full version, including alpha/beta/rc tags. | # The full version, including alpha/beta/rc tags. | ||||||
| release = '0.9.0' | release = '0.10.0' | ||||||
|  |  | ||||||
| # The language for content autogenerated by Sphinx. Refer to documentation | # The language for content autogenerated by Sphinx. Refer to documentation | ||||||
| # for a list of supported languages. | # for a list of supported languages. | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| # Variables for X11 support | # Variables for X11 support | ||||||
| X11_LIBS = x11 | X11_LIBS = x11 | ||||||
| X11_CFLAGS = -DX11 | X11_CFLAGS = -DX11 | ||||||
| X11_OBJ = xtra.o | X11_OBJ = x11focus.o | ||||||
|  |  | ||||||
| # Check if we can build X11 support | # Check if we can build X11 support | ||||||
| CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # Version | # Version | ||||||
| TOXIC_VERSION = 0.9.0 | TOXIC_VERSION = 0.10.0 | ||||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||||
| ifneq (, $(findstring error, $(REV))) | ifneq (, $(findstring error, $(REV))) | ||||||
|     VERSION = $(TOXIC_VERSION) |     VERSION = $(TOXIC_VERSION) | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ help: | |||||||
| 	@echo "  DISABLE_QRCODE:         Set to \"1\" to force building without QR export support" | 	@echo "  DISABLE_QRCODE:         Set to \"1\" to force building without QR export support" | ||||||
| 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | ||||||
| 	@echo "  ENABLE_PYTHON:          Set to \"1\" to enable building with Python scripting support" | 	@echo "  ENABLE_PYTHON:          Set to \"1\" to enable building with Python scripting support" | ||||||
|  | 	@echo "  ENABLE_RELEASE:         Set to \"1\" to build without debug symbols and with full compiler optimizations" | ||||||
|  | 	@echo "  ENABLE_ASAN:            Set to \"1\" to build with LLVM address sanitizer enabled. | ||||||
| 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | ||||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| '\" t | '\" t | ||||||
| .\"     Title: toxic | .\"     Title: toxic | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2016-09-20 | .\"      Date: 2020-05-04 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  Language: English | ||||||
| .\" | .\" | ||||||
| .TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" | .TH "TOXIC" "1" "2020\-05\-04" "toxic __VERSION__" "Toxic Manual" | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| .\" * Define some portability stuff | .\" * Define some portability stuff | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ | |||||||
| .\"     Title: toxic.conf | .\"     Title: toxic.conf | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2018-10-27 | .\"      Date: 2020-11-18 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  Language: English | ||||||
| .\" | .\" | ||||||
| .TH "TOXIC\&.CONF" "5" "2018\-10\-27" "toxic __VERSION__" "Toxic Manual" | .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual" | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| .\" * Define some portability stuff | .\" * Define some portability stuff | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| @@ -93,6 +93,26 @@ Enable or disable acoustic alerts on events\&. true or false | |||||||
| Select between native terminal colors and toxic color theme\&. true or false | Select between native terminal colors and toxic color theme\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \fBcolor_bar_bg\fR | ||||||
|  | .RS 4 | ||||||
|  | set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBcolor_bar_fg\fR | ||||||
|  | .RS 4 | ||||||
|  | set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBcolor_bar_accent\fR | ||||||
|  | .RS 4 | ||||||
|  | set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBcolor_bar_notify\fR | ||||||
|  | .RS 4 | ||||||
|  | set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
| \fBautolog\fR | \fBautolog\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Enable or disable autologging\&. true or false | Enable or disable autologging\&. true or false | ||||||
| @@ -135,7 +155,7 @@ Maximum lines for chat window history\&. Integer value\&. (for example: 700) | |||||||
| .PP | .PP | ||||||
| \fBnotification_timeout\fR | \fBnotification_timeout\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Time in milliseconds to display a notification\&. (for example: 3000) | Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000) | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBline_join\fR | \fBline_join\fR | ||||||
| @@ -219,7 +239,22 @@ Audio output device\&. Integer value\&. Number corresponds to | |||||||
| .PP | .PP | ||||||
| \fBVAD_threshold\fR | \fBVAD_threshold\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0-40\&.0 | Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0 | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBconference_audio_channels\fR | ||||||
|  | .RS 4 | ||||||
|  | Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBchat_audio_channels\fR | ||||||
|  | .RS 4 | ||||||
|  | Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBpush_to_talk\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false | ||||||
| .RE | .RE | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|   | |||||||
| @@ -60,6 +60,18 @@ OPTIONS | |||||||
|     *native_colors*;; |     *native_colors*;; | ||||||
|         Select between native terminal colors and toxic color theme. true or false |         Select between native terminal colors and toxic color theme. true or false | ||||||
|  |  | ||||||
|  |     *color_bar_bg*;; | ||||||
|  |         set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |  | ||||||
|  |     *color_bar_fg*;; | ||||||
|  |         set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |  | ||||||
|  |     *color_bar_accent*;; | ||||||
|  |         set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |  | ||||||
|  |     *color_bar_notify*;; | ||||||
|  |         set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |  | ||||||
|     *autolog*;; |     *autolog*;; | ||||||
|         Enable or disable autologging. true or false |         Enable or disable autologging. true or false | ||||||
|  |  | ||||||
| @@ -139,6 +151,15 @@ OPTIONS | |||||||
|         Voice Activity Detection threshold.  Float value. Recommended values are |         Voice Activity Detection threshold.  Float value. Recommended values are | ||||||
|         1.0-40.0 |         1.0-40.0 | ||||||
|  |  | ||||||
|  |     *conference_audio_channels*;; | ||||||
|  |         Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||||
|  |  | ||||||
|  |     *chat_audio_channels*;; | ||||||
|  |         Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||||
|  |  | ||||||
|  |     *push_to_talk*;; | ||||||
|  |         Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false | ||||||
|  |  | ||||||
| *tox*:: | *tox*:: | ||||||
|     Configuration related to paths. |     Configuration related to paths. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,20 +9,32 @@ ui = { | |||||||
|   alerts=true; |   alerts=true; | ||||||
|  |  | ||||||
|   // Output a bell when receiving a message (see manpage) |   // Output a bell when receiving a message (see manpage) | ||||||
|   bell_on_message=true |   bell_on_message=true; | ||||||
|  |  | ||||||
|   // Output a bell when receiving a filetransfer (see manpage) |   // Output a bell when receiving a filetransfer (see manpage) | ||||||
|   bell_on_filetrans=true |   bell_on_filetrans=true; | ||||||
|  |  | ||||||
|   // Don't output a bell when a filetransfer was accepted (see manpage) |   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||||
|   bell_on_filetrans_accept=false |   bell_on_filetrans_accept=false; | ||||||
|  |  | ||||||
|   // Output a bell when receiving a group/call invite (see manpage) |   // Output a bell when receiving a group/call invite (see manpage) | ||||||
|   bell_on_invite=true |   bell_on_invite=true; | ||||||
|  |  | ||||||
|   // true to use native terminal colours, false to use toxic default colour theme |   // true to use native terminal colours, false to use toxic default colour theme | ||||||
|   native_colors=false; |   native_colors=false; | ||||||
|  |  | ||||||
|  |   // set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |   color_bar_bg="blue"; | ||||||
|  |  | ||||||
|  |   // set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |   color_bar_fg="white"; | ||||||
|  |  | ||||||
|  |   // set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |   color_bar_accent="cyan"; | ||||||
|  |  | ||||||
|  |   // set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta) | ||||||
|  |   color_bar_notify="yellow"; | ||||||
|  |  | ||||||
|   // true to enable autologging, false to disable |   // true to enable autologging, false to disable | ||||||
|   autolog=false; |   autolog=false; | ||||||
|  |  | ||||||
| @@ -30,7 +42,7 @@ ui = { | |||||||
|   time_format=24; |   time_format=24; | ||||||
|  |  | ||||||
|   // Timestamp format string according to date/strftime format. Overrides time_format setting |   // Timestamp format string according to date/strftime format. Overrides time_format setting | ||||||
|   timestamp_format="%H:%M:%S"; |   timestamp_format="%H:%M"; | ||||||
|  |  | ||||||
|   // true to show you when others are typing a message in 1-on-1 chats |   // true to show you when others are typing a message in 1-on-1 chats | ||||||
|   show_typing_other=true; |   show_typing_other=true; | ||||||
| @@ -54,19 +66,19 @@ ui = { | |||||||
|   history_size=700; |   history_size=700; | ||||||
|  |  | ||||||
|   // time in milliseconds to display a notification |   // time in milliseconds to display a notification | ||||||
|   notification_timeout=3000; |   notification_timeout=6000; | ||||||
|  |  | ||||||
|   // Indicator for display when someone connects or joins a group. |   // Indicator for display when someone connects or joins a group | ||||||
|   line_join="-->"; |   line_join="-->"; | ||||||
|  |  | ||||||
|   // Indicator for display when someone disconnects or leaves a group. |   // Indicator for display when someone disconnects or leaves a group | ||||||
|   line_quit="<--"; |   line_quit="<--"; | ||||||
|  |  | ||||||
|   // Indicator for alert messages. |   // Indicator for alert messages. | ||||||
|   line_alert="-!-"; |   line_alert="-!-"; | ||||||
|  |  | ||||||
|   // Indicator for normal messages. |   // Indicator for normal messages. | ||||||
|   line_normal="---"; |   line_normal="-"; | ||||||
|  |  | ||||||
|   // true to change status based on screen/tmux attach/detach, false to disable |   // true to change status based on screen/tmux attach/detach, false to disable | ||||||
|   mplex_away=true; |   mplex_away=true; | ||||||
| @@ -84,6 +96,15 @@ audio = { | |||||||
|  |  | ||||||
|   // default VAD threshold; float (recommended values are 1.0-40.0) |   // default VAD threshold; float (recommended values are 1.0-40.0) | ||||||
|   VAD_threshold=5.0; |   VAD_threshold=5.0; | ||||||
|  |  | ||||||
|  |   // Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo. | ||||||
|  |   conference_audio_channels=1; | ||||||
|  |  | ||||||
|  |   // Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo. | ||||||
|  |   chat_audio_channels=2; | ||||||
|  |  | ||||||
|  |   // toggle conference push-to-talk | ||||||
|  |   push_to_talk=false; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| tox = { | tox = { | ||||||
| @@ -122,6 +143,6 @@ keys = { | |||||||
|   half_page_up="Ctrl+F"; |   half_page_up="Ctrl+F"; | ||||||
|   half_page_down="Ctrl+V"; |   half_page_down="Ctrl+V"; | ||||||
|   page_bottom="Ctrl+H"; |   page_bottom="Ctrl+H"; | ||||||
|   toggle_peerlist="Ctrl+b"; |   toggle_peerlist="Ctrl+B"; | ||||||
|   toggle_paste_mode="Ctrl+T"; |   toggle_paste_mode="Ctrl+T"; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								src/api.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/api.c
									
									
									
									
									
								
							| @@ -99,18 +99,16 @@ void api_send(const char *msg) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *name = api_get_nick(); |     char *name = api_get_nick(); | ||||||
|     char  timefrmt[TIME_STR_SIZE]; |  | ||||||
|  |  | ||||||
|     if (name == NULL) { |     if (name == NULL) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     self_window = get_active_window(); |     self_window = get_active_window(); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line)); |     strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line)); | ||||||
|     add_line_to_hist(self_window->chatwin); |     add_line_to_hist(self_window->chatwin); | ||||||
|     int id = line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg); |     int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg); | ||||||
|     cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id); |     cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id); | ||||||
|     free(name); |     free(name); | ||||||
| } | } | ||||||
| @@ -156,7 +154,7 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|             error_str = "Only one argument allowed."; |             error_str = "Only one argument allowed."; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -165,7 +163,7 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|     if (fp == NULL) { |     if (fp == NULL) { | ||||||
|         error_str = "Path does not exist."; |         error_str = "Path does not exist."; | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -175,36 +173,36 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|  |  | ||||||
| void invoke_autoruns(WINDOW *window, ToxWindow *self) | void invoke_autoruns(WINDOW *window, ToxWindow *self) | ||||||
| { | { | ||||||
|     struct dirent *dir; |     char abspath_buf[PATH_MAX + 256]; | ||||||
|     char    abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1]; |     char err_buf[PATH_MAX + 128]; | ||||||
|     size_t  path_len; |  | ||||||
|     DIR    *d; |  | ||||||
|     FILE   *fp; |  | ||||||
|  |  | ||||||
|     if (user_settings->autorun_path[0] == '\0') { |     if (user_settings->autorun_path[0] == '\0') { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     d = opendir(user_settings->autorun_path); |     DIR *d = opendir(user_settings->autorun_path); | ||||||
|  |  | ||||||
|     if (d == NULL) { |     if (d == NULL) { | ||||||
|         snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path); |         snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path); | ||||||
|         api_display(err_buf); |         api_display(err_buf); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     struct dirent *dir = NULL; | ||||||
|  |  | ||||||
|     cur_window  = window; |     cur_window  = window; | ||||||
|  |  | ||||||
|     self_window = self; |     self_window = self; | ||||||
|  |  | ||||||
|     while ((dir = readdir(d)) != NULL) { |     while ((dir = readdir(d)) != NULL) { | ||||||
|         path_len = strlen(dir->d_name); |         size_t path_len = strlen(dir->d_name); | ||||||
|  |  | ||||||
|         if (!strcmp(dir->d_name + path_len - 3, ".py")) { |         if (!strcmp(dir->d_name + path_len - 3, ".py")) { | ||||||
|             snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name); |             snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name); | ||||||
|             fp = fopen(abspath_buf, "r"); |             FILE *fp = fopen(abspath_buf, "r"); | ||||||
|  |  | ||||||
|             if (fp == NULL) { |             if (fp == NULL) { | ||||||
|                 snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf); |                 snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf); | ||||||
|                 api_display(err_buf); |                 api_display(err_buf); | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ | |||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| @@ -64,6 +65,7 @@ extern ToxWindow *windows[MAX_WINDOWS_NUM]; | |||||||
|  |  | ||||||
| struct CallControl CallControl; | struct CallControl CallControl; | ||||||
|  |  | ||||||
|  | extern struct user_settings *user_settings; | ||||||
| extern struct Winthread Winthread; | extern struct Winthread Winthread; | ||||||
|  |  | ||||||
| void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, | void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, | ||||||
| @@ -88,7 +90,7 @@ void audio_bit_rate_callback(ToxAV *av, uint32_t friend_number, uint32_t audio_b | |||||||
|  |  | ||||||
| static void print_err(ToxWindow *self, const char *error_str) | static void print_err(ToxWindow *self, const char *error_str) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox) | ToxAV *init_audio(ToxWindow *self, Tox *tox) | ||||||
| @@ -103,7 +105,7 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox) | |||||||
|     CallControl.default_audio_bit_rate = 64; |     CallControl.default_audio_bit_rate = 64; | ||||||
|     CallControl.audio_sample_rate = 48000; |     CallControl.audio_sample_rate = 48000; | ||||||
|     CallControl.audio_frame_duration = 20; |     CallControl.audio_frame_duration = 20; | ||||||
|     CallControl.audio_channels = 1; |     CallControl.audio_channels = user_settings->chat_audio_channels; | ||||||
|  |  | ||||||
|     CallControl.video_enabled = false; |     CallControl.video_enabled = false; | ||||||
|     CallControl.default_video_bit_rate = 0; |     CallControl.default_video_bit_rate = 0; | ||||||
| @@ -111,13 +113,13 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox) | |||||||
|  |  | ||||||
|     if (!CallControl.av) { |     if (!CallControl.av) { | ||||||
|         CallControl.audio_errors |= ae_StartingCoreAudio; |         CallControl.audio_errors |= ae_StartingCoreAudio; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV"); | ||||||
|  |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_devices() == de_InternalError) { |     if (init_devices() == de_InternalError) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); | ||||||
|         toxav_kill(CallControl.av); |         toxav_kill(CallControl.av); | ||||||
|  |  | ||||||
|         return CallControl.av = NULL; |         return CallControl.av = NULL; | ||||||
| @@ -198,7 +200,7 @@ static bool cancel_call(Call *call) | |||||||
| static int start_transmission(ToxWindow *self, Call *call) | static int start_transmission(ToxWindow *self, Call *call) | ||||||
| { | { | ||||||
|     if (!self || !CallControl.av) { |     if (!self || !CallControl.av) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare audio transmission"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare audio transmission"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -207,17 +209,17 @@ static int start_transmission(ToxWindow *self, Call *call) | |||||||
|  |  | ||||||
|     if (error != de_None) { |     if (error != de_None) { | ||||||
|         if (error == de_FailedStart) { |         if (error == de_FailedStart) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start audio input device"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to start audio input device"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (error == de_InternalError) { |         if (error == de_InternalError) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening audio input device"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening audio input device"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (open_output_device(&call->out_idx, |     if (open_output_device(&call->out_idx, | ||||||
|                            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) { |                            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open audio output device!"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open audio output device!"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -340,7 +342,7 @@ void on_call_state(ToxAV *av, uint32_t friend_number, uint32_t state, void *user | |||||||
|         case TOXAV_FRIEND_CALL_STATE_ERROR: |         case TOXAV_FRIEND_CALL_STATE_ERROR: | ||||||
|         case TOXAV_FRIEND_CALL_STATE_FINISHED: |         case TOXAV_FRIEND_CALL_STATE_FINISHED: | ||||||
|             if (state == TOXAV_FRIEND_CALL_STATE_ERROR) { |             if (state == TOXAV_FRIEND_CALL_STATE_ERROR) { | ||||||
|                 line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!"); |                 line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (call->status == cs_Pending) { |             if (call->status == cs_Pending) { | ||||||
| @@ -667,7 +669,7 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (* | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -706,7 +708,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char ( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -746,7 +748,7 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -814,7 +816,7 @@ void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                       "Current audio encoding bitrate: %u", call->audio_bit_rate); |                       "Current audio encoding bitrate: %u", call->audio_bit_rate); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -914,7 +916,7 @@ void stop_current_call(ToxWindow *self) | |||||||
|  */ |  */ | ||||||
| static void realloc_calls(uint32_t n) | static void realloc_calls(uint32_t n) | ||||||
| { | { | ||||||
|     if (n <= 0) { |     if (n == 0) { | ||||||
|         free(CallControl.calls); |         free(CallControl.calls); | ||||||
|         CallControl.calls = NULL; |         CallControl.calls = NULL; | ||||||
|         return; |         return; | ||||||
|   | |||||||
| @@ -397,7 +397,7 @@ static DeviceError open_source(Device *device) | |||||||
|     alSourcei(device->source, AL_LOOPING, AL_FALSE); |     alSourcei(device->source, AL_LOOPING, AL_FALSE); | ||||||
|  |  | ||||||
|     const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo); |     const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo); | ||||||
|     size_t zeros_size = frame_size / 2; |     size_t zeros_size = frame_size * sizeof(uint16_t); | ||||||
|     uint16_t *zeros = calloc(1, zeros_size); |     uint16_t *zeros = calloc(1, zeros_size); | ||||||
|  |  | ||||||
|     if (zeros == NULL) { |     if (zeros == NULL) { | ||||||
| @@ -407,7 +407,7 @@ static DeviceError open_source(Device *device) | |||||||
|  |  | ||||||
|     for (int i = 0; i < OPENAL_BUFS; ++i) { |     for (int i = 0; i < OPENAL_BUFS; ++i) { | ||||||
|         alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros, |         alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros, | ||||||
|                      frame_size, device->frame_info.sample_rate); |                      zeros_size, device->frame_info.sample_rate); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(zeros); |     free(zeros); | ||||||
| @@ -775,7 +775,7 @@ float get_input_volume(void) | |||||||
| void print_al_devices(ToxWindow *self, DeviceType type) | void print_al_devices(ToxWindow *self, DeviceType type) | ||||||
| { | { | ||||||
|     for (int i = 0; i < audio_state->num_al_devices[type]; ++i) { |     for (int i = 0; i < audio_state->num_al_devices[type]; ++i) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, |         line_info_add(self, false, NULL, NULL, SYS_MSG, | ||||||
|                       audio_state->current_al_device_name[type] |                       audio_state->current_al_device_name[type] | ||||||
|                       && strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0, |                       && strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0, | ||||||
|                       0, "%d: %s", i, audio_state->al_device_names[type][i]); |                       0, "%d: %s", i, audio_state->al_device_names[type][i]); | ||||||
|   | |||||||
| @@ -45,10 +45,10 @@ static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matc | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < n_matches; ++i) { |     for (size_t i = 0; i < n_matches; ++i) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* puts match in match buffer. if more than one match, add first n chars that are identical. | /* puts match in match buffer. if more than one match, add first n chars that are identical. | ||||||
| @@ -109,7 +109,7 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *endchrs = " "; |     const char *endchrs = " "; | ||||||
|     char ubuf[MAX_STR_SIZE] = {0}; |     char ubuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     /* work with multibyte string copy of buf for simplicity */ |     /* work with multibyte string copy of buf for simplicity */ | ||||||
|     if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) { |     if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) { | ||||||
| @@ -118,8 +118,8 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t | |||||||
|  |  | ||||||
|     /* isolate substring from space behind pos to pos */ |     /* isolate substring from space behind pos to pos */ | ||||||
|     char tmp[MAX_STR_SIZE]; |     char tmp[MAX_STR_SIZE]; | ||||||
|     snprintf(tmp, sizeof(tmp), "%s", ubuf); |     memcpy(tmp, ubuf, ctx->pos); | ||||||
|     tmp[ctx->pos] = '\0'; |     tmp[ctx->pos] = 0; | ||||||
|  |  | ||||||
|     const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' '); |     const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' '); | ||||||
|     char *sub = calloc(1, strlen(ubuf) + 1); |     char *sub = calloc(1, strlen(ubuf) + 1); | ||||||
| @@ -147,7 +147,7 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!sub[0]) { |     if (!sub[0] && !(dir_search && n_items == 1)) { | ||||||
|         free(sub); |         free(sub); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										256
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								src/chat.c
									
									
									
									
									
								
							| @@ -128,7 +128,14 @@ static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing) | |||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     tox_self_set_typing(m, self->num, is_typing, NULL); |     TOX_ERR_SET_TYPING err; | ||||||
|  |     tox_self_set_typing(m, self->num, is_typing, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_SET_TYPING_OK) { | ||||||
|  |         fprintf(stderr, "Warning: tox_self_set_typing() failed with error %d\n", err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ctx->self_is_typing = is_typing; |     ctx->self_is_typing = is_typing; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -160,11 +167,11 @@ void kill_chat_window(ToxWindow *self, Tox *m) | |||||||
|     del_window(self); |     del_window(self); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void recv_message_helper(ToxWindow *self, const char *msg, const char *nick, const char *timefrmt) | static void recv_message_helper(ToxWindow *self, const char *msg, const char *nick) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, 0, "%s", msg); |     line_info_add(self, true, nick, NULL, IN_MSG, 0, 0, "%s", msg); | ||||||
|     write_to_log(msg, nick, ctx->log, false); |     write_to_log(msg, nick, ctx->log, false); | ||||||
|  |  | ||||||
|     if (self->active_box != -1) { |     if (self->active_box != -1) { | ||||||
| @@ -176,11 +183,11 @@ static void recv_message_helper(ToxWindow *self, const char *msg, const char *ni | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void recv_action_helper(ToxWindow *self, const char *action, const char *nick, const char *timefrmt) | static void recv_action_helper(ToxWindow *self, const char *action, const char *nick) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action); |     line_info_add(self, true, nick, NULL, IN_ACTION, 0, 0, "%s", action); | ||||||
|     write_to_log(action, nick, ctx->log, true); |     write_to_log(action, nick, ctx->log, true); | ||||||
|  |  | ||||||
|     if (self->active_box != -1) { |     if (self->active_box != -1) { | ||||||
| @@ -203,16 +210,13 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Message_Ty | |||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     if (type == TOX_MESSAGE_TYPE_NORMAL) { |     if (type == TOX_MESSAGE_TYPE_NORMAL) { | ||||||
|         recv_message_helper(self, msg, nick, timefrmt); |         recv_message_helper(self, msg, nick); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (type == TOX_MESSAGE_TYPE_ACTION) { |     if (type == TOX_MESSAGE_TYPE_ACTION) { | ||||||
|         recv_action_helper(self, msg, nick, timefrmt); |         recv_action_helper(self, msg, nick); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -230,9 +234,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C | |||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|     const char *msg; |     const char *msg; | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
| @@ -247,19 +248,19 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C | |||||||
|         chat_resume_file_senders(self, m, num); |         chat_resume_file_senders(self, m, num); | ||||||
|  |  | ||||||
|         msg = "has come online"; |         msg = "has come online"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); |         line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
|     } else if (connection_status == TOX_CONNECTION_NONE) { |     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||||
|         Friends.list[num].is_typing = false; |         Friends.list[num].is_typing = false; | ||||||
|  |  | ||||||
|         if (self->chatwin->self_is_typing) { |         if (self->chatwin->self_is_typing) { | ||||||
|             set_self_typingstatus(self, m, 0); |             set_self_typingstatus(self, m, false); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         chat_pause_file_transfers(num); |         chat_pause_file_transfers(num); | ||||||
|  |  | ||||||
|         msg = "has gone offline"; |         msg = "has gone offline"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); |         line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -497,18 +498,16 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char msg[MAX_STR_SIZE]; |  | ||||||
|  |  | ||||||
|     switch (control) { |     switch (control) { | ||||||
|         case TOX_FILE_CONTROL_RESUME: { |         case TOX_FILE_CONTROL_RESUME: { | ||||||
|             /* transfer is accepted */ |             /* transfer is accepted */ | ||||||
|             if (ft->state == FILE_TRANSFER_PENDING) { |             if (ft->state == FILE_TRANSFER_PENDING) { | ||||||
|                 ft->state = FILE_TRANSFER_STARTED; |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%zu] for '%s' accepted.", |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%zu] for '%s' accepted.", | ||||||
|                               ft->index, ft->file_name); |                               ft->index, ft->file_name); | ||||||
|                 char progline[MAX_STR_SIZE]; |                 char progline[MAX_STR_SIZE]; | ||||||
|                 init_progress_bar(progline); |                 init_progress_bar(progline); | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||||
|                 sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL); |                 sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL); | ||||||
|                 ft->line_id = self->chatwin->hst->line_end->id + 2; |                 ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||||
|             } else if (ft->state == FILE_TRANSFER_PAUSED) {    /* transfer is resumed */ |             } else if (ft->state == FILE_TRANSFER_PAUSED) {    /* transfer is resumed */ | ||||||
| @@ -524,6 +523,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         case TOX_FILE_CONTROL_CANCEL: { |         case TOX_FILE_CONTROL_CANCEL: { | ||||||
|  |             char msg[MAX_STR_SIZE]; | ||||||
|             snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); |             snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); | ||||||
|             close_file_transfer(self, m, ft, -1, msg, notif_error); |             close_file_transfer(self, m, ft, -1, msg, notif_error); | ||||||
|             break; |             break; | ||||||
| @@ -631,14 +631,14 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ | |||||||
|  |  | ||||||
|     if (!ft) { |     if (!ft) { | ||||||
|         tox_file_control(m, friendnum, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); |         tox_file_control(m, friendnum, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                       "File transfer request failed: Too many concurrent file transfers."); |                       "File transfer request failed: Too many concurrent file transfers."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char sizestr[32]; |     char sizestr[32]; | ||||||
|     bytes_convert_str(sizestr, sizeof(sizestr), file_size); |     bytes_convert_str(sizestr, sizeof(sizestr), file_size); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr); | ||||||
|  |  | ||||||
|     if (!valid_file_name(filename, name_length)) { |     if (!valid_file_name(filename, name_length)) { | ||||||
|         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, "File transfer failed: Invalid file name.", notif_error); |         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, "File transfer failed: Invalid file name.", notif_error); | ||||||
| @@ -697,7 +697,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %zu' to accept the file transfer.", ft->index); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %zu' to accept the file transfer.", ft->index); | ||||||
|  |  | ||||||
|     ft->file_size = file_size; |     ft->file_size = file_size; | ||||||
|     snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path); |     snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path); | ||||||
| @@ -739,21 +739,21 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe | |||||||
|     Friends.list[friendnumber].conference_invite.length = length; |     Friends.list[friendnumber].conference_invite.length = length; | ||||||
|     Friends.list[friendnumber].conference_invite.type = type; |     Friends.list[friendnumber].conference_invite.type = type; | ||||||
|  |  | ||||||
|     sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL); |  | ||||||
|  |  | ||||||
|     char name[TOX_MAX_NAME_LENGTH]; |     char name[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, name, friendnumber); |     get_nick_truncate(m, name, friendnumber); | ||||||
|  |  | ||||||
|     const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference"; |     const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference"; | ||||||
|  |  | ||||||
|     if (self->active_box != -1) { |     if (self->active_box != -1) { | ||||||
|         box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join %s", description); |         box_notify2(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, self->active_box, | ||||||
|  |                     "invites you to join %s", description); | ||||||
|     } else { |     } else { | ||||||
|         box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join %s", description); |         box_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, &self->active_box, name, | ||||||
|  |                    "invites you to join %s", description); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat."); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat."); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* AV Stuff */ | /* AV Stuff */ | ||||||
| @@ -771,7 +771,7 @@ void chat_onInvite(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state | |||||||
|     /* call is flagged active here */ |     /* call is flagged active here */ | ||||||
|     self->is_call = true; |     self->is_call = true; | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); | ||||||
|  |  | ||||||
|     if (self->ringing_sound == -1) { |     if (self->ringing_sound == -1) { | ||||||
|         sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound); |         sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound); | ||||||
| @@ -793,7 +793,7 @@ void chat_onRinging(ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it."); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it."); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|  |  | ||||||
| @@ -815,7 +815,7 @@ void chat_onStarting(ToxWindow *self, ToxAV *av, uint32_t friend_number, int sta | |||||||
|  |  | ||||||
|     init_infobox(self); |     init_infobox(self); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); | ||||||
|  |  | ||||||
|     /* call is flagged active here */ |     /* call is flagged active here */ | ||||||
|     self->is_call = true; |     self->is_call = true; | ||||||
| @@ -835,7 +835,7 @@ void chat_onEnding(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); | ||||||
|  |  | ||||||
|     self->is_call = false; |     self->is_call = false; | ||||||
|  |  | ||||||
| @@ -854,7 +854,7 @@ void chat_onError(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     self->is_call = false; |     self->is_call = false; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error!"); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| @@ -875,7 +875,7 @@ void chat_onStart(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | |||||||
|  |  | ||||||
|     init_infobox(self); |     init_infobox(self); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| @@ -893,7 +893,7 @@ void chat_onCancel(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state | |||||||
|  |  | ||||||
|     self->is_call = false; |     self->is_call = false; | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| @@ -909,7 +909,7 @@ void chat_onReject(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Rejected!"); | ||||||
|     self->is_call = false; |     self->is_call = false; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| @@ -927,7 +927,7 @@ void chat_onEnd(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); | ||||||
|     self->is_call = false; |     self->is_call = false; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| @@ -1027,7 +1027,7 @@ static void draw_infobox(ToxWindow *self) | |||||||
|     wattroff(infobox->win, A_BOLD); |     wattroff(infobox->win, A_BOLD); | ||||||
|     wprintw(infobox->win, "%.2f\n", (double) infobox->vad_lvl); |     wprintw(infobox->win, "%.2f\n", (double) infobox->vad_lvl); | ||||||
|  |  | ||||||
|     wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); |     wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_ULCORNER, ' ', ACS_LLCORNER, ' '); | ||||||
|     wnoutrefresh(infobox->win); |     wnoutrefresh(infobox->win); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1045,10 +1045,7 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) | |||||||
|     size_t len = tox_self_get_name_size(m); |     size_t len = tox_self_get_name_size(m); | ||||||
|     selfname[len] = '\0'; |     selfname[len] = '\0'; | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     int id = line_info_add(self, true, selfname, NULL, OUT_ACTION, 0, 0, "%s", action); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     int id = line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action); |  | ||||||
|     cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id); |     cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1083,7 +1080,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         input_new_char(self, key, x, x2); |         input_new_char(self, key, x, x2); | ||||||
|  |  | ||||||
|         if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) { |         if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) { | ||||||
|             set_self_typingstatus(self, m, 1); |             set_self_typingstatus(self, m, true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
| @@ -1093,11 +1090,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (input_handle(self, key, x, x2)) { |     int input_ret = input_handle(self, key, x, x2); | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int input_ret = false; |  | ||||||
|  |  | ||||||
|     if (key == L'\t' && ctx->len > 1 && ctx->line[0] == '/') {    /* TAB key: auto-complete */ |     if (key == L'\t' && ctx->len > 1 && ctx->line[0] == '/') {    /* TAB key: auto-complete */ | ||||||
|         input_ret = true; |         input_ret = true; | ||||||
| @@ -1146,7 +1139,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|  |  | ||||||
|             wstrsubst(ctx->line, L'¶', L'\n'); |             wstrsubst(ctx->line, L'¶', L'\n'); | ||||||
|  |  | ||||||
|             char line[MAX_STR_SIZE] = {0}; |             char line[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { |             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { | ||||||
|                 memset(line, 0, sizeof(line)); |                 memset(line, 0, sizeof(line)); | ||||||
| @@ -1161,28 +1154,27 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|                 } else { |                 } else { | ||||||
|                     execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); |                     execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else if (line[0]) { | ||||||
|                 char selfname[TOX_MAX_NAME_LENGTH]; |                 char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|                 tox_self_get_name(m, (uint8_t *) selfname); |                 tox_self_get_name(m, (uint8_t *) selfname); | ||||||
|  |  | ||||||
|                 size_t len = tox_self_get_name_size(m); |                 size_t len = tox_self_get_name_size(m); | ||||||
|                 selfname[len] = '\0'; |                 selfname[len] = '\0'; | ||||||
|  |  | ||||||
|                 char timefrmt[TIME_STR_SIZE]; |                 int id = line_info_add(self, true, selfname, NULL, OUT_MSG, 0, 0, "%s", line); | ||||||
|                 get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|                 int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line); |  | ||||||
|                 cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id); |                 cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id); | ||||||
|  |             } else { | ||||||
|  |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |         wclear(ctx->linewin); | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |         wmove(self->window, y2, 0); | ||||||
|         reset_buf(ctx); |         reset_buf(ctx); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ctx->len <= 0 && ctx->self_is_typing) { |     if (ctx->len <= 0 && ctx->self_is_typing) { | ||||||
|         set_self_typingstatus(self, m, 0); |         set_self_typingstatus(self, m, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return input_ret; |     return input_ret; | ||||||
| @@ -1190,7 +1182,8 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|  |  | ||||||
| static void chat_onDraw(ToxWindow *self, Tox *m) | static void chat_onDraw(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |     if (y2 <= 0 || x2 <= 0) { | ||||||
| @@ -1205,15 +1198,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     wclear(ctx->linewin); |     wclear(ctx->linewin); | ||||||
|  |  | ||||||
|     curs_set(1); |  | ||||||
|  |  | ||||||
|     if (ctx->len > 0) { |     if (ctx->len > 0) { | ||||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); |         mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     curs_set(1); | ||||||
|  |  | ||||||
|     /* Draw status bar */ |     /* Draw status bar */ | ||||||
|     StatusBar *statusbar = self->stb; |     StatusBar *statusbar = self->stb; | ||||||
|     mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); |  | ||||||
|     wmove(statusbar->topline, 0, 0); |     wmove(statusbar->topline, 0, 0); | ||||||
|  |  | ||||||
|     /* Draw name, status and note in statusbar */ |     /* Draw name, status and note in statusbar */ | ||||||
| @@ -1223,38 +1215,63 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|         switch (status) { |         switch (status) { | ||||||
|             case TOX_USER_STATUS_NONE: |             case TOX_USER_STATUS_NONE: | ||||||
|                 colour = GREEN; |                 colour = STATUS_ONLINE; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_AWAY: |             case TOX_USER_STATUS_AWAY: | ||||||
|                 colour = YELLOW; |                 colour = STATUS_AWAY; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_BUSY: |             case TOX_USER_STATUS_BUSY: | ||||||
|                 colour = RED; |                 colour = STATUS_ONLINE; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, " ["); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||||
|         wprintw(statusbar->topline, " %s", ONLINE_CHAR); |         wprintw(statusbar->topline, "%s", ONLINE_CHAR); | ||||||
|         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||||
|  |  | ||||||
|         if (Friends.list[self->num].is_typing) { |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|             wattron(statusbar->topline, COLOR_PAIR(YELLOW)); |         wprintw(statusbar->topline, "] "); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         pthread_mutex_lock(&Winthread.lock); | ||||||
|  |         const bool is_typing = Friends.list[self->num].is_typing; | ||||||
|  |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|  |         if (is_typing) { | ||||||
|  |             wattron(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_NOTIFY)); | ||||||
|  |         } else { | ||||||
|  |             wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wattron(statusbar->topline, A_BOLD); |         wprintw(statusbar->topline, "%s", statusbar->nick); | ||||||
|         wprintw(statusbar->topline, " %s ", statusbar->nick); |  | ||||||
|         wattroff(statusbar->topline, A_BOLD); |  | ||||||
|  |  | ||||||
|         if (Friends.list[self->num].is_typing) { |         if (is_typing) { | ||||||
|             wattroff(statusbar->topline, COLOR_PAIR(YELLOW)); |             wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_NOTIFY)); | ||||||
|  |         } else { | ||||||
|  |             wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_TEXT)); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         wprintw(statusbar->topline, " %s", OFFLINE_CHAR); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wattron(statusbar->topline, A_BOLD); |         wprintw(statusbar->topline, " ["); | ||||||
|         wprintw(statusbar->topline, " %s ", statusbar->nick); |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wattroff(statusbar->topline, A_BOLD); |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(statusbar->topline, "%s", OFFLINE_CHAR); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, "] "); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(statusbar->topline, "%s", statusbar->nick); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Reset statusbar->statusmsg on window resize */ |     /* Reset statusbar->statusmsg on window resize */ | ||||||
| @@ -1283,30 +1300,51 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (statusbar->statusmsg[0]) { |     if (statusbar->statusmsg[0]) { | ||||||
|         wprintw(statusbar->topline, ": %s ", statusbar->statusmsg); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, " | "); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(statusbar->topline, "%s ", statusbar->statusmsg); | ||||||
|  |     } else { | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wclrtoeol(statusbar->topline); |     int s_y; | ||||||
|  |     int s_x; | ||||||
|  |     getyx(statusbar->topline, s_y, s_x); | ||||||
|  |  | ||||||
|  |     mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x - (KEY_IDENT_DIGITS * 2) - 3); | ||||||
|  |     wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|     wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3); |     wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3); | ||||||
|  |  | ||||||
|  |     wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|     wprintw(statusbar->topline, "{"); |     wprintw(statusbar->topline, "{"); | ||||||
|  |     wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|     size_t i; |     wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|     for (i = 0; i < KEY_IDENT_DIGITS; ++i) { |     for (size_t i = 0; i < KEY_IDENT_DIGITS; ++i) { | ||||||
|         wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff); |         wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wprintw(statusbar->topline, "}\n"); |     wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); |     wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |     wprintw(statusbar->topline, "} "); | ||||||
|  |     wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|     int y, x; |     int y; | ||||||
|  |     int x; | ||||||
|     getyx(self->window, y, x); |     getyx(self->window, y, x); | ||||||
|  |  | ||||||
|     UNUSED_VAR(x); |     UNUSED_VAR(x); | ||||||
|  |  | ||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||||
|     wmove(self->window, y + 1, new_x); |     wmove(self->window, y, new_x); | ||||||
|  |  | ||||||
|  |     draw_window_bar(self); | ||||||
|  |  | ||||||
|     wnoutrefresh(self->window); |     wnoutrefresh(self->window); | ||||||
|  |  | ||||||
| @@ -1327,10 +1365,35 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void chat_init_log(ToxWindow *self, Tox *m, const char *self_nick) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     char myid[TOX_ADDRESS_SIZE]; | ||||||
|  |     tox_self_get_address(m, (uint8_t *) myid); | ||||||
|  |  | ||||||
|  |     if (log_init(ctx->log, self_nick, myid, Friends.list[self->num].pub_key, LOG_TYPE_CHAT) != 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to initialize chat log."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (load_chat_history(self, ctx->log) != 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (Friends.list[self->num].logging_on) { | ||||||
|  |         if (log_enable(ctx->log) != 0) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static void chat_onInit(ToxWindow *self, Tox *m) | static void chat_onInit(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     curs_set(1); |     curs_set(1); | ||||||
|     int x2, y2; |  | ||||||
|  |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |     if (y2 <= 0 || x2 <= 0) { | ||||||
| @@ -1363,9 +1426,10 @@ static void chat_onInit(ToxWindow *self, Tox *m) | |||||||
|     /* Init subwindows */ |     /* Init subwindows */ | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     statusbar->topline = subwin(self->window, 2, x2, 0, 0); |     statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0); | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||||
|  |     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); | ||||||
|  |  | ||||||
|     ctx->hst = calloc(1, sizeof(struct history)); |     ctx->hst = calloc(1, sizeof(struct history)); | ||||||
|     ctx->log = calloc(1, sizeof(struct chatlog)); |     ctx->log = calloc(1, sizeof(struct chatlog)); | ||||||
| @@ -1377,19 +1441,9 @@ static void chat_onInit(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     line_info_init(ctx->hst); |     line_info_init(ctx->hst); | ||||||
|  |  | ||||||
|     char myid[TOX_ADDRESS_SIZE]; |     chat_init_log(self, m, nick); | ||||||
|     tox_self_get_address(m, (uint8_t *) myid); |  | ||||||
|  |  | ||||||
|     int log_ret = log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT); |     execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);  // Print log status to screen | ||||||
|     load_chat_history(self, ctx->log); |  | ||||||
|  |  | ||||||
|     if (!Friends.list[self->num].logging_on) { |  | ||||||
|         log_disable(ctx->log); |  | ||||||
|     } else if (log_ret == -1) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); |  | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |     scrollok(ctx->history, 0); | ||||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 0); |     wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 2) { |     if (argc < 2) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -50,7 +50,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     long int idx = strtol(argv[2], NULL, 10); |     long int idx = strtol(argv[2], NULL, 10); | ||||||
|  |  | ||||||
|     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { |     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -62,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     } else if (strcasecmp(inoutstr, "out") == 0) { |     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); |         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!ft) { |     if (!ft) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { |     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -85,25 +85,25 @@ void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int conferencenum = strtol(argv[1], NULL, 10); |     long int conferencenum = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { |     if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Invite err; |     Tox_Err_Conference_Invite err; | ||||||
|  |  | ||||||
|     if (!tox_conference_invite(m, self->num, conferencenum, &err)) { |     if (!tox_conference_invite(m, self->num, conferencenum, &err)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -113,7 +113,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|     UNUSED_VAR(argv); |     UNUSED_VAR(argv); | ||||||
|  |  | ||||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -122,7 +122,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|     uint8_t type = Friends.list[self->num].conference_invite.type; |     uint8_t type = Friends.list[self->num].conference_invite.type; | ||||||
|  |  | ||||||
|     if (!Friends.list[self->num].conference_invite.pending) { |     if (!Friends.list[self->num].conference_invite.pending) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -133,7 +133,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); |         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { |         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|     } else if (type == TOX_CONFERENCE_TYPE_AV) { |     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
| @@ -142,21 +142,21 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|                                                 audio_conference_callback, NULL); |                                                 audio_conference_callback, NULL); | ||||||
|  |  | ||||||
|         if (conferencenum == (uint32_t) -1) { |         if (conferencenum == (uint32_t) -1) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #else | #else | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|         return; |         return; | ||||||
| #endif | #endif | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { |     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||||
|         tox_conference_delete(m, conferencenum, NULL); |         tox_conference_delete(m, conferencenum, NULL); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -165,7 +165,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|  |  | ||||||
|     if (type == TOX_CONFERENCE_TYPE_AV) { |     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|         if (!init_conference_audio_input(m, conferencenum)) { |         if (!init_conference_audio_input(m, conferencenum)) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -177,26 +177,26 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int idx = strtol(argv[1], NULL, 10); |     long int idx = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { |     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); |     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||||
|  |  | ||||||
|     if (!ft) { |     if (!ft) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ft->state != FILE_TRANSFER_PENDING) { |     if (ft->state != FILE_TRANSFER_PENDING) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -213,12 +213,12 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|         goto on_recv_error; |         goto on_recv_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path); | ||||||
|  |  | ||||||
|     /* prep progress bar line */ |     /* prep progress bar line */ | ||||||
|     char progline[MAX_STR_SIZE]; |     char progline[MAX_STR_SIZE]; | ||||||
|     init_progress_bar(progline); |     init_progress_bar(progline); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||||
|  |  | ||||||
|     ft->line_id = self->chatwin->hst->line_end->id + 2; |     ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||||
|     ft->state = FILE_TRANSFER_STARTED; |     ft->state = FILE_TRANSFER_STARTED; | ||||||
| @@ -229,23 +229,23 @@ on_recv_error: | |||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: |         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: |         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_NOT_FOUND: |         case TOX_ERR_FILE_CONTROL_NOT_FOUND: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_SENDQ: |         case TOX_ERR_FILE_CONTROL_SENDQ: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||||
|             return; |             return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -257,7 +257,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     const char *errmsg = NULL; |     const char *errmsg = NULL; | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -266,21 +266,21 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     int path_len = strlen(path); |     int path_len = strlen(path); | ||||||
|  |  | ||||||
|     if (path_len >= MAX_STR_SIZE) { |     if (path_len >= MAX_STR_SIZE) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FILE *file_to_send = fopen(path, "r"); |     FILE *file_to_send = fopen(path, "r"); | ||||||
|  |  | ||||||
|     if (file_to_send == NULL) { |     if (file_to_send == NULL) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     off_t filesize = file_size(path); |     off_t filesize = file_size(path); | ||||||
|  |  | ||||||
|     if (filesize == 0) { |     if (filesize == 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); | ||||||
|         fclose(file_to_send); |         fclose(file_to_send); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -310,7 +310,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|  |  | ||||||
|     char sizestr[32]; |     char sizestr[32]; | ||||||
|     bytes_convert_str(sizestr, sizeof(sizestr), filesize); |     bytes_convert_str(sizestr, sizeof(sizestr), filesize); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr); | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
| @@ -338,7 +338,7 @@ on_send_error: | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); | ||||||
|     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); |     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); | ||||||
|     fclose(file_to_send); |     fclose(file_to_send); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										327
									
								
								src/conference.c
									
									
									
									
									
								
							
							
						
						
									
										327
									
								
								src/conference.c
									
									
									
									
									
								
							| @@ -65,6 +65,9 @@ | |||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
|  |  | ||||||
|  | #define MAX_CONFERENCE_NUM (MAX_WINDOWS_NUM - 2) | ||||||
|  | #define CONFERENCE_EVENT_WAIT 30 | ||||||
|  |  | ||||||
| static ConferenceChat conferences[MAX_CONFERENCE_NUM]; | static ConferenceChat conferences[MAX_CONFERENCE_NUM]; | ||||||
| static int max_conference_index = 0; | static int max_conference_index = 0; | ||||||
|  |  | ||||||
| @@ -100,6 +103,7 @@ static const char *conference_cmd_list[] = { | |||||||
|     "/quit", |     "/quit", | ||||||
|     "/requests", |     "/requests", | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |     "/ptt", | ||||||
|     "/sense", |     "/sense", | ||||||
| #endif | #endif | ||||||
|     "/status", |     "/status", | ||||||
| @@ -114,6 +118,25 @@ static const char *conference_cmd_list[] = { | |||||||
|  |  | ||||||
| static ToxWindow *new_conference_chat(uint32_t conferencenum); | static ToxWindow *new_conference_chat(uint32_t conferencenum); | ||||||
|  |  | ||||||
|  | void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length) | ||||||
|  | { | ||||||
|  |     ConferenceChat *chat = &conferences[conferencesnum]; | ||||||
|  |  | ||||||
|  |     if (!chat->active) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (length > CONFERENCE_MAX_TITLE_LENGTH) { | ||||||
|  |         length = CONFERENCE_MAX_TITLE_LENGTH; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(chat->title, title, length); | ||||||
|  |     chat->title[length] = 0; | ||||||
|  |     chat->title_length = length; | ||||||
|  |  | ||||||
|  |     set_window_title(self, title, length); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void kill_conference_window(ToxWindow *self) | static void kill_conference_window(ToxWindow *self) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
| @@ -130,8 +153,35 @@ static void kill_conference_window(ToxWindow *self) | |||||||
|     del_window(self); |     del_window(self); | ||||||
| } | } | ||||||
|  |  | ||||||
| int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, | static void init_conference_logging(ToxWindow *self, Tox *m, uint32_t conferencenum) | ||||||
|                         size_t title_length) | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     char my_id[TOX_ADDRESS_SIZE]; | ||||||
|  |     tox_self_get_address(m, (uint8_t *) my_id); | ||||||
|  |  | ||||||
|  |     char conference_id[TOX_CONFERENCE_ID_SIZE]; | ||||||
|  |     tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id); | ||||||
|  |  | ||||||
|  |     if (log_init(ctx->log, conferences[self->num].title, my_id, conference_id, LOG_TYPE_CHAT) != 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (load_chat_history(self, ctx->log) != 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (user_settings->autolog == AUTOLOG_ON) { | ||||||
|  |         if (log_enable(ctx->log) != 0) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);  // print log state to screen | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length) | ||||||
| { | { | ||||||
|     if (conferencenum > MAX_CONFERENCE_NUM) { |     if (conferencenum > MAX_CONFERENCE_NUM) { | ||||||
|         return -1; |         return -1; | ||||||
| @@ -154,8 +204,15 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char | |||||||
|             conferences[i].audio_enabled = false; |             conferences[i].audio_enabled = false; | ||||||
|             conferences[i].last_sent_audio = 0; |             conferences[i].last_sent_audio = 0; | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  |             conferences[i].push_to_talk_enabled = user_settings->push_to_talk; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|             set_active_window_index(conferences[i].chatwin); |             set_active_window_index(conferences[i].chatwin); | ||||||
|             set_window_title(self, title, title_length); |  | ||||||
|  |             conference_set_title(self, conferencenum, title, length); | ||||||
|  |  | ||||||
|  |             init_conference_logging(self, m, conferencenum); | ||||||
|  |  | ||||||
|             if (i == max_conference_index) { |             if (i == max_conference_index) { | ||||||
|                 ++max_conference_index; |                 ++max_conference_index; | ||||||
| @@ -225,6 +282,25 @@ static void delete_conference(ToxWindow *self, Tox *m, uint32_t conferencenum) | |||||||
|     free_conference(self, conferencenum); |     free_conference(self, conferencenum); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title) | ||||||
|  | { | ||||||
|  |     ConferenceChat *chat = &conferences[conferencenum]; | ||||||
|  |  | ||||||
|  |     if (!chat->active) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char myid[TOX_ADDRESS_SIZE]; | ||||||
|  |     tox_self_get_address(m, (uint8_t *) myid); | ||||||
|  |  | ||||||
|  |     char conference_id[TOX_CONFERENCE_ID_SIZE]; | ||||||
|  |     tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id); | ||||||
|  |  | ||||||
|  |     if (rename_logfile(chat->title, new_title, myid, conference_id, chat->chatwin) != 0) { | ||||||
|  |         fprintf(stderr, "Failed to rename conference log to `%s`\n", new_title); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* destroys and re-creates conference window with or without the peerlist */ | /* destroys and re-creates conference window with or without the peerlist */ | ||||||
| void redraw_conference_win(ToxWindow *self) | void redraw_conference_win(ToxWindow *self) | ||||||
| { | { | ||||||
| @@ -234,9 +310,9 @@ void redraw_conference_win(ToxWindow *self) | |||||||
|     refresh(); |     refresh(); | ||||||
|     clear(); |     clear(); | ||||||
|  |  | ||||||
|     int x2, y2; |     int x2; | ||||||
|     getmaxyx(stdscr, y2, x2); |     int y2; | ||||||
|     y2 -= 2; |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |     if (y2 <= 0 || x2 <= 0) { | ||||||
|         return; |         return; | ||||||
| @@ -249,20 +325,22 @@ void redraw_conference_win(ToxWindow *self) | |||||||
|  |  | ||||||
|     delwin(ctx->linewin); |     delwin(ctx->linewin); | ||||||
|     delwin(ctx->history); |     delwin(ctx->history); | ||||||
|  |     delwin(self->window_bar); | ||||||
|     delwin(self->window); |     delwin(self->window); | ||||||
|  |  | ||||||
|     self->window = newwin(y2, x2, 0, 0); |     self->window = newwin(y2, x2, 0, 0); | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); | ||||||
|  |     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||||
|  |  | ||||||
|     if (self->show_peerlist) { |     if (self->show_peerlist) { | ||||||
|         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); |         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); | ||||||
|         ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); |         ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); | ||||||
|     } else { |     } else { | ||||||
|         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |         ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |     scrollok(ctx->history, 0); | ||||||
|  |     wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum, | static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum, | ||||||
| @@ -289,12 +367,12 @@ static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t con | |||||||
|  |  | ||||||
|     /* Only play sound if mentioned by someone else */ |     /* Only play sound if mentioned by someone else */ | ||||||
|     if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { |     if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { | ||||||
|         sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL); |  | ||||||
|  |  | ||||||
|         if (self->active_box != -1) { |         if (self->active_box != -1) { | ||||||
|             box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); |             box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                         self->active_box, "%s %s", nick, msg); | ||||||
|         } else { |         } else { | ||||||
|             box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg); |             box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                        &self->active_box, self->name, "%s %s", nick, msg); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         nick_clr = RED; |         nick_clr = RED; | ||||||
| @@ -302,10 +380,7 @@ static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t con | |||||||
|         sound_notify(self, silent, NT_WNDALERT_1, NULL); |         sound_notify(self, silent, NT_WNDALERT_1, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     line_info_add(self, true, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg); |  | ||||||
|     write_to_log(msg, nick, ctx->log, false); |     write_to_log(msg, nick, ctx->log, false); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -319,19 +394,24 @@ static void conference_onConferenceTitleChange(ToxWindow *self, Tox *m, uint32_t | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     set_window_title(self, title, length); |     ConferenceChat *chat = &conferences[conferencenum]; | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     if (!chat->active) { | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     conference_rename_log_path(m, conferencenum, title);  // must be called first | ||||||
|  |  | ||||||
|  |     conference_set_title(self, conferencenum, title, length); | ||||||
|  |  | ||||||
|     /* don't announce title when we join the room */ |     /* don't announce title when we join the room */ | ||||||
|     if (!timed_out(conferences[self->num].start_time, CONFERENCE_EVENT_WAIT)) { |     if (!timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_conference_nick_truncate(m, nick, peernum, conferencenum); |     get_conference_nick_truncate(m, nick, peernum, conferencenum); | ||||||
|     line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); |     line_info_add(self, true, nick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); | ||||||
|  |  | ||||||
|     char tmp_event[MAX_STR_SIZE]; |     char tmp_event[MAX_STR_SIZE]; | ||||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); |     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||||
| @@ -492,6 +572,22 @@ static ConferencePeer *peer_in_conference(uint32_t conferencenum, uint32_t peern | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|  | /* Return true if ptt is disabled or enabled and active. */ | ||||||
|  | static bool conference_check_push_to_talk(ConferenceChat *chat) | ||||||
|  | { | ||||||
|  |     if (!chat->push_to_talk_enabled) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return !timed_out(chat->ptt_last_pushed, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void conference_enable_push_to_talk(ConferenceChat *chat) | ||||||
|  | { | ||||||
|  |     chat->ptt_last_pushed = get_unix_time(); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum) | static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum) | ||||||
| { | { | ||||||
|     ConferenceChat *chat = &conferences[conferencenum]; |     ConferenceChat *chat = &conferences[conferencenum]; | ||||||
| @@ -523,7 +619,7 @@ static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t pee | |||||||
|     const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2); |     const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2); | ||||||
|     set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0); |     set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0); | ||||||
| } | } | ||||||
| #endif | #endif // AUDIO | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx) | static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx) | ||||||
| @@ -532,7 +628,10 @@ static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, | |||||||
|         const ConferencePeer *peer = &list[i]; |         const ConferencePeer *peer = &list[i]; | ||||||
|  |  | ||||||
|         if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) { |         if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) { | ||||||
|  |             if (idx) { | ||||||
|                 *idx = i; |                 *idx = i; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -540,7 +639,8 @@ static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, uint32_t old_num_peers) | static void update_peer_list(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t num_peers, | ||||||
|  |                              uint32_t old_num_peers) | ||||||
| { | { | ||||||
|     ConferenceChat *chat = &conferences[conferencenum]; |     ConferenceChat *chat = &conferences[conferencenum]; | ||||||
|  |  | ||||||
| @@ -548,6 +648,8 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     ConferencePeer *old_peer_list = malloc(old_num_peers * sizeof(ConferencePeer)); |     ConferencePeer *old_peer_list = malloc(old_num_peers * sizeof(ConferencePeer)); | ||||||
|  |  | ||||||
|     if (!old_peer_list) { |     if (!old_peer_list) { | ||||||
| @@ -559,7 +661,11 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, | |||||||
|         memcpy(old_peer_list, chat->peer_list, old_num_peers * sizeof(ConferencePeer)); |         memcpy(old_peer_list, chat->peer_list, old_num_peers * sizeof(ConferencePeer)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     realloc_peer_list(chat, num_peers); |     if (realloc_peer_list(chat, num_peers) != 0) { | ||||||
|  |         free(old_peer_list); | ||||||
|  |         fprintf(stderr, "Warning: realloc_peer_list() failed in update_peer_list()\n"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     for (uint32_t i = 0; i < num_peers; ++i) { |     for (uint32_t i = 0; i < num_peers; ++i) { | ||||||
|         ConferencePeer *peer = &chat->peer_list[i]; |         ConferencePeer *peer = &chat->peer_list[i]; | ||||||
| @@ -575,12 +681,14 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         bool new_peer = true; | ||||||
|         uint32_t j; |         uint32_t j; | ||||||
|  |  | ||||||
|         if (find_peer_by_pubkey(old_peer_list, old_num_peers, peer->pubkey, &j)) { |         if (find_peer_by_pubkey(old_peer_list, old_num_peers, peer->pubkey, &j)) { | ||||||
|             ConferencePeer *old_peer = &old_peer_list[j]; |             ConferencePeer *old_peer = &old_peer_list[j]; | ||||||
|             memcpy(peer, old_peer, sizeof(ConferencePeer)); |             memcpy(peer, old_peer, sizeof(ConferencePeer)); | ||||||
|             old_peer->active = false; |             old_peer->active = false; | ||||||
|  |             new_peer = false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         size_t length = tox_conference_peer_get_name_size(m, conferencenum, i, &err); |         size_t length = tox_conference_peer_get_name_size(m, conferencenum, i, &err); | ||||||
| @@ -601,22 +709,34 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, | |||||||
|         peer->name_length = length; |         peer->name_length = length; | ||||||
|         peer->peernum = i; |         peer->peernum = i; | ||||||
|  |  | ||||||
|  |         if (new_peer && peer->name_length > 0 && timed_out(chat->start_time, CONFERENCE_EVENT_WAIT)) { | ||||||
|  |             const char *msg = "has joined the conference"; | ||||||
|  |             line_info_add(self, true, peer->name, NULL, CONNECTION, 0, GREEN, msg); | ||||||
|  |             write_to_log(msg, peer->name, ctx->log, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|         set_peer_audio_position(m, conferencenum, i); |         set_peer_audio_position(m, conferencenum, i); | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     conference_update_name_list(conferencenum); | ||||||
|  |  | ||||||
|     for (uint32_t i = 0; i < old_num_peers; ++i) { |     for (uint32_t i = 0; i < old_num_peers; ++i) { | ||||||
|         ConferencePeer *old_peer = &old_peer_list[i]; |         ConferencePeer *old_peer = &old_peer_list[i]; | ||||||
|  |  | ||||||
|         if (old_peer->active) { |         if (old_peer->active) { | ||||||
|  |             if (old_peer->name_length > 0 && !find_peer_by_pubkey(chat->peer_list, chat->num_peers, old_peer->pubkey, NULL)) { | ||||||
|  |                 const char *msg = "has left the conference"; | ||||||
|  |                 line_info_add(self, true, old_peer->name, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|  |                 write_to_log(msg, old_peer->name, ctx->log, true); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             free_peer(old_peer); |             free_peer(old_peer); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(old_peer_list); |     free(old_peer_list); | ||||||
|  |  | ||||||
|     conference_update_name_list(conferencenum); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint32_t conferencenum) | static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint32_t conferencenum) | ||||||
| @@ -648,7 +768,7 @@ static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint3 | |||||||
|  |  | ||||||
|     chat->num_peers = num_peers; |     chat->num_peers = num_peers; | ||||||
|     chat->max_idx = num_peers; |     chat->max_idx = num_peers; | ||||||
|     update_peer_list(m, conferencenum, num_peers, old_num); |     update_peer_list(self, m, conferencenum, num_peers, old_num); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum, | static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum, | ||||||
| @@ -662,17 +782,22 @@ static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint3 | |||||||
|  |  | ||||||
|     const ConferencePeer *peer = peer_in_conference(conferencenum, peernum); |     const ConferencePeer *peer = peer_in_conference(conferencenum, peernum); | ||||||
|  |  | ||||||
|     if (peer != NULL && peer->name_length > 0) { |     if (peer != NULL) { | ||||||
|         ChatContext *ctx = self->chatwin; |         ChatContext *ctx = self->chatwin; | ||||||
|         char timefrmt[TIME_STR_SIZE]; |  | ||||||
|         get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|         char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; |         if (peer->name_length > 0) { | ||||||
|         snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name); |             char log_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; | ||||||
|  |             line_info_add(self, true, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); | ||||||
|  |  | ||||||
|         write_to_log(tmp_event, peer->name, ctx->log, true); |             snprintf(log_event, sizeof(log_event), "is now known as %s", (const char *) name); | ||||||
|         line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); |             write_to_log(log_event, peer->name, ctx->log, true); | ||||||
|  |  | ||||||
|  |             // this is kind of a hack; peers always join a group with no name set and then set it after | ||||||
|  |         } else if (timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) { | ||||||
|  |             const char *msg = "has joined the conference"; | ||||||
|  |             line_info_add(self, true, name, NULL, CONNECTION, 0, GREEN, msg); | ||||||
|  |             write_to_log(msg, name, ctx->log, true); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     conference_onConferenceNameListChange(self, m, conferencenum); |     conference_onConferenceNameListChange(self, m, conferencenum); | ||||||
| @@ -688,7 +813,7 @@ static void send_conference_action(ToxWindow *self, ChatContext *ctx, Tox *m, ch | |||||||
|     Tox_Err_Conference_Send_Message err; |     Tox_Err_Conference_Send_Message err; | ||||||
|  |  | ||||||
|     if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &err)) { |     if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &err)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -698,7 +823,6 @@ static int sidebar_offset(uint32_t conferencenum) | |||||||
|     return 2 + conferences[conferencenum].audio_enabled; |     return 2 + conferences[conferencenum].audio_enabled; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Return true if input is recognized by handler |  * Return true if input is recognized by handler | ||||||
|  */ |  */ | ||||||
| @@ -741,6 +865,15 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|     bool input_ret = false; |     bool input_ret = false; | ||||||
|     ConferenceChat *chat = &conferences[self->num]; |     ConferenceChat *chat = &conferences[self->num]; | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  |  | ||||||
|  |     if (chat->audio_enabled && chat->push_to_talk_enabled && key == KEY_F(2)) { | ||||||
|  |         input_ret = true; | ||||||
|  |         conference_enable_push_to_talk(chat); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #endif // AUDIO | ||||||
|  |  | ||||||
|     if (key == L'\t') {  /* TAB key: auto-completes peer name or command */ |     if (key == L'\t') {  /* TAB key: auto-completes peer name or command */ | ||||||
|         input_ret = true; |         input_ret = true; | ||||||
|  |  | ||||||
| @@ -842,17 +975,19 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|                 } else { |                 } else { | ||||||
|                     execute(ctx->history, self, m, line, CONFERENCE_COMMAND_MODE); |                     execute(ctx->history, self, m, line, CONFERENCE_COMMAND_MODE); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else if (line[0]) { | ||||||
|                 Tox_Err_Conference_Send_Message err; |                 Tox_Err_Conference_Send_Message err; | ||||||
|  |  | ||||||
|                 if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) { |                 if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) { | ||||||
|                     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err); |                     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err); | ||||||
|                 } |                 } | ||||||
|  |             } else { | ||||||
|  |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |         wclear(ctx->linewin); | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |         wmove(self->window, y2, 0); | ||||||
|         reset_buf(ctx); |         reset_buf(ctx); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -866,15 +1001,9 @@ static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i) | |||||||
|     const uint32_t peernum = conferences[self->num].name_list[peer_idx].peernum; |     const uint32_t peernum = conferences[self->num].name_list[peer_idx].peernum; | ||||||
|     const bool is_self = tox_conference_peer_number_is_ours(m, self->num, peernum, NULL); |     const bool is_self = tox_conference_peer_number_is_ours(m, self->num, peernum, NULL); | ||||||
|     const bool audio = conferences[self->num].audio_enabled; |     const bool audio = conferences[self->num].audio_enabled; | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|     /* truncate nick to fit in side panel without modifying list */ |  | ||||||
|     char tmpnick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|     int maxlen = SIDEBAR_WIDTH - 2 - 2 * audio; |  | ||||||
|  |  | ||||||
|     if (audio) { |     if (audio) { | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|         pthread_mutex_lock(&Winthread.lock); |  | ||||||
|         const ConferencePeer *peer = peer_in_conference(self->num, peernum); |         const ConferencePeer *peer = peer_in_conference(self->num, peernum); | ||||||
|         const bool audio_active = is_self |         const bool audio_active = is_self | ||||||
|                                   ? !timed_out(conferences[self->num].last_sent_audio, 2) |                                   ? !timed_out(conferences[self->num].last_sent_audio, 2) | ||||||
| @@ -891,8 +1020,14 @@ static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i) | |||||||
|         wattroff(ctx->sidebar, aud_attr); |         wattroff(ctx->sidebar, aud_attr); | ||||||
|         waddch(ctx->sidebar, ' '); |         waddch(ctx->sidebar, ' '); | ||||||
| #endif | #endif | ||||||
|  |     } else { | ||||||
|  |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* truncate nick to fit in side panel without modifying list */ | ||||||
|  |     char tmpnick[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     int maxlen = SIDEBAR_WIDTH - 2 - 2 * audio; | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|     memcpy(tmpnick, &conferences[self->num].name_list[peer_idx].name, maxlen); |     memcpy(tmpnick, &conferences[self->num].name_list[peer_idx].name, maxlen); | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
| @@ -921,6 +1056,12 @@ static void conference_onDraw(ToxWindow *self, Tox *m) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ConferenceChat *chat = &conferences[self->num]; | ||||||
|  |  | ||||||
|  |     if (!chat->active) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
| @@ -932,19 +1073,20 @@ static void conference_onDraw(ToxWindow *self, Tox *m) | |||||||
|     curs_set(1); |     curs_set(1); | ||||||
|  |  | ||||||
|     if (ctx->len > 0) { |     if (ctx->len > 0) { | ||||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); |         mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wclear(ctx->sidebar); |     wclear(ctx->sidebar); | ||||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); |  | ||||||
|  |  | ||||||
|     if (self->show_peerlist) { |     if (self->show_peerlist) { | ||||||
|  |         wattron(ctx->sidebar, COLOR_PAIR(BLUE)); | ||||||
|         mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); |         mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); | ||||||
|         mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); |         mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); | ||||||
|  |         wattroff(ctx->sidebar, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         const uint32_t num_peers = conferences[self->num].num_peers; |         const uint32_t num_peers = chat->num_peers; | ||||||
|         const bool audio = conferences[self->num].audio_enabled; |         const bool audio = chat->audio_enabled; | ||||||
|         const int header_lines = sidebar_offset(self->num); |         const int header_lines = sidebar_offset(self->num); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
| @@ -953,32 +1095,45 @@ static void conference_onDraw(ToxWindow *self, Tox *m) | |||||||
|         if (audio) { |         if (audio) { | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|             pthread_mutex_lock(&Winthread.lock); |             pthread_mutex_lock(&Winthread.lock); | ||||||
|             const bool mic_on = !device_is_muted(input, conferences[self->num].audio_in_idx); |             const bool ptt_idle = !conference_check_push_to_talk(chat) && chat->push_to_talk_enabled; | ||||||
|  |             const bool mic_on = !device_is_muted(input, chat->audio_in_idx); | ||||||
|             const float volume = get_input_volume(); |             const float volume = get_input_volume(); | ||||||
|             const float threshold = device_get_VAD_threshold(conferences[self->num].audio_in_idx); |             const float threshold = device_get_VAD_threshold(chat->audio_in_idx); | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |             pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|             wmove(ctx->sidebar, line, 1); |             wmove(ctx->sidebar, line, 1); | ||||||
|             wattron(ctx->sidebar, A_BOLD); |             wattron(ctx->sidebar, A_BOLD); | ||||||
|             wprintw(ctx->sidebar, "Mic: "); |             wprintw(ctx->sidebar, "Mic: "); | ||||||
|             const int color = mic_on && volume > threshold ? GREEN : RED; |  | ||||||
|  |             if (!mic_on) { | ||||||
|  |                 wattron(ctx->sidebar, COLOR_PAIR(RED)); | ||||||
|  |                 wprintw(ctx->sidebar, "MUTED"); | ||||||
|  |                 wattroff(ctx->sidebar, COLOR_PAIR(RED)); | ||||||
|  |             } else if (ptt_idle)  { | ||||||
|  |                 wattron(ctx->sidebar, COLOR_PAIR(GREEN)); | ||||||
|  |                 wprintw(ctx->sidebar, "PTT"); | ||||||
|  |                 wattroff(ctx->sidebar, COLOR_PAIR(GREEN)); | ||||||
|  |             }  else { | ||||||
|  |                 const int color = volume > threshold ? GREEN : RED; | ||||||
|                 wattron(ctx->sidebar, COLOR_PAIR(color)); |                 wattron(ctx->sidebar, COLOR_PAIR(color)); | ||||||
|  |  | ||||||
|             if (mic_on) { |  | ||||||
|                 float v = volume; |                 float v = volume; | ||||||
|  |  | ||||||
|  |                 if (v <= 0.0f) { | ||||||
|  |                     wprintw(ctx->sidebar, "."); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 while (v > 0.0f) { |                 while (v > 0.0f) { | ||||||
|                     wprintw(ctx->sidebar, v > 10.0f ? (v > 15.0f ? "*" : "+") : (v > 5.0f ? "-" : ".")); |                     wprintw(ctx->sidebar, v > 10.0f ? (v > 15.0f ? "*" : "+") : (v > 5.0f ? "-" : ".")); | ||||||
|                     v -= 20.0f; |                     v -= 20.0f; | ||||||
|                 } |                 } | ||||||
|             } else { |  | ||||||
|                 wprintw(ctx->sidebar, "OFF"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|                 wattroff(ctx->sidebar, COLOR_PAIR(color)); |                 wattroff(ctx->sidebar, COLOR_PAIR(color)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             wattroff(ctx->sidebar, A_BOLD); |             wattroff(ctx->sidebar, A_BOLD); | ||||||
|             ++line; |             ++line; | ||||||
| #endif | #endif  // AUDIO | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wmove(ctx->sidebar, line, 1); |         wmove(ctx->sidebar, line, 1); | ||||||
| @@ -987,8 +1142,10 @@ static void conference_onDraw(ToxWindow *self, Tox *m) | |||||||
|         wattroff(ctx->sidebar, A_BOLD); |         wattroff(ctx->sidebar, A_BOLD); | ||||||
|         ++line; |         ++line; | ||||||
|  |  | ||||||
|  |         wattron(ctx->sidebar, COLOR_PAIR(BLUE)); | ||||||
|         mvwaddch(ctx->sidebar, line, 0, ACS_LTEE); |         mvwaddch(ctx->sidebar, line, 0, ACS_LTEE); | ||||||
|         mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); |         mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); | ||||||
|  |         wattroff(ctx->sidebar, COLOR_PAIR(BLUE)); | ||||||
|         ++line; |         ++line; | ||||||
|  |  | ||||||
|         for (uint32_t i = 0; |         for (uint32_t i = 0; | ||||||
| @@ -1005,7 +1162,9 @@ static void conference_onDraw(ToxWindow *self, Tox *m) | |||||||
|     UNUSED_VAR(x); |     UNUSED_VAR(x); | ||||||
|  |  | ||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||||
|     wmove(self->window, y + 1, new_x); |     wmove(self->window, y, new_x); | ||||||
|  |  | ||||||
|  |     draw_window_bar(self); | ||||||
|  |  | ||||||
|     wnoutrefresh(self->window); |     wnoutrefresh(self->window); | ||||||
|  |  | ||||||
| @@ -1025,9 +1184,10 @@ static void conference_onInit(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); |     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); | ||||||
|  |     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); | ||||||
|     ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); |     ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); | ||||||
|  |  | ||||||
|     ctx->hst = calloc(1, sizeof(struct history)); |     ctx->hst = calloc(1, sizeof(struct history)); | ||||||
|     ctx->log = calloc(1, sizeof(struct chatlog)); |     ctx->log = calloc(1, sizeof(struct chatlog)); | ||||||
| @@ -1038,17 +1198,6 @@ static void conference_onInit(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     line_info_init(ctx->hst); |     line_info_init(ctx->hst); | ||||||
|  |  | ||||||
|     if (user_settings->autolog == AUTOLOG_ON) { |  | ||||||
|         char myid[TOX_ADDRESS_SIZE]; |  | ||||||
|         tox_self_get_address(m, (uint8_t *) myid); |  | ||||||
|  |  | ||||||
|         if (log_enable(self->name, myid, NULL, ctx->log, LOG_CONFERENCE) == -1) { |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); |  | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |     scrollok(ctx->history, 0); | ||||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 0); |     wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||||
| } | } | ||||||
| @@ -1094,7 +1243,6 @@ static ToxWindow *new_conference_chat(uint32_t conferencenum) | |||||||
|  |  | ||||||
| #define CONFAV_SAMPLE_RATE 48000 | #define CONFAV_SAMPLE_RATE 48000 | ||||||
| #define CONFAV_FRAME_DURATION 20 | #define CONFAV_FRAME_DURATION 20 | ||||||
| #define CONFAV_AUDIO_CHANNELS 1 |  | ||||||
| #define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000) | #define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000) | ||||||
|  |  | ||||||
| void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm, | void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm, | ||||||
| @@ -1131,12 +1279,20 @@ static void conference_read_device_callback(const int16_t *captured, uint32_t si | |||||||
|  |  | ||||||
|     AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data; |     AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data; | ||||||
|  |  | ||||||
|     conferences[audio_input_callback_data->conferencenum].last_sent_audio = get_unix_time(); |     ConferenceChat *chat = &conferences[audio_input_callback_data->conferencenum]; | ||||||
|  |  | ||||||
|  |     if (!conference_check_push_to_talk(chat)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     chat->last_sent_audio = get_unix_time(); | ||||||
|  |  | ||||||
|  |     int channels = user_settings->conference_audio_channels; | ||||||
|  |  | ||||||
|     toxav_group_send_audio(audio_input_callback_data->tox, |     toxav_group_send_audio(audio_input_callback_data->tox, | ||||||
|                            audio_input_callback_data->conferencenum, |                            audio_input_callback_data->conferencenum, | ||||||
|                            captured, CONFAV_SAMPLES_PER_FRAME, |                            captured, CONFAV_SAMPLES_PER_FRAME, | ||||||
|                            CONFAV_AUDIO_CHANNELS, CONFAV_SAMPLE_RATE); |                            channels, CONFAV_SAMPLE_RATE); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool init_conference_audio_input(Tox *tox, uint32_t conferencenum) | bool init_conference_audio_input(Tox *tox, uint32_t conferencenum) | ||||||
| @@ -1150,9 +1306,11 @@ bool init_conference_audio_input(Tox *tox, uint32_t conferencenum) | |||||||
|     const AudioInputCallbackData audio_input_callback_data = { tox, conferencenum }; |     const AudioInputCallbackData audio_input_callback_data = { tox, conferencenum }; | ||||||
|     chat->audio_input_callback_data = audio_input_callback_data; |     chat->audio_input_callback_data = audio_input_callback_data; | ||||||
|  |  | ||||||
|  |     int channels = user_settings->conference_audio_channels; | ||||||
|  |  | ||||||
|     bool success = (open_input_device(&chat->audio_in_idx, |     bool success = (open_input_device(&chat->audio_in_idx, | ||||||
|                                       conference_read_device_callback, &chat->audio_input_callback_data, true, |                                       conference_read_device_callback, &chat->audio_input_callback_data, true, | ||||||
|                                       CONFAV_SAMPLE_RATE, CONFAV_FRAME_DURATION, CONFAV_AUDIO_CHANNELS) |                                       CONFAV_SAMPLE_RATE, CONFAV_FRAME_DURATION, channels) | ||||||
|                     == de_None); |                     == de_None); | ||||||
|  |  | ||||||
|     chat->audio_enabled = success; |     chat->audio_enabled = success; | ||||||
| @@ -1160,6 +1318,19 @@ bool init_conference_audio_input(Tox *tox, uint32_t conferencenum) | |||||||
|     return success; |     return success; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled) | ||||||
|  | { | ||||||
|  |     ConferenceChat *chat = &conferences[conferencenum]; | ||||||
|  |  | ||||||
|  |     if (!chat->active) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     chat->push_to_talk_enabled = enabled; | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool enable_conference_audio(Tox *tox, uint32_t conferencenum) | bool enable_conference_audio(Tox *tox, uint32_t conferencenum) | ||||||
| { | { | ||||||
|     if (!toxav_groupchat_av_enabled(tox, conferencenum)) { |     if (!toxav_groupchat_av_enabled(tox, conferencenum)) { | ||||||
| @@ -1244,4 +1415,4 @@ float conference_get_VAD_threshold(uint32_t conferencenum) | |||||||
|  |  | ||||||
|     return device_get_VAD_threshold(chat->audio_in_idx); |     return device_get_VAD_threshold(chat->audio_in_idx); | ||||||
| } | } | ||||||
| #endif | #endif  // AUDIO | ||||||
|   | |||||||
| @@ -26,9 +26,8 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
|  | #define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH | ||||||
| #define SIDEBAR_WIDTH 16 | #define SIDEBAR_WIDTH 16 | ||||||
| #define MAX_CONFERENCE_NUM MAX_WINDOWS_NUM - 2 |  | ||||||
| #define CONFERENCE_EVENT_WAIT 3 |  | ||||||
|  |  | ||||||
| typedef struct ConferencePeer { | typedef struct ConferencePeer { | ||||||
|     bool       active; |     bool       active; | ||||||
| @@ -64,12 +63,18 @@ typedef struct { | |||||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ |     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||||
|     time_t start_time; |     time_t start_time; | ||||||
|  |  | ||||||
|  |     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||||
|  |     size_t title_length; | ||||||
|  |  | ||||||
|     ConferencePeer *peer_list; |     ConferencePeer *peer_list; | ||||||
|     uint32_t max_idx; |     uint32_t max_idx; | ||||||
|  |  | ||||||
|     NameListEntry *name_list; |     NameListEntry *name_list; | ||||||
|     uint32_t num_peers; |     uint32_t num_peers; | ||||||
|  |  | ||||||
|  |     bool push_to_talk_enabled; | ||||||
|  |     time_t ptt_last_pushed; | ||||||
|  |  | ||||||
|     bool audio_enabled; |     bool audio_enabled; | ||||||
|     time_t last_sent_audio; |     time_t last_sent_audio; | ||||||
|     uint32_t audio_in_idx; |     uint32_t audio_in_idx; | ||||||
| @@ -79,11 +84,15 @@ typedef struct { | |||||||
| /* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */ | /* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */ | ||||||
| void free_conference(ToxWindow *self, uint32_t conferencenum); | void free_conference(ToxWindow *self, uint32_t conferencenum); | ||||||
|  |  | ||||||
| int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t title_length); | int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length); | ||||||
|  |  | ||||||
| /* destroys and re-creates conference window with or without the peerlist */ | /* destroys and re-creates conference window with or without the peerlist */ | ||||||
| void redraw_conference_win(ToxWindow *self); | void redraw_conference_win(ToxWindow *self); | ||||||
|  |  | ||||||
|  | void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length); | ||||||
|  | void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title); | ||||||
|  | int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log); | ||||||
|  |  | ||||||
| /* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum | /* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum | ||||||
|  * of `maxpeers`. |  * of `maxpeers`. | ||||||
|  * Maches each peer whose name or pubkey begins with `prefix`. |  * Maches each peer whose name or pubkey begins with `prefix`. | ||||||
| @@ -96,6 +105,7 @@ uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *pre | |||||||
| bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); | bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); | ||||||
| bool enable_conference_audio(Tox *tox, uint32_t conferencenum); | bool enable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||||
| bool disable_conference_audio(Tox *tox, uint32_t conferencenum); | bool disable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||||
|  | bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled); | ||||||
| void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, | void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, | ||||||
|                                const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t |                                const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t | ||||||
|                                sample_rate, void *userdata); |                                sample_rate, void *userdata); | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ | |||||||
|  |  | ||||||
| static void print_err(ToxWindow *self, const char *error_str) | static void print_err(ToxWindow *self, const char *error_str) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -40,7 +40,7 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     Tox_Err_Conference_Title err; |     Tox_Err_Conference_Title err; | ||||||
|     char title[MAX_STR_SIZE]; |     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         size_t tlen = tox_conference_get_title_size(m, self->num, &err); |         size_t tlen = tox_conference_get_title_size(m, self->num, &err); | ||||||
| @@ -56,31 +56,36 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         title[tlen] = '\0'; |         title[tlen] = '\0'; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     size_t len = strlen(argv[1]); | ||||||
|  |  | ||||||
|  |     if (len >= sizeof(title)) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     snprintf(title, sizeof(title), "%s", argv[1]); |     snprintf(title, sizeof(title), "%s", argv[1]); | ||||||
|     int len = strlen(title); |  | ||||||
|  |  | ||||||
|     if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { |     if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     set_window_title(self, title, len); |     conference_rename_log_path(m, self->num, title);  // must be called first | ||||||
|  |  | ||||||
|  |     conference_set_title(self, self->num, title, len); | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; |     char selfnick[TOX_MAX_NAME_LENGTH]; | ||||||
|  |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     tox_self_get_name(m, (uint8_t *) selfnick); |     tox_self_get_name(m, (uint8_t *) selfnick); | ||||||
|  |  | ||||||
|     size_t sn_len = tox_self_get_name_size(m); |     size_t sn_len = tox_self_get_name_size(m); | ||||||
|     selfnick[sn_len] = '\0'; |     selfnick[sn_len] = '\0'; | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); |     line_info_add(self, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); | ||||||
|  |  | ||||||
|     char tmp_event[MAX_STR_SIZE + 20]; |     char tmp_event[MAX_STR_SIZE + 20]; | ||||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); |     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||||
| @@ -104,7 +109,8 @@ void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (* | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { |     if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { | ||||||
|         print_err(self, enable ? "Enabled conference audio" : "Disabled conference audio"); |         print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk." | ||||||
|  |                   : "Disabled conference audio"); | ||||||
|     } else { |     } else { | ||||||
|         print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); |         print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); | ||||||
|     } |     } | ||||||
| @@ -133,16 +139,16 @@ void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char | |||||||
|             print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); |             print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); | ||||||
|  |  | ||||||
|             for (uint32_t i = 0; i < n; ++i) { |             for (uint32_t i = 0; i < n; ++i) { | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (conference_mute_peer(m, self->num, entries[0]->peernum)) { |         if (conference_mute_peer(m, self->num, entries[0]->peernum)) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); | ||||||
|         } else { |         } else { | ||||||
|             print_err(self, "No such audio peer"); |             print_err(self, "Peer is not on the call"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -153,7 +159,7 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha | |||||||
|     UNUSED_VAR(m); |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", | ||||||
|                       (double) conference_get_VAD_threshold(self->num)); |                       (double) conference_get_VAD_threshold(self->num)); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -172,9 +178,33 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (conference_set_VAD_threshold(self->num, value)) { |     if (conference_set_VAD_threshold(self->num, value)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); | ||||||
|     } else { |     } else { | ||||||
|         print_err(self, "Failed to set conference audio input sensitivity."); |         print_err(self, "Failed to set conference audio input sensitivity."); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|  |     bool enable; | ||||||
|  |  | ||||||
|  |     if (argc == 1 && !strcasecmp(argv[1], "on")) { | ||||||
|  |         enable = true; | ||||||
|  |     } else if (argc == 1 && !strcasecmp(argv[1], "off")) { | ||||||
|  |         enable = false; | ||||||
|  |     } else { | ||||||
|  |         print_err(self, "Please specify: on | off"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!toggle_conference_push_to_talk(self->num, enable)) { | ||||||
|  |         print_err(self, "Failed to toggle push to talk."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled"); | ||||||
|  | } | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|   | |||||||
| @@ -30,5 +30,6 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
| void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
| #endif /* CONFERENCE_COMMANDS_H */ | #endif /* CONFERENCE_COMMANDS_H */ | ||||||
|   | |||||||
| @@ -105,6 +105,7 @@ static struct cmd_func conference_commands[] = { | |||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     { "/audio",     cmd_enable_audio }, |     { "/audio",     cmd_enable_audio }, | ||||||
|     { "/mute",      cmd_conference_mute   }, |     { "/mute",      cmd_conference_mute   }, | ||||||
|  |     { "/ptt",       cmd_conference_push_to_talk }, | ||||||
|     { "/sense",     cmd_conference_sense  }, |     { "/sense",     cmd_conference_sense  }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|     { NULL,         NULL             }, |     { NULL,         NULL             }, | ||||||
| @@ -270,5 +271,5 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | |||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -60,13 +60,15 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char pct_str[STR_BUF_SIZE] = {0}; |     char pct_str[STR_BUF_SIZE]; | ||||||
|     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); |     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||||
|  |  | ||||||
|     char bps_str[STR_BUF_SIZE] = {0}; |     char bps_str[STR_BUF_SIZE]; | ||||||
|     bytes_convert_str(bps_str, sizeof(bps_str), bps); |     bytes_convert_str(bps_str, sizeof(bps_str), bps); | ||||||
|  |  | ||||||
|     char prog_line[NUM_PROG_MARKS + 1] = {0}; |     char prog_line[NUM_PROG_MARKS + 1]; | ||||||
|  |     prog_line[0] = 0; | ||||||
|  |  | ||||||
|     int n = pct_done / (100 / NUM_PROG_MARKS); |     int n = pct_done / (100 / NUM_PROG_MARKS); | ||||||
|     int i, j; |     int i, j; | ||||||
|  |  | ||||||
| @@ -272,7 +274,7 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C | |||||||
|             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); |             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     clear_file_transfer(ft); |     clear_file_transfer(ft); | ||||||
|   | |||||||
| @@ -397,11 +397,8 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Mess | |||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     line_info_add(prompt, true, nick, NULL, IN_MSG, 0, 0, "%s", str); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |     line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "* Warning: Too many windows are open."); | ||||||
|  |  | ||||||
|     line_info_add(prompt, timefrmt, nick, NULL, IN_MSG, 0, 0, "%s", str); |  | ||||||
|     line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Warning: Too many windows are open."); |  | ||||||
|     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -453,7 +450,9 @@ static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const | |||||||
|     tox_self_get_address(m, (uint8_t *) myid); |     tox_self_get_address(m, (uint8_t *) myid); | ||||||
|  |  | ||||||
|     if (strcmp(oldname, newnamecpy) != 0) { |     if (strcmp(oldname, newnamecpy) != 0) { | ||||||
|         rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin); |         if (rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin) != 0) { | ||||||
|  |             fprintf(stderr, "Failed to rename friend chat log from `%s` to `%s`\n", oldname, newnamecpy); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sort_friendlist_index(); |     sort_friendlist_index(); | ||||||
| @@ -609,7 +608,7 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_ | |||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, |     line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, | ||||||
|                   "* File transfer from %s failed: too many windows are open.", nick); |                   "* File transfer from %s failed: too many windows are open.", nick); | ||||||
|  |  | ||||||
|     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
| @@ -640,7 +639,7 @@ static void friendlist_onConferenceInvite(ToxWindow *self, Tox *m, int32_t num, | |||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, |     line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, | ||||||
|                   "* Conference chat invite from %s failed: too many windows are open.", nick); |                   "* Conference chat invite from %s failed: too many windows are open.", nick); | ||||||
|  |  | ||||||
|     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
| @@ -805,7 +804,7 @@ static void delete_blocked_friend(uint32_t bnum) | |||||||
| /* deletes contact from friendlist and puts in blocklist */ | /* deletes contact from friendlist and puts in blocklist */ | ||||||
| void block_friend(Tox *m, uint32_t fnum) | void block_friend(Tox *m, uint32_t fnum) | ||||||
| { | { | ||||||
|     if (Friends.num_friends <= 0) { |     if (Friends.num_friends == 0) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -852,7 +851,7 @@ static void unblock_friend(Tox *m, uint32_t bnum) | |||||||
|     uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err); |     uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { |     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err); |         line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -921,7 +920,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|                 set_active_window_index(Friends.list[f].chatwin); |                 set_active_window_index(Friends.list[f].chatwin); | ||||||
|             } else { |             } else { | ||||||
|                 const char *msg = "* Warning: Too many windows are open."; |                 const char *msg = "* Warning: Too many windows are open."; | ||||||
|                 line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, msg); |                 line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, msg); | ||||||
|                 sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |                 sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1020,7 +1019,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2) | |||||||
|         wmove(self->window, y2 - 1, 1); |         wmove(self->window, y2 - 1, 1); | ||||||
|  |  | ||||||
|         wattron(self->window, A_BOLD); |         wattron(self->window, A_BOLD); | ||||||
|         wprintw(self->window, "Key: "); |         wprintw(self->window, "Public key: "); | ||||||
|         wattroff(self->window, A_BOLD); |         wattroff(self->window, A_BOLD); | ||||||
|  |  | ||||||
|         int i; |         int i; | ||||||
| @@ -1055,6 +1054,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|     wprintw(self->window, "key for help\n\n"); |     wprintw(self->window, "key for help\n\n"); | ||||||
|     wattroff(self->window, COLOR_PAIR(CYAN)); |     wattroff(self->window, COLOR_PAIR(CYAN)); | ||||||
|  |  | ||||||
|  |     draw_window_bar(self); | ||||||
|  |  | ||||||
|     if (blocklist_view == 1) { |     if (blocklist_view == 1) { | ||||||
|         blocklist_onDraw(self, m, y2, x2); |         blocklist_onDraw(self, m, y2, x2); | ||||||
|         return; |         return; | ||||||
| @@ -1096,9 +1097,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|         int num_selected = Friends.num_selected; |         int num_selected = Friends.num_selected; | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|  |         if (is_active) { | ||||||
|             bool f_selected = false; |             bool f_selected = false; | ||||||
|  |  | ||||||
|         if (is_active) { |  | ||||||
|             if (i == num_selected) { |             if (i == num_selected) { | ||||||
|                 wattron(self->window, A_BOLD); |                 wattron(self->window, A_BOLD); | ||||||
|                 wprintw(self->window, " > "); |                 wprintw(self->window, " > "); | ||||||
| @@ -1245,7 +1246,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|         wmove(self->window, y2 - 1, 1); |         wmove(self->window, y2 - 1, 1); | ||||||
|  |  | ||||||
|         wattron(self->window, A_BOLD); |         wattron(self->window, A_BOLD); | ||||||
|         wprintw(self->window, "Key: "); |         wprintw(self->window, "Public key: "); | ||||||
|         wattroff(self->window, A_BOLD); |         wattroff(self->window, A_BOLD); | ||||||
|  |  | ||||||
|         int i; |         int i; | ||||||
| @@ -1263,6 +1264,21 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void friendlist_onInit(ToxWindow *self, Tox *m) | ||||||
|  | { | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|  |     int x2; | ||||||
|  |     int y2; | ||||||
|  |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) { | ||||||
|  |         exit_toxic_err("failed in friendlist_onInit", FATALERR_CURSES); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - 2, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
| void disable_chatwin(uint32_t f_num) | void disable_chatwin(uint32_t f_num) | ||||||
| { | { | ||||||
|     Friends.list[f_num].chatwin = -1; |     Friends.list[f_num].chatwin = -1; | ||||||
| @@ -1288,10 +1304,10 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, | |||||||
|         } else { |         } else { | ||||||
|             char nick[TOX_MAX_NAME_LENGTH]; |             char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|             get_nick_truncate(m, nick, Friends.list[friend_number].num); |             get_nick_truncate(m, nick, Friends.list[friend_number].num); | ||||||
|             line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); |             line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); | ||||||
|  |  | ||||||
|             const char *errmsg = "* Warning: Too many windows are open."; |             const char *errmsg = "* Warning: Too many windows are open."; | ||||||
|             line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg); |             line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, errmsg); | ||||||
|  |  | ||||||
|             sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |             sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
|         } |         } | ||||||
| @@ -1341,6 +1357,7 @@ ToxWindow *new_friendlist(void) | |||||||
|  |  | ||||||
|     ret->type = WINDOW_TYPE_FRIEND_LIST; |     ret->type = WINDOW_TYPE_FRIEND_LIST; | ||||||
|  |  | ||||||
|  |     ret->onInit = &friendlist_onInit; | ||||||
|     ret->onKey = &friendlist_onKey; |     ret->onKey = &friendlist_onKey; | ||||||
|     ret->onDraw = &friendlist_onDraw; |     ret->onDraw = &friendlist_onDraw; | ||||||
|     ret->onFriendAdded = &friendlist_onFriendAdded; |     ret->onFriendAdded = &friendlist_onFriendAdded; | ||||||
| @@ -1376,6 +1393,6 @@ ToxWindow *new_friendlist(void) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret->help = help; |     ret->help = help; | ||||||
|     strcpy(ret->name, "contacts"); |     strcpy(ret->name, "Contacts"); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -82,6 +82,7 @@ typedef struct { | |||||||
| } FriendsList; | } FriendsList; | ||||||
|  |  | ||||||
| ToxWindow *new_friendlist(void); | ToxWindow *new_friendlist(void); | ||||||
|  | void friendlist_onInit(ToxWindow *self, Tox *m); | ||||||
| void disable_chatwin(uint32_t f_num); | void disable_chatwin(uint32_t f_num); | ||||||
| int get_friendnum(uint8_t *name); | int get_friendnum(uint8_t *name); | ||||||
| int load_blocklist(char *data); | int load_blocklist(char *data); | ||||||
|   | |||||||
| @@ -49,19 +49,19 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int req = strtol(argv[1], NULL, 10); |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!FrndRequests.request[req].active) { |     if (!FrndRequests.request[req].active) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -69,10 +69,10 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); |     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { |     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||||
|         return; |         return; | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||||
|         on_friend_added(m, friendnum, true); |         on_friend_added(m, friendnum, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -141,7 +141,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -149,7 +149,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -158,7 +158,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|  |  | ||||||
|     if (argc > 1) { |     if (argc > 1) { | ||||||
|         if (argv[2][0] != '\"') { |         if (argv[2][0] != '\"') { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -192,7 +192,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|             xx[2] = '\0'; |             xx[2] = '\0'; | ||||||
|  |  | ||||||
|             if (sscanf(xx, "%02x", &x) != 1) { |             if (sscanf(xx, "%02x", &x) != 1) { | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -200,7 +200,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (friend_is_blocked(id_bin)) { |         if (friend_is_blocked(id_bin)) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list."); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -216,7 +216,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|  |  | ||||||
|     if (argc != 1 || strlen(argv[1]) < 3) { |     if (argc != 1 || strlen(argv[1]) < 3) { | ||||||
|         avatar_unset(m); |         avatar_unset(m); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -225,7 +225,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|     int len = strlen(path); |     int len = strlen(path); | ||||||
|  |  | ||||||
|     if (len <= 0) { |     if (len <= 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -234,13 +234,13 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|     get_file_name(filename, sizeof(filename), path); |     get_file_name(filename, sizeof(filename), path); | ||||||
|  |  | ||||||
|     if (avatar_set(m, path, len) == -1) { |     if (avatar_set(m, path, len) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", |                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", | ||||||
|                       MAX_AVATAR_FILE_SIZE); |                       MAX_AVATAR_FILE_SIZE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -258,7 +258,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc != 3) { |     if (argc != 3) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -269,14 +269,14 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     long int port = strtol(port_str, NULL, 10); |     long int port = strtol(port_str, NULL, 10); | ||||||
|  |  | ||||||
|     if (port <= 0 || port > MAX_PORT_RANGE) { |     if (port <= 0 || port > MAX_PORT_RANGE) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; |     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||||
|  |  | ||||||
|     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { |     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -286,15 +286,15 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: |         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_BOOTSTRAP_BAD_PORT: |         case TOX_ERR_BOOTSTRAP_BAD_PORT: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_BOOTSTRAP_NULL: |         case TOX_ERR_BOOTSTRAP_NULL: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
| @@ -308,19 +308,19 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     UNUSED_VAR(m); |     UNUSED_VAR(m); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int req = strtol(argv[1], NULL, 10); |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!FrndRequests.request[req].active) { |     if (!FrndRequests.request[req].active) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -345,12 +345,12 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -361,11 +361,11 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     } else if (!strcasecmp(argv[1], "text")) { |     } else if (!strcasecmp(argv[1], "text")) { | ||||||
|         type = TOX_CONFERENCE_TYPE_TEXT; |         type = TOX_CONFERENCE_TYPE_TEXT; | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint32_t conferencenum; |     uint32_t conferencenum = 0; | ||||||
|  |  | ||||||
|     if (type == TOX_CONFERENCE_TYPE_TEXT) { |     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||||
|         Tox_Err_Conference_New err; |         Tox_Err_Conference_New err; | ||||||
| @@ -373,7 +373,7 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|         conferencenum = tox_conference_new(m, &err); |         conferencenum = tox_conference_new(m, &err); | ||||||
|  |  | ||||||
|         if (err != TOX_ERR_CONFERENCE_NEW_OK) { |         if (err != TOX_ERR_CONFERENCE_NEW_OK) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|     } else if (type == TOX_CONFERENCE_TYPE_AV) { |     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
| @@ -381,21 +381,18 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|         conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL); |         conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL); | ||||||
|  |  | ||||||
|         if (conferencenum == (uint32_t) -1) { |         if (conferencenum == (uint32_t) -1) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #else | #else | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|         return; |         return; | ||||||
| #endif | #endif | ||||||
|     } else { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); |  | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { |     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||||
|         tox_conference_delete(m, conferencenum, NULL); |         tox_conference_delete(m, conferencenum, NULL); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -404,13 +401,13 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|  |  | ||||||
|     if (type == TOX_CONFERENCE_TYPE_AV) { |     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||||
|         if (!init_conference_audio_input(m, conferencenum)) { |         if (!init_conference_audio_input(m, conferencenum)) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -427,29 +424,15 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|             msg = "Logging for this window is OFF; type \"/log on\" to enable."; |             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *swch = argv[1]; |     const char *swch = argv[1]; | ||||||
|  |  | ||||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { |     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||||
|         char myid[TOX_ADDRESS_SIZE]; |         msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log."; | ||||||
|         tox_self_get_address(m, (uint8_t *) myid); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|  |  | ||||||
|         int log_ret = -1; |  | ||||||
|  |  | ||||||
|         if (self->type == WINDOW_TYPE_CHAT) { |  | ||||||
|             Friends.list[self->num].logging_on = true; |  | ||||||
|             log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); |  | ||||||
|         } else if (self->type == WINDOW_TYPE_PROMPT) { |  | ||||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); |  | ||||||
|         } else if (self->type == WINDOW_TYPE_CONFERENCE) { |  | ||||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_CONFERENCE); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |  | ||||||
|         return; |         return; | ||||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { |     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { | ||||||
|         if (self->type == WINDOW_TYPE_CHAT) { |         if (self->type == WINDOW_TYPE_CHAT) { | ||||||
| @@ -459,12 +442,12 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|         log_disable(log); |         log_disable(log); | ||||||
|  |  | ||||||
|         msg = "Logging disabled."; |         msg = "Logging disabled."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; |     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -478,11 +461,11 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     tox_self_get_address(m, (uint8_t *) bin_id); |     tox_self_get_address(m, (uint8_t *) bin_id); | ||||||
|  |  | ||||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { |     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef QRCODE | #ifdef QRCODE | ||||||
| @@ -495,7 +478,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     tox_self_get_address(m, (uint8_t *) bin_id); |     tox_self_get_address(m, (uint8_t *) bin_id); | ||||||
|  |  | ||||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { |     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -508,7 +491,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     char *dir = malloc(data_file_len + 1); |     char *dir = malloc(data_file_len + 1); | ||||||
|  |  | ||||||
|     if (dir == NULL) { |     if (dir == NULL) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -517,7 +500,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
| #ifdef QRPNG | #ifdef QRPNG | ||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||||
|         free(dir); |         free(dir); | ||||||
|         return; |         return; | ||||||
|     } else if (!strcmp(argv[1], "txt")) { |     } else if (!strcmp(argv[1], "txt")) { | ||||||
| @@ -527,7 +510,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|         char *qr_path = malloc(qr_path_buf_size); |         char *qr_path = malloc(qr_path_buf_size); | ||||||
|  |  | ||||||
|         if (qr_path == NULL) { |         if (qr_path == NULL) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); | ||||||
|             free(dir); |             free(dir); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -535,13 +518,13 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); |         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||||
|  |  | ||||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { |         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|             free(dir); |             free(dir); | ||||||
|             free(qr_path); |             free(qr_path); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||||
|  |  | ||||||
|         free(qr_path); |         free(qr_path); | ||||||
|  |  | ||||||
| @@ -551,7 +534,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|         char *qr_path = malloc(qr_path_buf_size); |         char *qr_path = malloc(qr_path_buf_size); | ||||||
|  |  | ||||||
|         if (qr_path == NULL) { |         if (qr_path == NULL) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); | ||||||
|             free(dir); |             free(dir); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -559,18 +542,18 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); |         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); | ||||||
|  |  | ||||||
|         if (ID_to_QRcode_png(id_string, qr_path) == -1) { |         if (ID_to_QRcode_png(id_string, qr_path) == -1) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|             free(dir); |             free(dir); | ||||||
|             free(qr_path); |             free(qr_path); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||||
|  |  | ||||||
|         free(qr_path); |         free(qr_path); | ||||||
|  |  | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||||
|         free(dir); |         free(dir); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -586,7 +569,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -595,7 +578,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     size_t len = strlen(nick); |     size_t len = strlen(nick); | ||||||
|  |  | ||||||
|     if (!valid_nick(nick)) { |     if (!valid_nick(nick)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -613,7 +596,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     UNUSED_VAR(window); |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -628,7 +611,7 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|         nospam = strtol(argv[1], NULL, 16); |         nospam = strtol(argv[1], NULL, 16); | ||||||
|  |  | ||||||
|         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { |         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -636,12 +619,12 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|     uint32_t old_nospam = tox_self_get_nospam(m); |     uint32_t old_nospam = tox_self_get_nospam(m); | ||||||
|     tox_self_set_nospam(m, (uint32_t) nospam); |     tox_self_set_nospam(m, (uint32_t) nospam); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||||
|     cmd_myid(window, self, m, 0, NULL); |     cmd_myid(window, self, m, 0, NULL); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                   "Any services that relied on your old ID will need to be updated manually."); |                   "Any services that relied on your old ID will need to be updated manually."); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||||
|                   old_nospam); |                   old_nospam); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -673,7 +656,7 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     UNUSED_VAR(argv); |     UNUSED_VAR(argv); | ||||||
|  |  | ||||||
|     if (FrndRequests.num_requests == 0) { |     if (FrndRequests.num_requests == 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -693,11 +676,11 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|             strcat(id, d); |             strcat(id, d); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); | ||||||
|  |  | ||||||
|         if (++count < FrndRequests.num_requests) { |         if (++count < FrndRequests.num_requests) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -712,7 +695,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Require a status. Statuses are: online, busy and away."; |         errmsg = "Require a status. Statuses are: online, busy and away."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|         goto finish; |         goto finish; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -727,13 +710,13 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|         status = TOX_USER_STATUS_BUSY; |         status = TOX_USER_STATUS_BUSY; | ||||||
|     } else { |     } else { | ||||||
|         errmsg = "Invalid status. Valid statuses are: online, busy and away."; |         errmsg = "Invalid status. Valid statuses are: online, busy and away."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|         goto finish; |         goto finish; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     tox_self_set_status(m, status); |     tox_self_set_status(m, status); | ||||||
|     prompt_update_status(prompt, status); |     prompt_update_status(prompt, status); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str); | ||||||
|  |  | ||||||
|  |  | ||||||
| finish: | finish: | ||||||
|   | |||||||
| @@ -306,7 +306,7 @@ static void help_draw_conference(ToxWindow *self) | |||||||
|     wprintw(win, "Conference commands:\n"); |     wprintw(win, "Conference commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /title <msg>               : Set conference title (show current title if no msg)\n"); |     wprintw(win, "  /title <msg>               : Show/set conference title\n"); | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     wattron(win, A_BOLD); |     wattron(win, A_BOLD); | ||||||
|     wprintw(win, "\n Audio:\n"); |     wprintw(win, "\n Audio:\n"); | ||||||
| @@ -314,6 +314,7 @@ static void help_draw_conference(ToxWindow *self) | |||||||
|     wprintw(win, "  /audio <on> or <off>       : Enable/disable audio in an audio conference\n"); |     wprintw(win, "  /audio <on> or <off>       : Enable/disable audio in an audio conference\n"); | ||||||
|     wprintw(win, "  /mute                      : Toggle self audio mute status\n"); |     wprintw(win, "  /mute                      : Toggle self audio mute status\n"); | ||||||
|     wprintw(win, "  /mute <nick> or <pubkey>   : Toggle peer audio mute status\n"); |     wprintw(win, "  /mute <nick> or <pubkey>   : Toggle peer audio mute status\n"); | ||||||
|  |     wprintw(win, "  /ptt <on> or <off>         : Toggle audio input Push-To-Talk (F2 to activate)\n"); | ||||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n\n"); |     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n\n"); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -403,7 +404,7 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|         case L'o': |         case L'o': | ||||||
|             height = 6; |             height = 6; | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|             height += 5; |             height += 7; | ||||||
| #endif | #endif | ||||||
|             help_init_window(self, height, 80); |             help_init_window(self, height, 80); | ||||||
|             self->help->type = HELP_CONFERENCE; |             self->help->type = HELP_CONFERENCE; | ||||||
|   | |||||||
							
								
								
									
										502
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										502
									
								
								src/line_info.c
									
									
									
									
									
								
							| @@ -46,7 +46,7 @@ void line_info_init(struct history *hst) | |||||||
|  |  | ||||||
|     hst->line_start = hst->line_root; |     hst->line_start = hst->line_root; | ||||||
|     hst->line_end = hst->line_start; |     hst->line_end = hst->line_start; | ||||||
|     hst->queue_sz = 0; |     hst->queue_size = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* resets line_start (moves to end of chat history) */ | /* resets line_start (moves to end of chat history) */ | ||||||
| @@ -54,27 +54,28 @@ void line_info_reset_start(ToxWindow *self, struct history *hst) | |||||||
| { | { | ||||||
|     struct line_info *line = hst->line_end; |     struct line_info *line = hst->line_end; | ||||||
|  |  | ||||||
|     if (line->prev == NULL) { |     if (line == NULL || line->prev == NULL) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2; | ||||||
|  |     int x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|     int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; |     int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||||
|     int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? 2 : 0; |     int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst; | ||||||
|     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); |  | ||||||
|  |  | ||||||
|     int curlines = 0; |     uint16_t curlines = 0; | ||||||
|     int nxtlines = line->newlines + (line->len / (x2 - side_offst)); |  | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|         curlines += 1 + nxtlines; |         curlines += line->format_lines; | ||||||
|         line = line->prev; |         line = line->prev; | ||||||
|         nxtlines = line->newlines + (line->len / (x2 - side_offst)); |     } while (line->prev && curlines + line->format_lines <= max_y); | ||||||
|     } while (line->prev && curlines + nxtlines < max_y); |  | ||||||
|  |  | ||||||
|     hst->line_start = line; |     hst->line_start = line; | ||||||
|  |  | ||||||
|  |     self->scroll_pause = false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void line_info_cleanup(struct history *hst) | void line_info_cleanup(struct history *hst) | ||||||
| @@ -87,9 +88,7 @@ void line_info_cleanup(struct history *hst) | |||||||
|         tmp1 = tmp2; |         tmp1 = tmp2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < hst->queue_sz; ++i) { |  | ||||||
|         if (hst->queue[i]) { |         if (hst->queue[i]) { | ||||||
|             free(hst->queue[i]); |             free(hst->queue[i]); | ||||||
|         } |         } | ||||||
| @@ -117,29 +116,230 @@ static void line_info_root_fwd(struct history *hst) | |||||||
| /* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */ | /* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */ | ||||||
| static struct line_info *line_info_ret_queue(struct history *hst) | static struct line_info *line_info_ret_queue(struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->queue_sz <= 0) { |     if (hst->queue_size == 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct line_info *line = hst->queue[0]; |     struct line_info *line = hst->queue[0]; | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < hst->queue_sz; ++i) { |  | ||||||
|         hst->queue[i] = hst->queue[i + 1]; |         hst->queue[i] = hst->queue[i + 1]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     --hst->queue_sz; |     --hst->queue_size; | ||||||
|  |  | ||||||
|     return line; |     return line; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Prints a maximum of `n` chars from `s` to `win`. | ||||||
|  |  * | ||||||
|  |  * Return 1 if the string contains a newline byte. | ||||||
|  |  * Return 0 if string does not contain a newline byte. | ||||||
|  |  * Return -1 if printing was aborted. | ||||||
|  |  */ | ||||||
|  | static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y) | ||||||
|  | { | ||||||
|  |     bool newline = false; | ||||||
|  |     char ch; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < n && (ch = s[i]); ++i) { | ||||||
|  |         if (ch == '\n') { | ||||||
|  |             newline = true; | ||||||
|  |  | ||||||
|  |             int x; | ||||||
|  |             int y; | ||||||
|  |             UNUSED_VAR(x); | ||||||
|  |             getyx(win, y, x); | ||||||
|  |  | ||||||
|  |             // make sure cursor will wrap correctly after newline to prevent display bugs | ||||||
|  |             if (y + 1 >= max_y) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (win) { | ||||||
|  |             wprintw(win, "%c", ch); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return newline; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns the index of the last space character in `s` found before `limit`. | ||||||
|  |  * Returns -1 if no space is found. | ||||||
|  |  */ | ||||||
|  | static int rspace_index(const char *s, int limit) | ||||||
|  | { | ||||||
|  |     for (int i = limit; i >= 0; --i) { | ||||||
|  |         if (s[i] == ' ') { | ||||||
|  |             return i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns the first index in `s` containing a newline byte found before `limit`. | ||||||
|  |  * Returns -1 if no newline  is found. | ||||||
|  |  */ | ||||||
|  | static int newline_index(const char *s, int limit) | ||||||
|  | { | ||||||
|  |     char ch; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < limit && (ch = s[i]); ++i) { | ||||||
|  |         if (ch == '\n') { | ||||||
|  |             return i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns the number of newline bytes in `s` */ | ||||||
|  | static unsigned int newline_count(const char *s) | ||||||
|  | { | ||||||
|  |     char ch; | ||||||
|  |     unsigned int count = 0; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; (ch = s[i]); ++i) { | ||||||
|  |         if (ch == '\n') { | ||||||
|  |             ++count; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return count; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Prints `line` message to window, wrapping at the last word that fits on the current line. | ||||||
|  |  * This function updates the `format_lines` field of `line` according to current window dimensions. | ||||||
|  |  * | ||||||
|  |  * If `win` is null nothing will be printed to the window. This is useful to set the | ||||||
|  |  * `format_lines` field on initialization. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 if not all characters in line's message were printed to screen. | ||||||
|  |  */ | ||||||
|  | static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y) | ||||||
|  | { | ||||||
|  |     int x; | ||||||
|  |     int y; | ||||||
|  |     UNUSED_VAR(y); | ||||||
|  |  | ||||||
|  |     const char *msg = line->msg; | ||||||
|  |     uint16_t length = line->msg_len; | ||||||
|  |     uint16_t lines = 0; | ||||||
|  |     const int x_start = line->len - line->msg_len - 1;  // manually keep track of x position because ncurses sucks | ||||||
|  |     int x_limit = max_x - x_start; | ||||||
|  |  | ||||||
|  |     if (x_limit <= 1) { | ||||||
|  |         fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (msg) { | ||||||
|  |         getyx(win, y, x); | ||||||
|  |  | ||||||
|  |         // next line would print past window limit so we abort; we don't want to update format_lines | ||||||
|  |         if (x > x_start) { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (length < x_limit) { | ||||||
|  |             int p_ret = print_n_chars(win, msg, length, max_y); | ||||||
|  |  | ||||||
|  |             if (p_ret == 1) { | ||||||
|  |                 lines += newline_count(msg); | ||||||
|  |             } else if (p_ret == -1) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             ++lines; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int newline_idx = newline_index(msg, x_limit - 1); | ||||||
|  |  | ||||||
|  |         if (newline_idx >= 0) { | ||||||
|  |             if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             msg += (newline_idx + 1); | ||||||
|  |             length -= (newline_idx + 1); | ||||||
|  |             x_limit = max_x; // if we find a newline we stop adding column padding for rest of message | ||||||
|  |             ++lines; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int space_idx = rspace_index(msg, x_limit - 1); | ||||||
|  |  | ||||||
|  |         if (space_idx >= 1) { | ||||||
|  |             if (print_n_chars(win, msg, space_idx, max_y) == -1) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             msg += space_idx + 1; | ||||||
|  |             length -= (space_idx + 1); | ||||||
|  |  | ||||||
|  |             if (win) { | ||||||
|  |                 waddch(win, '\n'); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if (print_n_chars(win, msg, x_limit, max_y) == -1) { | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             msg += x_limit; | ||||||
|  |             length -= x_limit; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Add padding to the start of the next line | ||||||
|  |         if (win && x_limit < max_x) { | ||||||
|  |             for (size_t i = 0; i < x_start; ++i) { | ||||||
|  |                 waddch(win, ' '); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ++lines; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (win && line->noread_flag) { | ||||||
|  |         getyx(win, y, x); | ||||||
|  |  | ||||||
|  |         if (x >= max_x - 1 || x == x_start) { | ||||||
|  |             ++lines; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         wattron(win, COLOR_PAIR(RED)); | ||||||
|  |         wprintw(win, " x"); | ||||||
|  |         wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line->format_lines = lines; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void line_info_init_line(ToxWindow *self, struct line_info *line) | ||||||
|  | { | ||||||
|  |     int y2; | ||||||
|  |     int x2; | ||||||
|  |     UNUSED_VAR(y2); | ||||||
|  |  | ||||||
|  |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; | ||||||
|  |     const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; | ||||||
|  |  | ||||||
|  |     print_wrap(NULL, line, max_x, max_y); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* creates new line_info line and puts it in the queue. | /* creates new line_info line and puts it in the queue. | ||||||
|  * |  * | ||||||
|  * Returns the id of the new line. |  * Returns the id of the new line. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type, | ||||||
|                   uint8_t bold, uint8_t colour, const char *msg, ...) |                   uint8_t bold, uint8_t colour, const char *msg, ...) | ||||||
| { | { | ||||||
|     if (!self) { |     if (!self) { | ||||||
| @@ -148,7 +348,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const | |||||||
|  |  | ||||||
|     struct history *hst = self->chatwin->hst; |     struct history *hst = self->chatwin->hst; | ||||||
|  |  | ||||||
|     if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) { |     if (hst->queue_size >= MAX_LINE_INFO_QUEUE) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -158,7 +358,8 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const | |||||||
|         exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); |         exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0}; |     char frmt_msg[MAX_LINE_INFO_MSG_SIZE]; | ||||||
|  |     frmt_msg[0] = 0; | ||||||
|  |  | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, msg); |     va_start(args, msg); | ||||||
| @@ -173,33 +374,33 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const | |||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_ACTION: |         case OUT_ACTION: | ||||||
|             len += strlen(user_settings->line_normal) + 2; |             len += strlen(user_settings->line_normal) + 2; // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case IN_MSG: |         case IN_MSG: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_MSG: |         case OUT_MSG: | ||||||
|             len += strlen(user_settings->line_normal) + 3; |             len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case CONNECTION: |         case CONNECTION: | ||||||
|             len += strlen(user_settings->line_join) + 2; |             len += strlen(user_settings->line_join) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case DISCONNECTION: |         case DISCONNECTION: | ||||||
|             len += strlen(user_settings->line_quit) + 2; |             len += strlen(user_settings->line_quit) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case SYS_MSG: |         case SYS_MSG: | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case NAME_CHANGE: |         case NAME_CHANGE: | ||||||
|             len += strlen(user_settings->line_alert) + 1; |             len += strlen(user_settings->line_alert) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case PROMPT: |         case PROMPT: | ||||||
|             ++len; |             len += 2;  // '$' char and a space | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
| @@ -207,21 +408,16 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     uint16_t msg_len = 0; | ||||||
|  |  | ||||||
|     if (frmt_msg[0]) { |     if (frmt_msg[0]) { | ||||||
|         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); |         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); | ||||||
|         len += strlen(new_line->msg); |         msg_len = strlen(new_line->msg); | ||||||
|  |         len += msg_len; | ||||||
|         int i; |  | ||||||
|  |  | ||||||
|         for (i = 0; frmt_msg[i]; ++i) { |  | ||||||
|             if (frmt_msg[i] == '\n') { |  | ||||||
|                 ++new_line->newlines; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (timestr) { |     if (show_timestamp) { | ||||||
|         snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); |         get_time_str(new_line->timestr, sizeof(new_line->timestr)); | ||||||
|         len += strlen(new_line->timestr) + 1; |         len += strlen(new_line->timestr) + 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -235,15 +431,18 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const | |||||||
|         len += strlen(new_line->name2); |         len += strlen(new_line->name2); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX; |     new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX; | ||||||
|     new_line->len = len; |     new_line->len = len; | ||||||
|  |     new_line->msg_len = msg_len; | ||||||
|     new_line->type = type; |     new_line->type = type; | ||||||
|     new_line->bold = bold; |     new_line->bold = bold; | ||||||
|     new_line->colour = colour; |     new_line->colour = colour; | ||||||
|     new_line->noread_flag = false; |     new_line->noread_flag = false; | ||||||
|     new_line->timestamp = get_unix_time(); |     new_line->timestamp = get_unix_time(); | ||||||
|  |  | ||||||
|     hst->queue[hst->queue_sz++] = new_line; |     line_info_init_line(self, new_line); | ||||||
|  |  | ||||||
|  |     hst->queue[hst->queue_size++] = new_line; | ||||||
|  |  | ||||||
|     return new_line->id; |     return new_line->id; | ||||||
| } | } | ||||||
| @@ -267,27 +466,8 @@ static void line_info_check_queue(ToxWindow *self) | |||||||
|     hst->line_end = line; |     hst->line_end = line; | ||||||
|     hst->line_end->id = line->id; |     hst->line_end->id = line->id; | ||||||
|  |  | ||||||
|     int y, y2, x, x2; |     if (!self->scroll_pause) { | ||||||
|     getmaxyx(self->window, y2, x2); |         line_info_reset_start(self, hst); | ||||||
|     getyx(self->chatwin->history, y, x); |  | ||||||
|  |  | ||||||
|     UNUSED_VAR(x); |  | ||||||
|  |  | ||||||
|     if (x2 <= SIDEBAR_WIDTH) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;   /* offset width of conference sidebar */ |  | ||||||
|     int lines = 1 + line->newlines + (line->len / (x2 - offst)); |  | ||||||
|     int max_y = y2 - CHATBOX_HEIGHT; |  | ||||||
|  |  | ||||||
|     /* move line_start forward proportionate to the number of new lines */ |  | ||||||
|     if (y + lines - 1 >= max_y) { |  | ||||||
|         while (lines > 0 && hst->line_start->next) { |  | ||||||
|             lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst)); |  | ||||||
|             hst->line_start = hst->line_start->next; |  | ||||||
|             ++hst->start_id; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -310,25 +490,44 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|     wclear(win); |     wclear(win); | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2; | ||||||
|  |  | ||||||
|  |     int x2; | ||||||
|  |  | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (x2 <= SIDEBAR_WIDTH) { |     if (x2 - 1 <= SIDEBAR_WIDTH) {  // leave room on x axis for sidebar padding | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (self->type == WINDOW_TYPE_CONFERENCE) { |     if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|         wmove(win, 0, 0); |         wmove(win, 0, 0); | ||||||
|     } else { |     } else { | ||||||
|         wmove(win, 2, 0); |         wmove(win, TOP_BAR_HEIGHT, 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct line_info *line = hst->line_start->next; |     struct line_info *line = hst->line_start->next; | ||||||
|  |  | ||||||
|     int numlines = 0; |     if (!line) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; | ||||||
|  |     const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; | ||||||
|  |     uint16_t numlines = line->format_lines; | ||||||
|  |     int print_ret = 0; | ||||||
|  |  | ||||||
|  |     while (line && numlines++ <= max_y && print_ret == 0) { | ||||||
|  |         int y; | ||||||
|  |         int x; | ||||||
|  |         UNUSED_VAR(y); | ||||||
|  |  | ||||||
|  |         getyx(win, y, x); | ||||||
|  |  | ||||||
|  |         if (x > 0) { // Prevents us from printing off the screen | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|     while (line && numlines++ <= y2) { |  | ||||||
|         uint8_t type = line->type; |         uint8_t type = line->type; | ||||||
|  |  | ||||||
|         switch (type) { |         switch (type) { | ||||||
| @@ -355,43 +554,32 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); |                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); | ||||||
|                 wattroff(win, COLOR_PAIR(nameclr)); |                 wattroff(win, COLOR_PAIR(nameclr)); | ||||||
|  |  | ||||||
|                 char *msg = line->msg; |                 if (line->msg[0] == 0) { | ||||||
|  |                     waddch(win, '\n'); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 while (msg) { |                 if (line->msg[0] == '>') { | ||||||
|                     char *line = strsep(&msg, "\n"); |  | ||||||
|  |  | ||||||
|                     if (line[0] == '>') { |  | ||||||
|                     wattron(win, COLOR_PAIR(GREEN)); |                     wattron(win, COLOR_PAIR(GREEN)); | ||||||
|                     } else if (line[0] == '<') { |                 } else if (line->msg[0] == '<') { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     wattron(win, COLOR_PAIR(RED)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |  | ||||||
|                     if (line[0] == '>') { |                 if (line->msg[0] == '>') { | ||||||
|                     wattroff(win, COLOR_PAIR(GREEN)); |                     wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|                     } else if (line[0] == '<') { |                 } else if (line->msg[0] == '<') { | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |                     wattroff(win, COLOR_PAIR(RED)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                     // change the \0 set by strsep back to \n |                 if (type == OUT_MSG && !line->read_flag) { | ||||||
|                     if (msg) { |                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                         msg[-1] = '\n'; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |  | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |  | ||||||
|                     wprintw(win, " x"); |  | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |  | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n"); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case OUT_ACTION_READ: |             case OUT_ACTION_READ: | ||||||
| @@ -406,21 +594,17 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(YELLOW)); |                 wattron(win, COLOR_PAIR(YELLOW)); | ||||||
|                 wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg); |                 wprintw(win, "%s %s ", user_settings->line_normal, line->name1); | ||||||
|  |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|                 wattroff(win, COLOR_PAIR(YELLOW)); |                 wattroff(win, COLOR_PAIR(YELLOW)); | ||||||
|  |  | ||||||
|                 if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |                 if (type == OUT_ACTION && !line->read_flag) { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                     wprintw(win, " x"); |  | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |  | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n"); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case SYS_MSG: |             case SYS_MSG: | ||||||
| @@ -438,7 +622,8 @@ void line_info_print(ToxWindow *self) | |||||||
|                     wattron(win, COLOR_PAIR(line->colour)); |                     wattron(win, COLOR_PAIR(line->colour)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "%s\n", line->msg); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |                 waddch(win, '\n'); | ||||||
|  |  | ||||||
|                 if (line->bold) { |                 if (line->bold) { | ||||||
|                     wattroff(win, A_BOLD); |                     wattroff(win, A_BOLD); | ||||||
| @@ -456,10 +641,10 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wattroff(win, COLOR_PAIR(GREEN)); |                 wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|  |  | ||||||
|                 if (line->msg[0]) { |                 if (line->msg[0]) { | ||||||
|                     wprintw(win, "%s", line->msg); |                     print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n"); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case CONNECTION: |             case CONNECTION: | ||||||
| @@ -474,7 +659,9 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s ", line->name1); |                 wprintw(win, "%s ", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s\n", line->msg); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |                 waddch(win, '\n'); | ||||||
|  |  | ||||||
|                 wattroff(win, COLOR_PAIR(line->colour)); |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
| @@ -491,7 +678,9 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s ", line->name1); |                 wprintw(win, "%s ", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s\n", line->msg); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |                 waddch(win, '\n'); | ||||||
|  |  | ||||||
|                 wattroff(win, COLOR_PAIR(line->colour)); |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
| @@ -507,7 +696,7 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s", line->name1); |                 wprintw(win, "%s", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s", line->msg); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |  | ||||||
|                 wattron(win, A_BOLD); |                 wattron(win, A_BOLD); | ||||||
|                 wprintw(win, "%s\n", line->name2); |                 wprintw(win, "%s\n", line->name2); | ||||||
| @@ -521,11 +710,43 @@ void line_info_print(ToxWindow *self) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* keep calling until queue is empty */ |     /* keep calling until queue is empty */ | ||||||
|     if (hst->queue_sz > 0) { |     if (hst->queue_size > 0) { | ||||||
|         line_info_print(self); |         line_info_print(self); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Return true if all lines starting from `line` can fit on the screen. | ||||||
|  |  */ | ||||||
|  | static bool line_info_screen_fit(ToxWindow *self, struct line_info *line) | ||||||
|  | { | ||||||
|  |     if (!line) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int x2; | ||||||
|  |     int y2; | ||||||
|  |     getmaxyx(self->chatwin->history, y2, x2); | ||||||
|  |  | ||||||
|  |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|  |     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||||
|  |     const int max_y = y2 - top_offset; | ||||||
|  |  | ||||||
|  |     uint16_t lines = line->format_lines; | ||||||
|  |  | ||||||
|  |     while (line) { | ||||||
|  |         if (lines > max_y) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lines += line->format_lines; | ||||||
|  |         line = line->next; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* puts msg in specified line_info msg buffer */ | /* puts msg in specified line_info msg buffer */ | ||||||
| void line_info_set(ToxWindow *self, uint32_t id, char *msg) | void line_info_set(ToxWindow *self, uint32_t id, char *msg) | ||||||
| { | { | ||||||
| @@ -533,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) | |||||||
|  |  | ||||||
|     while (line) { |     while (line) { | ||||||
|         if (line->id == id) { |         if (line->id == id) { | ||||||
|  |             size_t new_len = strlen(msg); | ||||||
|  |             line->len = line->len - line->msg_len + new_len; | ||||||
|  |             line->msg_len = new_len; | ||||||
|             snprintf(line->msg, sizeof(line->msg), "%s", msg); |             snprintf(line->msg, sizeof(line->msg), "%s", msg); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -541,56 +765,74 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* static void line_info_goto_root(struct history *hst) | static void line_info_scroll_up(ToxWindow *self, struct history *hst) | ||||||
| { |  | ||||||
|     hst->line_start = hst->line_root; |  | ||||||
| } */ |  | ||||||
|  |  | ||||||
| static void line_info_scroll_up(struct history *hst) |  | ||||||
| { | { | ||||||
|     if (hst->line_start->prev) { |     if (hst->line_start->prev) { | ||||||
|         hst->line_start = hst->line_start->prev; |         hst->line_start = hst->line_start->prev; | ||||||
|     } else { |         self->scroll_pause = true; | ||||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_scroll_down(struct history *hst) | static void line_info_scroll_down(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->line_start->next) { |     struct line_info *next = hst->line_start->next; | ||||||
|         hst->line_start = hst->line_start->next; |  | ||||||
|  |     if (next && self->scroll_pause) { | ||||||
|  |         if (line_info_screen_fit(self, next->next)) { | ||||||
|  |             line_info_reset_start(self, hst); | ||||||
|         } else { |         } else { | ||||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); |             hst->line_start = next; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         line_info_reset_start(self, hst); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_page_up(ToxWindow *self, struct history *hst) | static void line_info_page_up(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     UNUSED_VAR(x2); |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|     int jump_dist = y2 / 2; |     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||||
|     int i; |     const int max_y = y2 - top_offset; | ||||||
|  |     size_t jump_dist = max_y / 2; | ||||||
|  |  | ||||||
|     for (i = 0; i < jump_dist && hst->line_start->prev; ++i) { |     for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) { | ||||||
|         hst->line_start = hst->line_start->prev; |         hst->line_start = hst->line_start->prev; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     self->scroll_pause = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_page_down(ToxWindow *self, struct history *hst) | static void line_info_page_down(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     int x2, y2; |     if (!self->scroll_pause) { | ||||||
|     getmaxyx(self->window, y2, x2); |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int x2; | ||||||
|  |     int y2; | ||||||
|  |     getmaxyx(self->chatwin->history, y2, x2); | ||||||
|  |  | ||||||
|     UNUSED_VAR(x2); |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|     int jump_dist = y2 / 2; |     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||||
|     int i; |     const int max_y = y2 - top_offset; | ||||||
|  |     size_t jump_dist = max_y / 2; | ||||||
|  |  | ||||||
|     for (i = 0; i < jump_dist && hst->line_start->next; ++i) { |     struct line_info *next = hst->line_start->next; | ||||||
|         hst->line_start = hst->line_start->next; |  | ||||||
|  |     for (size_t i = 0; i < jump_dist && next; ++i) { | ||||||
|  |         if (line_info_screen_fit(self, next->next)) { | ||||||
|  |             line_info_reset_start(self, hst); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         hst->line_start = next; | ||||||
|  |         next = hst->line_start->next; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -604,9 +846,9 @@ bool line_info_onKey(ToxWindow *self, wint_t key) | |||||||
|     } else if (key == user_settings->key_half_page_down) { |     } else if (key == user_settings->key_half_page_down) { | ||||||
|         line_info_page_down(self, hst); |         line_info_page_down(self, hst); | ||||||
|     } else if (key == user_settings->key_scroll_line_up) { |     } else if (key == user_settings->key_scroll_line_up) { | ||||||
|         line_info_scroll_up(hst); |         line_info_scroll_up(self, hst); | ||||||
|     } else if (key == user_settings->key_scroll_line_down) { |     } else if (key == user_settings->key_scroll_line_down) { | ||||||
|         line_info_scroll_down(hst); |         line_info_scroll_down(self, hst); | ||||||
|     } else if (key == user_settings->key_page_bottom) { |     } else if (key == user_settings->key_page_bottom) { | ||||||
|         line_info_reset_start(self, hst); |         line_info_reset_start(self, hst); | ||||||
|     } else { |     } else { | ||||||
|   | |||||||
| @@ -29,9 +29,9 @@ | |||||||
| #define MAX_HISTORY 100000 | #define MAX_HISTORY 100000 | ||||||
| #define MIN_HISTORY 40 | #define MIN_HISTORY 40 | ||||||
| #define MAX_LINE_INFO_QUEUE 1024 | #define MAX_LINE_INFO_QUEUE 1024 | ||||||
| #define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32    /* needs extra room for log loading */ | #define MAX_LINE_INFO_MSG_SIZE (MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32) /* needs extra room for log loading */ | ||||||
|  |  | ||||||
| typedef enum { | typedef enum LINE_TYPE { | ||||||
|     SYS_MSG, |     SYS_MSG, | ||||||
|     IN_MSG, |     IN_MSG, | ||||||
|     OUT_MSG, |     OUT_MSG, | ||||||
| @@ -54,10 +54,12 @@ struct line_info { | |||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint8_t bold; |     uint8_t bold; | ||||||
|     uint8_t colour; |     uint8_t colour; | ||||||
|     uint8_t noread_flag;   /* true if a line should be flagged as unread */ |     bool    noread_flag;   /* true if a line should be flagged as unread */ | ||||||
|  |     bool    read_flag;     /* true if a message has been flagged as read */ | ||||||
|     uint32_t id; |     uint32_t id; | ||||||
|     uint16_t len;   /* combined len of entire line */ |     uint16_t len;        /* combined length of entire line */ | ||||||
|     uint8_t newlines; |     uint16_t msg_len;    /* length of the message */ | ||||||
|  |     uint16_t format_lines;  /* number of lines the combined string takes up (dynamically set) */ | ||||||
|  |  | ||||||
|     struct line_info *prev; |     struct line_info *prev; | ||||||
|     struct line_info *next; |     struct line_info *next; | ||||||
| @@ -71,7 +73,7 @@ struct history { | |||||||
|     uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */ |     uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */ | ||||||
|  |  | ||||||
|     struct line_info *queue[MAX_LINE_INFO_QUEUE]; |     struct line_info *queue[MAX_LINE_INFO_QUEUE]; | ||||||
|     int queue_sz; |     size_t queue_size; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* creates new line_info line and puts it in the queue. | /* creates new line_info line and puts it in the queue. | ||||||
| @@ -79,7 +81,7 @@ struct history { | |||||||
|  * Returns the id of the new line. |  * Returns the id of the new line. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type, | ||||||
|                   uint8_t bold, uint8_t colour, const char *msg, ...); |                   uint8_t bold, uint8_t colour, const char *msg, ...); | ||||||
|  |  | ||||||
| /* Prints a section of history starting at line_start */ | /* Prints a section of history starting at line_start */ | ||||||
|   | |||||||
							
								
								
									
										248
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -35,29 +35,33 @@ | |||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| /* There are three types of logs: chat logs, conference logs, and prompt logs (see LOG_TYPE in log.h) | /* Creates a log path and puts it in `dest. | ||||||
|    A prompt log is in the format: LOGDIR/selfkey-home.log |  * | ||||||
|    A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log |  * There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h) | ||||||
|    A conference log is in the format: LOGDIR/selfkey-conferencename-date[time].log |  * A prompt log is in the format: LOGDIR/selfkey-home.log | ||||||
|  |  * A chat log is in the format: LOGDIR/selfkey-name-otherkey.log | ||||||
|    Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used. |  * | ||||||
|  |  * For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID. | ||||||
|    Returns 0 on success, -1 if the path is too long */ |  * For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID. | ||||||
| static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype) |  * | ||||||
|  |  * Return path length on success. | ||||||
|  |  * Return -1 if the path is too long. | ||||||
|  |  */ | ||||||
|  | static int get_log_path(char *dest, int destsize, const char *name, const char *selfkey, const char *otherkey) | ||||||
| { | { | ||||||
|     if (!valid_nick(name)) { |     if (!valid_nick(name)) { | ||||||
|         name = UNKNOWN_NAME; |         name = UNKNOWN_NAME; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *namedash = logtype == LOG_PROMPT ? "" : "-"; |     const char *namedash = otherkey ? "-" : ""; | ||||||
|     const char *set_path = user_settings->chatlogs_path; |     const char *set_path = user_settings->chatlogs_path; | ||||||
|  |  | ||||||
|     char *user_config_dir = get_user_config_dir(); |     char *user_config_dir = get_user_config_dir(); | ||||||
|     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); |     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); | ||||||
|     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); |     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); | ||||||
|  |  | ||||||
|     /* first 6 digits of selfkey */ |     /* first 6 bytes of selfkey */ | ||||||
|     char self_id[32]; |     char self_id[32] = {0}; | ||||||
|     path_len += KEY_IDENT_DIGITS * 2; |     path_len += KEY_IDENT_DIGITS * 2; | ||||||
|     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); |     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); | ||||||
|     sprintf(&self_id[2], "%02X", selfkey[1] & 0xff); |     sprintf(&self_id[2], "%02X", selfkey[1] & 0xff); | ||||||
| @@ -66,19 +70,13 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke | |||||||
|  |  | ||||||
|     char other_id[32] = {0}; |     char other_id[32] = {0}; | ||||||
|  |  | ||||||
|     switch (logtype) { |     if (otherkey) { | ||||||
|         case LOG_CHAT: |         /* first 6 bytes of otherkey */ | ||||||
|         path_len += KEY_IDENT_DIGITS * 2; |         path_len += KEY_IDENT_DIGITS * 2; | ||||||
|         sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); |         sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); | ||||||
|         sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); |         sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); | ||||||
|         sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); |         sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); | ||||||
|         other_id[KEY_IDENT_DIGITS * 2] = '\0'; |         other_id[KEY_IDENT_DIGITS * 2] = '\0'; | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case LOG_CONFERENCE: |  | ||||||
|             strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time()); |  | ||||||
|             path_len += strlen(other_id); |  | ||||||
|             break; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (path_len >= destsize) { |     if (path_len >= destsize) { | ||||||
| @@ -94,28 +92,35 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke | |||||||
|  |  | ||||||
|     free(user_config_dir); |     free(user_config_dir); | ||||||
|  |  | ||||||
|     return 0; |     return path_len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Opens log file or creates a new one */ | /* Initializes log path for `log`. | ||||||
| static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | static int init_logging_session(const char *name, const char *selfkey, const char *otherkey, struct chatlog *log, | ||||||
|  |                                 LOG_TYPE type) | ||||||
| { | { | ||||||
|     if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) { |     if (log == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char log_path[MAX_STR_SIZE]; |     char log_path[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) { |     int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey); | ||||||
|  |  | ||||||
|  |     if (path_len == -1 || path_len >= sizeof(log->path)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     log->file = fopen(log_path, "a+"); |     memcpy(log->path, log_path, path_len); | ||||||
|     snprintf(log->path, sizeof(log->path), "%s", log_path); |     log->path[path_len] = 0; | ||||||
|  |  | ||||||
|     if (log->file == NULL) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -124,6 +129,10 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth | |||||||
|  |  | ||||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) | ||||||
| { | { | ||||||
|  |     if (log == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!log->log_on) { |     if (!log->log_on) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -154,94 +163,162 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e | |||||||
|  |  | ||||||
| void log_disable(struct chatlog *log) | void log_disable(struct chatlog *log) | ||||||
| { | { | ||||||
|  |     if (log == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (log->file != NULL) { |     if (log->file != NULL) { | ||||||
|         fclose(log->file); |         fclose(log->file); | ||||||
|  |         log->file = NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *log = (struct chatlog) { |     log->lastwrite = 0; | ||||||
|         0 |     log->log_on = false; | ||||||
|     }; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | int log_enable(struct chatlog *log) | ||||||
| { | { | ||||||
|     log->log_on = true; |     if (log == NULL) { | ||||||
|  |  | ||||||
|     if (log->file != NULL) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { |  | ||||||
|         log_disable(log); |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (log->log_on) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (*log->path == 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (log->file != NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     log->file = fopen(log->path, "a+"); | ||||||
|  |  | ||||||
|  |     if (log->file == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     log->log_on = true; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Loads previous history from chat log */ | /* Initializes a log. This function must be called before any other logging operations. | ||||||
| void load_chat_history(ToxWindow *self, struct chatlog *log) |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type) | ||||||
| { | { | ||||||
|     if (log->file == NULL) { |     if (log == NULL) { | ||||||
|         return; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (log->file != NULL || log->log_on) { | ||||||
|  |         fprintf(stderr, "Warning: Called log_init() on an already initialized log\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (init_logging_session(name, selfkey, otherkey, log, type) == -1) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     log_disable(log); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Loads chat log history and prints it to `self` window. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success or if log file doesn't exist. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int load_chat_history(ToxWindow *self, struct chatlog *log) | ||||||
|  | { | ||||||
|  |     if (log == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (*log->path == 0) { | ||||||
|  |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     off_t sz = file_size(log->path); |     off_t sz = file_size(log->path); | ||||||
|  |  | ||||||
|     if (sz <= 0) { |     if (sz <= 0) { | ||||||
|         return; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *hstbuf = malloc(sz + 1); |     FILE *fp = fopen(log->path, "r"); | ||||||
|  |  | ||||||
|     if (hstbuf == NULL) { |     if (fp == NULL) { | ||||||
|         exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fseek(log->file, 0L, SEEK_SET) == -1) { |     char *buf = malloc(sz + 1); | ||||||
|         free(hstbuf); |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); |     if (buf == NULL) { | ||||||
|         return; |         fclose(fp); | ||||||
|  |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fread(hstbuf, sz, 1, log->file) != 1) { |     if (fseek(fp, 0L, SEEK_SET) == -1) { | ||||||
|         free(hstbuf); |         free(buf); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); |         fclose(fp); | ||||||
|         return; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hstbuf[sz] = '\0'; |     if (fread(buf, sz, 1, fp) != 1) { | ||||||
|  |         free(buf); | ||||||
|  |         fclose(fp); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|  |     buf[sz] = 0; | ||||||
|  |  | ||||||
|     /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */ |     /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */ | ||||||
|     int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size); |     int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size); | ||||||
|     int start, count = 0; |  | ||||||
|  |     int start = 0; | ||||||
|  |     int count = 0; | ||||||
|  |  | ||||||
|     /* start at end and backtrace L lines or to the beginning of buffer */ |     /* start at end and backtrace L lines or to the beginning of buffer */ | ||||||
|     for (start = sz - 1; start >= 0 && count < L; --start) { |     for (start = sz - 1; start >= 0 && count < L; --start) { | ||||||
|         if (hstbuf[start] == '\n') { |         if (buf[start] == '\n') { | ||||||
|             ++count; |             ++count; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *line = strtok(&hstbuf[start + 1], "\n"); |     char *tmp = NULL; | ||||||
|  |     const char *line = strtok_r(&buf[start + 1], "\n", &tmp); | ||||||
|  |  | ||||||
|     if (line == NULL) { |     if (line == NULL) { | ||||||
|         free(hstbuf); |         free(buf); | ||||||
|         return; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (line != NULL && count--) { |     while (line != NULL && count--) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line); | ||||||
|         line = strtok(NULL, "\n"); |         line = strtok_r(NULL, "\n", &tmp); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---"); | ||||||
|     free(hstbuf); |  | ||||||
|  |     free(buf); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* renames chatlog file replacing src with dest. | /* Renames chatlog file `src` to `dest`. | ||||||
|    Returns 0 on success or if no log exists, -1 on failure. */ |  * | ||||||
| int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum) |  * Return 0 on success or if no log exists. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum) | ||||||
| { | { | ||||||
|     ToxWindow *toxwin = get_window_ptr(winnum); |     ToxWindow *toxwin = get_window_ptr(winnum); | ||||||
|     struct chatlog *log = NULL; |     struct chatlog *log = NULL; | ||||||
| @@ -250,6 +327,11 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | |||||||
|     /* disable log if necessary and save its state */ |     /* disable log if necessary and save its state */ | ||||||
|     if (toxwin != NULL) { |     if (toxwin != NULL) { | ||||||
|         log = toxwin->chatwin->log; |         log = toxwin->chatwin->log; | ||||||
|  |  | ||||||
|  |         if (log == NULL) { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         log_on = log->log_on; |         log_on = log->log_on; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -260,24 +342,36 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | |||||||
|     char newpath[MAX_STR_SIZE]; |     char newpath[MAX_STR_SIZE]; | ||||||
|     char oldpath[MAX_STR_SIZE]; |     char oldpath[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) { |     if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey) == -1) { | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!file_exists(oldpath)) { |     if (!file_exists(oldpath)) { | ||||||
|  |         init_logging_session(dest, selfkey, otherkey, log, LOG_TYPE_CHAT);  // still need to rename path | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) { |     int new_path_len = get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey); | ||||||
|  |  | ||||||
|  |     if (new_path_len == -1 || new_path_len >= MAX_STR_SIZE) { | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (rename(oldpath, newpath) != 0) { |     if (file_exists(newpath)) { | ||||||
|  |         if (remove(oldpath) != 0) { | ||||||
|  |             fprintf(stderr, "Warning: remove() failed to remove log path `%s`\n", oldpath); | ||||||
|  |         } | ||||||
|  |     } else if (rename(oldpath, newpath) != 0) { | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (log != NULL) { | ||||||
|  |         memcpy(log->path, newpath, new_path_len); | ||||||
|  |         log->path[new_path_len] = 0; | ||||||
|  |  | ||||||
|         if (log_on) { |         if (log_on) { | ||||||
|         log_enable(dest, selfkey, otherkey, log, LOG_CHAT); |             log_enable(log); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -285,7 +379,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | |||||||
| on_error: | on_error: | ||||||
|  |  | ||||||
|     if (log_on) { |     if (log_on) { | ||||||
|         log_enable(src, selfkey, otherkey, log, LOG_CHAT); |         log_enable(log); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/log.h
									
									
									
									
									
								
							| @@ -30,30 +30,43 @@ struct chatlog { | |||||||
|     bool log_on;    /* specific to current chat window */ |     bool log_on;    /* specific to current chat window */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef enum { | typedef enum LOG_TYPE { | ||||||
|     LOG_CONFERENCE, |     LOG_TYPE_PROMPT, | ||||||
|     LOG_PROMPT, |     LOG_TYPE_CHAT, | ||||||
|     LOG_CHAT, |  | ||||||
| } LOG_TYPE; | } LOG_TYPE; | ||||||
|  |  | ||||||
|  | /* Initializes a log. This function must be called before any other logging operations. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type); | ||||||
|  |  | ||||||
| /* formats/writes line to log file */ | /* formats/writes line to log file */ | ||||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | ||||||
|  |  | ||||||
| /* enables logging for specified log and creates/fetches file if necessary. | /* enables logging for specified log. | ||||||
|  * |  * | ||||||
|  * Returns 0 on success. |  * Returns 0 on success. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); | int log_enable(struct chatlog *log); | ||||||
|  |  | ||||||
| /* disables logging for specified log and closes file */ | /* disables logging for specified log and closes file */ | ||||||
| void log_disable(struct chatlog *log); | void log_disable(struct chatlog *log); | ||||||
|  |  | ||||||
| /* Loads previous history from chat log */ | /* Loads chat log history and prints it to `self` window. | ||||||
| void load_chat_history(ToxWindow *self, struct chatlog *log); |  * | ||||||
|  |  * Return 0 on success or if log file doesn't exist. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int load_chat_history(ToxWindow *self, struct chatlog *log); | ||||||
|  |  | ||||||
| /* renames chatlog file replacing src with dest. | /* Renames chatlog file `src` to `dest`. | ||||||
|    Returns 0 on success or if no log exists, -1 on failure. */ |  * | ||||||
| int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum); |  * Return 0 on success or if no log exists. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum); | ||||||
|  |  | ||||||
| #endif /* LOG_H */ | #endif /* LOG_H */ | ||||||
|   | |||||||
| @@ -86,9 +86,9 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg) | |||||||
|  |  | ||||||
|         line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ; |         line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ; | ||||||
|  |  | ||||||
|         if (line->noread_flag == true) { |         if (line->noread_flag) { | ||||||
|             line->len -= 2; |  | ||||||
|             line->noread_flag = false; |             line->noread_flag = false; | ||||||
|  |             line->read_flag = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
|   | |||||||
| @@ -71,17 +71,20 @@ int timed_out(time_t timestamp, time_t timeout) | |||||||
|     return timestamp + timeout <= get_unix_time(); |     return timestamp + timeout <= get_unix_time(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Sleeps the caller's thread for `usec` microseconds */ | /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||||
| void sleep_thread(long int usec) | void sleep_thread(long int usec) | ||||||
| { | { | ||||||
|     struct timespec req; |     struct timespec req; | ||||||
|  |     struct timespec rem; | ||||||
|  |  | ||||||
|     req.tv_sec = 0; |     req.tv_sec = 0; | ||||||
|     req.tv_nsec = usec * 1000L; |     req.tv_nsec = usec * 1000L; | ||||||
|  |  | ||||||
|     if (nanosleep(&req, NULL) == -1) { |     if (nanosleep(&req, &rem) == -1) { | ||||||
|  |         if (nanosleep(&rem, NULL) == -1) { | ||||||
|             fprintf(stderr, "nanosleep() returned -1\n"); |             fprintf(stderr, "nanosleep() returned -1\n"); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get the current local time */ | /* Get the current local time */ | ||||||
| @@ -93,11 +96,16 @@ struct tm *get_time(void) | |||||||
|     return timeinfo; |     return timeinfo; | ||||||
| } | } | ||||||
|  |  | ||||||
| /*Puts the current time in buf in the format of [HH:mm:ss] */ | /* Puts the current time in buf in the format of specified by the config */ | ||||||
| void get_time_str(char *buf, int bufsize) | void get_time_str(char *buf, size_t bufsize) | ||||||
| { | { | ||||||
|  |     if (buf == NULL || bufsize == 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     *buf = 0; | ||||||
|  |  | ||||||
|     if (user_settings->timestamps == TIMESTAMPS_OFF) { |     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||||
|         buf[0] = '\0'; |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -584,7 +592,7 @@ void set_window_title(ToxWindow *self, const char *title, int len) | |||||||
|  |  | ||||||
|     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; |     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|  |  | ||||||
|     if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title */ |     if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */ | ||||||
|         snprintf(cpy, sizeof(cpy), "%u %s", self->num, title); |         snprintf(cpy, sizeof(cpy), "%u %s", self->num, title); | ||||||
|     } else { |     } else { | ||||||
|         snprintf(cpy, sizeof(cpy), "%s", title); |         snprintf(cpy, sizeof(cpy), "%s", title); | ||||||
|   | |||||||
| @@ -81,8 +81,8 @@ int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char | |||||||
| /* get the current unix time (not thread safe) */ | /* get the current unix time (not thread safe) */ | ||||||
| time_t get_unix_time(void); | time_t get_unix_time(void); | ||||||
|  |  | ||||||
| /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | /* Puts the current time in buf in the format of specified by the config */ | ||||||
| void get_time_str(char *buf, int bufsize); | void get_time_str(char *buf, size_t bufsize); | ||||||
|  |  | ||||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
| void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | ||||||
| @@ -108,7 +108,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | |||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(time_t timestamp, time_t timeout); | int timed_out(time_t timestamp, time_t timeout); | ||||||
|  |  | ||||||
| /* Sleeps the caller's thread for `usec` microseconds */ | /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||||
| void sleep_thread(long int usec); | void sleep_thread(long int usec); | ||||||
|  |  | ||||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ | /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | |||||||
|     va_end(args); |     va_end(args); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg); | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| @@ -365,12 +365,12 @@ on_exit: | |||||||
| void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) | void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) | ||||||
| { | { | ||||||
|     if (t_data.disabled) { |     if (t_data.disabled) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (t_data.busy) { |     if (t_data.busy) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -382,20 +382,20 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, | |||||||
|     t_data.busy = true; |     t_data.busy = true; | ||||||
|  |  | ||||||
|     if (pthread_attr_init(&lookup_thread.attr) != 0) { |     if (pthread_attr_init(&lookup_thread.attr) != 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); | ||||||
|         clear_thread_data(); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { |     if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set"); | ||||||
|         pthread_attr_destroy(&lookup_thread.attr); |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|         clear_thread_data(); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { |     if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init"); | ||||||
|         pthread_attr_destroy(&lookup_thread.attr); |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|         clear_thread_data(); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ | |||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "xtra.h" | #include "x11focus.h" | ||||||
|  |  | ||||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
| @@ -126,6 +126,8 @@ static void tab_notify(ToxWindow *self, uint64_t flags) | |||||||
|     } else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) { |     } else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) { | ||||||
|         self->alert = WINDOW_ALERT_2; |         self->alert = WINDOW_ALERT_2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ++self->pending_messages; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool notifications_are_disabled(uint64_t flags) | static bool notifications_are_disabled(uint64_t flags) | ||||||
|   | |||||||
							
								
								
									
										171
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -144,7 +144,7 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms | |||||||
|     tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err); |     tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_SET_INFO_OK) { |     if (err != TOX_ERR_SET_INFO_OK) { | ||||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err); |         line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -281,18 +281,18 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|             add_line_to_hist(ctx); |             add_line_to_hist(ctx); | ||||||
|             wstrsubst(ctx->line, L'¶', L'\n'); |             wstrsubst(ctx->line, L'¶', L'\n'); | ||||||
|  |  | ||||||
|             char line[MAX_STR_SIZE] = {0}; |             char line[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { |             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { | ||||||
|                 memset(line, 0, sizeof(line)); |                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); | ||||||
|             } |             } else { | ||||||
|  |                 line_info_add(self, false, NULL, NULL, PROMPT, 0, 0, "%s", line); | ||||||
|             line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); |  | ||||||
|                 execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); |                 execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |         wclear(ctx->linewin); | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |         wmove(self->window, y2, 0); | ||||||
|         reset_buf(ctx); |         reset_buf(ctx); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -301,7 +301,8 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|  |  | ||||||
| static void prompt_onDraw(ToxWindow *self, Tox *m) | static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |     if (y2 <= 0 || x2 <= 0) { | ||||||
| @@ -316,64 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     wclear(ctx->linewin); |     wclear(ctx->linewin); | ||||||
|  |  | ||||||
|     curs_set(1); |  | ||||||
|  |  | ||||||
|     if (ctx->len > 0) { |     if (ctx->len > 0) { | ||||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); |         mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len); | ||||||
|  |  | ||||||
|  |     curs_set(1); | ||||||
|  |  | ||||||
|     StatusBar *statusbar = self->stb; |     StatusBar *statusbar = self->stb; | ||||||
|  |  | ||||||
|     mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); |  | ||||||
|     wmove(statusbar->topline, 0, 0); |     wmove(statusbar->topline, 0, 0); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|     Tox_Connection connection = statusbar->connection; |     Tox_Connection connection = statusbar->connection; | ||||||
|  |     Tox_User_Status status = statusbar->status; | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (connection != TOX_CONNECTION_NONE) { |     if (connection != TOX_CONNECTION_NONE) { | ||||||
|         int colour = MAGENTA; |         int colour = MAGENTA; | ||||||
|         const char *status_text = "ERROR"; |         const char *status_text = "ERROR"; | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |  | ||||||
|         Tox_User_Status status = statusbar->status; |  | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|         switch (status) { |         switch (status) { | ||||||
|             case TOX_USER_STATUS_NONE: |             case TOX_USER_STATUS_NONE: | ||||||
|                 status_text = "Online"; |                 status_text = "Online"; | ||||||
|                 colour = GREEN; |                 colour = STATUS_ONLINE; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_AWAY: |             case TOX_USER_STATUS_AWAY: | ||||||
|                 status_text = "Away"; |                 status_text = "Away"; | ||||||
|                 colour = YELLOW; |                 colour = STATUS_AWAY; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_BUSY: |             case TOX_USER_STATUS_BUSY: | ||||||
|                 status_text = "Busy"; |                 status_text = "Busy"; | ||||||
|                 colour = RED; |                 colour = STATUS_BUSY; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wprintw(statusbar->topline, " [%s]", status_text); |         wprintw(statusbar->topline, " ["); | ||||||
|         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); | ||||||
|  |         wprintw(statusbar->topline, "%s", status_text); | ||||||
|  |         wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, "]"); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|         wattron(statusbar->topline, A_BOLD); |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); |         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|         wattroff(statusbar->topline, A_BOLD); |  | ||||||
|     } else { |     } else { | ||||||
|         wprintw(statusbar->topline, " [Offline]"); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wattron(statusbar->topline, A_BOLD); |         wprintw(statusbar->topline, " ["); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(statusbar->topline, "Offline"); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, "]"); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); |         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|         wattroff(statusbar->topline, A_BOLD); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     int s_y; | ||||||
|  |     int s_x; | ||||||
|  |     getyx(statusbar->topline, s_y, s_x); | ||||||
|  |  | ||||||
|  |     mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x); | ||||||
|  |     wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |  | ||||||
|     /* Reset statusbar->statusmsg on window resize */ |     /* Reset statusbar->statusmsg on window resize */ | ||||||
|     if (x2 != self->x) { |     if (x2 != self->x) { | ||||||
|         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; |         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||||
| @@ -401,20 +426,27 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (statusbar->statusmsg[0]) { |     if (statusbar->statusmsg[0]) { | ||||||
|         wprintw(statusbar->topline, " : %s", statusbar->statusmsg); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(statusbar->topline, " | "); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |  | ||||||
|  |         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(statusbar->topline, "%s", statusbar->statusmsg); | ||||||
|  |         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); |     int y; | ||||||
|  |     int x; | ||||||
|     int y, x; |  | ||||||
|     getyx(self->window, y, x); |     getyx(self->window, y, x); | ||||||
|  |  | ||||||
|     UNUSED_VAR(x); |     UNUSED_VAR(x); | ||||||
|  |  | ||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||||
|     wmove(self->window, y + 1, new_x); |     wmove(self->window, y, new_x); | ||||||
|  |  | ||||||
|  |     draw_window_bar(self); | ||||||
|  |  | ||||||
|     wnoutrefresh(self->window); |     wnoutrefresh(self->window); | ||||||
|  |  | ||||||
| @@ -434,8 +466,6 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | |||||||
|         snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); |         snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|     const char *msg; |     const char *msg; | ||||||
|  |  | ||||||
|     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { |     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { | ||||||
| @@ -444,7 +474,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | |||||||
|  |  | ||||||
|     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { |     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { | ||||||
|         msg = "has come online"; |         msg = "has come online"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); |         line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
|  |  | ||||||
|         if (self->active_box != -1) { |         if (self->active_box != -1) { | ||||||
| @@ -456,7 +486,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | |||||||
|         } |         } | ||||||
|     } else if (connection_status == TOX_CONNECTION_NONE) { |     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||||
|         msg = "has gone offline"; |         msg = "has gone offline"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); |         line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
|  |  | ||||||
|         if (self->active_box != -1) { |         if (self->active_box != -1) { | ||||||
| @@ -476,21 +506,18 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con | |||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     line_info_add(self, true, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data); |  | ||||||
|     write_to_log("Friend request with the message '%s'", "", ctx->log, true); |     write_to_log("Friend request with the message '%s'", "", ctx->log, true); | ||||||
|  |  | ||||||
|     int n = add_friend_request(key, data); |     int n = add_friend_request(key, data); | ||||||
|  |  | ||||||
|     if (n == -1) { |     if (n == -1) { | ||||||
|         const char *errmsg = "Friend request queue is full. Discarding request."; |         const char *errmsg = "Friend request queue is full. Discarding request."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n); | ||||||
|     sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL); |     sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -535,29 +562,50 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run) | |||||||
|     prompt_update_nick(prompt, nick); |     prompt_update_nick(prompt, nick); | ||||||
|  |  | ||||||
|     /* Init statusbar subwindow */ |     /* Init statusbar subwindow */ | ||||||
|     statusbar->topline = subwin(self->window, 2, x2, 0, 0); |     statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void print_welcome_msg(ToxWindow *self) | static void print_welcome_msg(ToxWindow *self) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|  |  | ||||||
|     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client."; |     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||||
|     msg = "Type \"/help\" for assistance. Further help may be found via the man page."; |     msg = "Type \"/help\" for assistance. Further help may be found via the man page."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     char myid[TOX_ADDRESS_SIZE]; | ||||||
|  |     tox_self_get_address(m, (uint8_t *) myid); | ||||||
|  |  | ||||||
|  |     if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (user_settings->autolog == AUTOLOG_ON) { | ||||||
|  |         if (log_enable(ctx->log) == -1) { | ||||||
|  |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void prompt_onInit(ToxWindow *self, Tox *m) | static void prompt_onInit(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     curs_set(1); |     curs_set(1); | ||||||
|     int y2, x2; |  | ||||||
|  |     int y2; | ||||||
|  |     int x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |     if (y2 <= 0 || x2 <= 0) { | ||||||
| @@ -565,8 +613,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |  | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||||
|  |     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||||
|  |     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); | ||||||
|  |  | ||||||
|     ctx->log = calloc(1, sizeof(struct chatlog)); |     ctx->log = calloc(1, sizeof(struct chatlog)); | ||||||
|     ctx->hst = calloc(1, sizeof(struct history)); |     ctx->hst = calloc(1, sizeof(struct history)); | ||||||
| @@ -577,14 +627,7 @@ static void prompt_onInit(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     line_info_init(ctx->hst); |     line_info_init(ctx->hst); | ||||||
|  |  | ||||||
|     if (user_settings->autolog == AUTOLOG_ON) { |     prompt_init_log(self, m, self->name); | ||||||
|         char myid[TOX_ADDRESS_SIZE]; |  | ||||||
|         tox_self_get_address(m, (uint8_t *) myid); |  | ||||||
|  |  | ||||||
|         if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) { |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |     scrollok(ctx->history, 0); | ||||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 0); |     wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||||
| @@ -611,7 +654,7 @@ ToxWindow *new_prompt(void) | |||||||
|     ret->onConnectionChange = &prompt_onConnectionChange; |     ret->onConnectionChange = &prompt_onConnectionChange; | ||||||
|     ret->onFriendRequest = &prompt_onFriendRequest; |     ret->onFriendRequest = &prompt_onFriendRequest; | ||||||
|  |  | ||||||
|     strcpy(ret->name, "home"); |     strcpy(ret->name, "Home"); | ||||||
|  |  | ||||||
|     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); |     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); | ||||||
|     StatusBar *stb = calloc(1, sizeof(StatusBar)); |     StatusBar *stb = calloc(1, sizeof(StatusBar)); | ||||||
|   | |||||||
| @@ -114,9 +114,7 @@ static PyObject *python_api_get_status_message(PyObject *self, PyObject *args) | |||||||
|  |  | ||||||
| static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args) | static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args) | ||||||
| { | { | ||||||
|     size_t       i, ii; |  | ||||||
|     FriendsList  friends; |     FriendsList  friends; | ||||||
|     PyObject    *cur, *ret; |  | ||||||
|     char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1]; |     char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||||
|  |  | ||||||
|     if (!PyArg_ParseTuple(args, "")) { |     if (!PyArg_ParseTuple(args, "")) { | ||||||
| @@ -124,15 +122,15 @@ static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     friends = api_get_friendslist(); |     friends = api_get_friendslist(); | ||||||
|     ret     = PyList_New(0); |     PyObject *ret = PyList_New(0); | ||||||
|  |  | ||||||
|     for (i = 0; i < friends.num_friends; i++) { |     for (size_t i = 0; i < friends.num_friends; i++) { | ||||||
|         for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) { |         for (size_t ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) { | ||||||
|             snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff); |             snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0'; |         pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0'; | ||||||
|         cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf); |         PyObject *cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf); | ||||||
|         PyList_Append(ret, cur); |         PyList_Append(ret, cur); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -264,14 +262,14 @@ PyMODINIT_FUNC PyInit_toxic_api(void) | |||||||
|  |  | ||||||
| void terminate_python(void) | void terminate_python(void) | ||||||
| { | { | ||||||
|     struct python_registered_func *cur, *old; |  | ||||||
|  |  | ||||||
|     if (python_commands.name != NULL) { |     if (python_commands.name != NULL) { | ||||||
|         free(python_commands.name); |         free(python_commands.name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     struct python_registered_func *cur = NULL; | ||||||
|  |  | ||||||
|     for (cur = python_commands.next; cur != NULL;) { |     for (cur = python_commands.next; cur != NULL;) { | ||||||
|         old = cur; |         struct python_registered_func *old = cur; | ||||||
|         cur = cur->next; |         cur = cur->next; | ||||||
|         free(old->name); |         free(old->name); | ||||||
|         free(old); |         free(old); | ||||||
|   | |||||||
| @@ -73,6 +73,11 @@ static struct ui_strings { | |||||||
|  |  | ||||||
|     const char *mplex_away; |     const char *mplex_away; | ||||||
|     const char *mplex_away_note; |     const char *mplex_away_note; | ||||||
|  |  | ||||||
|  |     const char *color_bar_bg; | ||||||
|  |     const char *color_bar_fg; | ||||||
|  |     const char *color_bar_accent; | ||||||
|  |     const char *color_bar_notify; | ||||||
| } ui_strings = { | } ui_strings = { | ||||||
|     "ui", |     "ui", | ||||||
|     "timestamps", |     "timestamps", | ||||||
| @@ -100,6 +105,10 @@ static struct ui_strings { | |||||||
|     "line_normal", |     "line_normal", | ||||||
|     "mplex_away", |     "mplex_away", | ||||||
|     "mplex_away_note", |     "mplex_away_note", | ||||||
|  |     "color_bar_bg", | ||||||
|  |     "color_bar_fg", | ||||||
|  |     "color_bar_accent", | ||||||
|  |     "color_bar_notify", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void ui_defaults(struct user_settings *settings) | static void ui_defaults(struct user_settings *settings) | ||||||
| @@ -116,7 +125,7 @@ static void ui_defaults(struct user_settings *settings) | |||||||
|     settings->bell_on_invite = 0; |     settings->bell_on_invite = 0; | ||||||
|     settings->colour_theme = DFLT_COLS; |     settings->colour_theme = DFLT_COLS; | ||||||
|     settings->history_size = 700; |     settings->history_size = 700; | ||||||
|     settings->notification_timeout = 3000; |     settings->notification_timeout = 6000; | ||||||
|     settings->show_typing_self = SHOW_TYPING_ON; |     settings->show_typing_self = SHOW_TYPING_ON; | ||||||
|     settings->show_typing_other = SHOW_TYPING_ON; |     settings->show_typing_other = SHOW_TYPING_ON; | ||||||
|     settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; |     settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; | ||||||
| @@ -130,10 +139,7 @@ static void ui_defaults(struct user_settings *settings) | |||||||
|     snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); |     snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); | ||||||
|  |  | ||||||
|     settings->mplex_away = MPLEX_ON; |     settings->mplex_away = MPLEX_ON; | ||||||
|     snprintf(settings->mplex_away_note, |     snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE); | ||||||
|              sizeof(settings->mplex_away_note), |  | ||||||
|              "%s", |  | ||||||
|              MPLEX_AWAY_NOTE); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const struct keys_strings { | static const struct keys_strings { | ||||||
| @@ -205,11 +211,17 @@ static const struct audio_strings { | |||||||
|     const char *input_device; |     const char *input_device; | ||||||
|     const char *output_device; |     const char *output_device; | ||||||
|     const char *VAD_threshold; |     const char *VAD_threshold; | ||||||
|  |     const char *conference_audio_channels; | ||||||
|  |     const char *chat_audio_channels; | ||||||
|  |     const char *push_to_talk; | ||||||
| } audio_strings = { | } audio_strings = { | ||||||
|     "audio", |     "audio", | ||||||
|     "input_device", |     "input_device", | ||||||
|     "output_device", |     "output_device", | ||||||
|     "VAD_threshold", |     "VAD_threshold", | ||||||
|  |     "conference_audio_channels", | ||||||
|  |     "chat_audio_channels", | ||||||
|  |     "push_to_talk", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void audio_defaults(struct user_settings *settings) | static void audio_defaults(struct user_settings *settings) | ||||||
| @@ -217,6 +229,9 @@ static void audio_defaults(struct user_settings *settings) | |||||||
|     settings->audio_in_dev = 0; |     settings->audio_in_dev = 0; | ||||||
|     settings->audio_out_dev = 0; |     settings->audio_out_dev = 0; | ||||||
|     settings->VAD_threshold = 5.0; |     settings->VAD_threshold = 5.0; | ||||||
|  |     settings->conference_audio_channels = 1; | ||||||
|  |     settings->chat_audio_channels = 2; | ||||||
|  |     settings->push_to_talk = 0; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -330,7 +345,7 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|  |  | ||||||
|         if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) { |         if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) { | ||||||
|             if (time == 12) { |             if (time == 12) { | ||||||
|                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); |                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M %p"); | ||||||
|                 snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]"); |                 snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -339,6 +354,22 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|             snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str); |             snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_string(setting, ui_strings.color_bar_bg, &str)) { | ||||||
|  |             snprintf(s->color_bar_bg, sizeof(s->color_bar_bg), "%s", str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_string(setting, ui_strings.color_bar_fg, &str)) { | ||||||
|  |             snprintf(s->color_bar_fg, sizeof(s->color_bar_fg), "%s", str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_string(setting, ui_strings.color_bar_accent, &str)) { | ||||||
|  |             snprintf(s->color_bar_accent, sizeof(s->color_bar_accent), "%s", str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_string(setting, ui_strings.color_bar_notify, &str)) { | ||||||
|  |             snprintf(s->color_bar_notify, sizeof(s->color_bar_notify), "%s", str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) { |         if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) { | ||||||
|             snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str); |             snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str); | ||||||
|         } |         } | ||||||
| @@ -506,6 +537,15 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|         s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev; |         s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev; | ||||||
|  |  | ||||||
|         config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold); |         config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold); | ||||||
|  |  | ||||||
|  |         config_setting_lookup_int(setting, audio_strings.conference_audio_channels, &s->conference_audio_channels); | ||||||
|  |         s->conference_audio_channels = s->conference_audio_channels <= 0 | ||||||
|  |                                        || s->conference_audio_channels > 2 ? 1 : s->conference_audio_channels; | ||||||
|  |  | ||||||
|  |         config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels); | ||||||
|  |         s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels; | ||||||
|  |  | ||||||
|  |         config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -68,6 +68,11 @@ struct user_settings { | |||||||
|     char autorun_path[PATH_MAX]; |     char autorun_path[PATH_MAX]; | ||||||
|     char password_eval[PASSWORD_EVAL_MAX]; |     char password_eval[PASSWORD_EVAL_MAX]; | ||||||
|  |  | ||||||
|  |     char color_bar_bg[COLOR_STR_SIZE]; | ||||||
|  |     char color_bar_fg[COLOR_STR_SIZE]; | ||||||
|  |     char color_bar_accent[COLOR_STR_SIZE]; | ||||||
|  |     char color_bar_notify[COLOR_STR_SIZE]; | ||||||
|  |  | ||||||
|     int key_next_tab; |     int key_next_tab; | ||||||
|     int key_prev_tab; |     int key_prev_tab; | ||||||
|     int key_scroll_line_up; |     int key_scroll_line_up; | ||||||
| @@ -85,6 +90,9 @@ struct user_settings { | |||||||
|     int audio_in_dev; |     int audio_in_dev; | ||||||
|     int audio_out_dev; |     int audio_out_dev; | ||||||
|     double VAD_threshold; |     double VAD_threshold; | ||||||
|  |     int conference_audio_channels; | ||||||
|  |     int chat_audio_channels; | ||||||
|  |     int push_to_talk;      /* boolean */ | ||||||
| #endif | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -119,10 +127,10 @@ enum settings_values { | |||||||
| #define LINE_JOIN    "-->" | #define LINE_JOIN    "-->" | ||||||
| #define LINE_QUIT    "<--" | #define LINE_QUIT    "<--" | ||||||
| #define LINE_ALERT   "-!-" | #define LINE_ALERT   "-!-" | ||||||
| #define LINE_NORMAL  "---" | #define LINE_NORMAL  "-" | ||||||
| #define TIMESTAMP_DEFAULT      "%H:%M:%S" | #define TIMESTAMP_DEFAULT      "%H:%M" | ||||||
| #define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]" | #define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]" | ||||||
| #define MPLEX_AWAY_NOTE "Detached from screen" | #define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!" | ||||||
|  |  | ||||||
| int settings_load(struct user_settings *s, const char *patharg); | int settings_load(struct user_settings *s, const char *patharg); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										156
									
								
								src/toxic.c
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								src/toxic.c
									
									
									
									
									
								
							| @@ -66,7 +66,7 @@ | |||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
| #include "xtra.h" | #include "x11focus.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| @@ -205,10 +205,7 @@ void exit_toxic_success(Tox *m) | |||||||
|     curl_global_cleanup(); |     curl_global_cleanup(); | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
|     /* We have to terminate xtra last coz reasons |     terminate_x11focus(); | ||||||
|      * Please don't call this anywhere else coz trust me |  | ||||||
|      */ |  | ||||||
|     terminate_xtra(); |  | ||||||
| #endif /* X11 */ | #endif /* X11 */ | ||||||
|  |  | ||||||
|     exit(EXIT_SUCCESS); |     exit(EXIT_SUCCESS); | ||||||
| @@ -259,10 +256,14 @@ static void init_term(void) | |||||||
|     keypad(stdscr, 1); |     keypad(stdscr, 1); | ||||||
|     noecho(); |     noecho(); | ||||||
|     nonl(); |     nonl(); | ||||||
|     timeout(100); |     timeout(50); | ||||||
|  |  | ||||||
|     if (has_colors()) { |     if (has_colors()) { | ||||||
|         short bg_color = COLOR_BLACK; |         short bg_color = COLOR_BLACK; | ||||||
|  |         short bar_bg_color = COLOR_BLUE; | ||||||
|  |         short bar_fg_color = COLOR_WHITE; | ||||||
|  |         short bar_accent_color = COLOR_CYAN; | ||||||
|  |         short bar_notify_color = COLOR_YELLOW; | ||||||
|         start_color(); |         start_color(); | ||||||
|  |  | ||||||
|         if (user_settings->colour_theme == NATIVE_COLS) { |         if (user_settings->colour_theme == NATIVE_COLS) { | ||||||
| @@ -271,15 +272,120 @@ static void init_term(void) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         init_pair(0, COLOR_WHITE, COLOR_BLACK); |         if (!string_is_empty(user_settings->color_bar_bg)) { | ||||||
|         init_pair(1, COLOR_GREEN, bg_color); |             if (strcmp(user_settings->color_bar_bg, "black") == 0) { | ||||||
|         init_pair(2, COLOR_CYAN, bg_color); |                 bar_bg_color = COLOR_BLACK; | ||||||
|         init_pair(3, COLOR_RED, bg_color); |             } else if (strcmp(user_settings->color_bar_bg, "red") == 0) { | ||||||
|         init_pair(4, COLOR_BLUE, bg_color); |                 bar_bg_color = COLOR_RED; | ||||||
|         init_pair(5, COLOR_YELLOW, bg_color); |             } else if (strcmp(user_settings->color_bar_bg, "blue") == 0) { | ||||||
|         init_pair(6, COLOR_MAGENTA, bg_color); |                 bar_bg_color = COLOR_BLUE; | ||||||
|         init_pair(7, COLOR_BLACK, COLOR_BLACK); |             } else if (strcmp(user_settings->color_bar_bg, "cyan") == 0) { | ||||||
|         init_pair(8, COLOR_BLACK, COLOR_WHITE); |                 bar_bg_color = COLOR_CYAN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_bg, "green") == 0) { | ||||||
|  |                 bar_bg_color = COLOR_GREEN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_bg, "yellow") == 0) { | ||||||
|  |                 bar_bg_color = COLOR_YELLOW; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_bg, "magenta") == 0) { | ||||||
|  |                 bar_bg_color = COLOR_MAGENTA; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_bg, "white") == 0) { | ||||||
|  |                 bar_bg_color = COLOR_WHITE; | ||||||
|  |             } else { | ||||||
|  |                 bar_bg_color = COLOR_BLUE; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bar_bg_color = COLOR_BLUE; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!string_is_empty(user_settings->color_bar_fg)) { | ||||||
|  |             if (strcmp(user_settings->color_bar_fg, "black") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_BLACK; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "red") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_RED; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "blue") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_BLUE; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "cyan") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_CYAN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "green") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_GREEN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "yellow") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_YELLOW; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "magenta") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_MAGENTA; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_fg, "white") == 0) { | ||||||
|  |                 bar_fg_color = COLOR_WHITE; | ||||||
|  |             } else { | ||||||
|  |                 bar_fg_color = COLOR_WHITE; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bar_fg_color = COLOR_WHITE; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!string_is_empty(user_settings->color_bar_accent)) { | ||||||
|  |             if (strcmp(user_settings->color_bar_accent, "black") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_BLACK; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "red") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_RED; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "blue") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_BLUE; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "cyan") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_CYAN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "green") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_GREEN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "yellow") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_YELLOW; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "magenta") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_MAGENTA; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_accent, "white") == 0) { | ||||||
|  |                 bar_accent_color = COLOR_WHITE; | ||||||
|  |             } else { | ||||||
|  |                 bar_accent_color = COLOR_CYAN; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bar_accent_color = COLOR_CYAN; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!string_is_empty(user_settings->color_bar_notify)) { | ||||||
|  |             if (strcmp(user_settings->color_bar_notify, "black") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_BLACK; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "red") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_RED; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "blue") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_BLUE; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "cyan") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_CYAN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "green") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_GREEN; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "yellow") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_YELLOW; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "magenta") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_MAGENTA; | ||||||
|  |             } else if (strcmp(user_settings->color_bar_notify, "white") == 0) { | ||||||
|  |                 bar_notify_color = COLOR_WHITE; | ||||||
|  |             } else { | ||||||
|  |                 bar_notify_color = COLOR_YELLOW; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bar_notify_color = COLOR_YELLOW; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         init_pair(WHITE, COLOR_WHITE, COLOR_BLACK); | ||||||
|  |         init_pair(GREEN, COLOR_GREEN, bg_color); | ||||||
|  |         init_pair(CYAN, COLOR_CYAN, bg_color); | ||||||
|  |         init_pair(RED, COLOR_RED, bg_color); | ||||||
|  |         init_pair(BLUE, COLOR_BLUE, bg_color); | ||||||
|  |         init_pair(YELLOW, COLOR_YELLOW, bg_color); | ||||||
|  |         init_pair(MAGENTA, COLOR_MAGENTA, bg_color); | ||||||
|  |         init_pair(BLACK, COLOR_BLACK, COLOR_BLACK); | ||||||
|  |         init_pair(BLUE_BLACK, COLOR_BLUE, COLOR_BLACK); | ||||||
|  |         init_pair(BLACK_WHITE, COLOR_BLACK, COLOR_WHITE); | ||||||
|  |         init_pair(BLACK_BG, COLOR_BLACK, bar_bg_color); | ||||||
|  |         init_pair(PURPLE_BG, COLOR_MAGENTA, bar_bg_color); | ||||||
|  |         init_pair(BAR_TEXT, bar_fg_color, bar_bg_color); | ||||||
|  |         init_pair(BAR_ACCENT, bar_accent_color, bar_bg_color); | ||||||
|  |         init_pair(BAR_NOTIFY, bar_notify_color, bar_bg_color); | ||||||
|  |         init_pair(STATUS_ONLINE, COLOR_GREEN, bar_bg_color); | ||||||
|  |         init_pair(STATUS_AWAY, COLOR_YELLOW, bar_bg_color); | ||||||
|  |         init_pair(STATUS_BUSY, COLOR_RED, bar_bg_color); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     refresh(); |     refresh(); | ||||||
| @@ -1350,21 +1456,6 @@ static void init_default_data_files(void) | |||||||
|     free(user_config_dir); |     free(user_config_dir); | ||||||
| } | } | ||||||
|  |  | ||||||
| // this doesn't do anything (yet) |  | ||||||
| #ifdef X11 |  | ||||||
| void DnD_callback(const char *asdv, DropType dt) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(asdv); |  | ||||||
|     UNUSED_VAR(dt); |  | ||||||
|     // if (dt != DT_plain) |  | ||||||
|     //     return; |  | ||||||
|  |  | ||||||
|     // pthread_mutex_lock(&Winthread.lock); |  | ||||||
|     // line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); |  | ||||||
|     // pthread_mutex_unlock(&Winthread.lock); |  | ||||||
| } |  | ||||||
| #endif /* X11 */ |  | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
|     /* Make sure all written files are read/writeable only by the current user. */ |     /* Make sure all written files are read/writeable only by the current user. */ | ||||||
| @@ -1422,7 +1513,7 @@ int main(int argc, char **argv) | |||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
|  |  | ||||||
|     if (init_xtra(DnD_callback) == -1) { |     if (init_x11focus() == -1) { | ||||||
|         queue_init_message("X failed to initialize"); |         queue_init_message("X failed to initialize"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1439,6 +1530,7 @@ int main(int argc, char **argv) | |||||||
|     prompt = init_windows(m); |     prompt = init_windows(m); | ||||||
|     prompt_init_statusbar(prompt, m, !datafile_exists); |     prompt_init_statusbar(prompt, m, !datafile_exists); | ||||||
|     load_conferences(m); |     load_conferences(m); | ||||||
|  |     set_active_window_index(0); | ||||||
|  |  | ||||||
|     if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { |     if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { | ||||||
|         exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); |         exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); | ||||||
| @@ -1521,7 +1613,7 @@ int main(int argc, char **argv) | |||||||
|             pthread_mutex_lock(&Winthread.lock); |             pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|             if (store_data(m, DATA_FILE) != 0) { |             if (store_data(m, DATA_FILE) != 0) { | ||||||
|                 line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); |                 line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |             pthread_mutex_unlock(&Winthread.lock); | ||||||
|   | |||||||
| @@ -48,6 +48,7 @@ | |||||||
| #define TOXIC_MAX_NAME_LENGTH 32   /* Must be <= TOX_MAX_NAME_LENGTH */ | #define TOXIC_MAX_NAME_LENGTH 32   /* Must be <= TOX_MAX_NAME_LENGTH */ | ||||||
| #define KEY_IDENT_DIGITS 3    /* number of hex digits to display for the pub-key based identifier */ | #define KEY_IDENT_DIGITS 3    /* number of hex digits to display for the pub-key based identifier */ | ||||||
| #define TIME_STR_SIZE 32 | #define TIME_STR_SIZE 32 | ||||||
|  | #define COLOR_STR_SIZE 10 /* should fit every color option */ | ||||||
|  |  | ||||||
| #ifndef MAX_PORT_RANGE | #ifndef MAX_PORT_RANGE | ||||||
| #define MAX_PORT_RANGE 65535 | #define MAX_PORT_RANGE 65535 | ||||||
| @@ -75,7 +76,7 @@ | |||||||
| #define T_KEY_C_DOWN     0x20D    /* ctrl-down arrow */ | #define T_KEY_C_DOWN     0x20D    /* ctrl-down arrow */ | ||||||
| #define T_KEY_TAB        0x09     /* TAB key */ | #define T_KEY_TAB        0x09     /* TAB key */ | ||||||
|  |  | ||||||
| #define ONLINE_CHAR "*" | #define ONLINE_CHAR  "o" | ||||||
| #define OFFLINE_CHAR "o" | #define OFFLINE_CHAR "o" | ||||||
|  |  | ||||||
| typedef enum _FATAL_ERRS { | typedef enum _FATAL_ERRS { | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rat | |||||||
|  |  | ||||||
| static void print_err(ToxWindow *self, const char *error_str) | static void print_err(ToxWindow *self, const char *error_str) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| ToxAV *init_video(ToxWindow *self, Tox *tox) | ToxAV *init_video(ToxWindow *self, Tox *tox) | ||||||
| @@ -66,13 +66,13 @@ ToxAV *init_video(ToxWindow *self, Tox *tox) | |||||||
|     CallControl.video_frame_duration = 10; |     CallControl.video_frame_duration = 10; | ||||||
|  |  | ||||||
|     if (!CallControl.av) { |     if (!CallControl.av) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance"); | ||||||
|  |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_video_devices(CallControl.av) == vde_InternalError) { |     if (init_video_devices(CallControl.av) == vde_InternalError) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices"); | ||||||
|  |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
| @@ -110,17 +110,17 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y, | |||||||
|  |  | ||||||
|     /* Drop frame if video sending is disabled */ |     /* Drop frame if video sending is disabled */ | ||||||
|     if (this_call->video_bit_rate == 0 || this_call->status != cs_Active || this_call->vin_idx == -1) { |     if (this_call->video_bit_rate == 0 || this_call->status != cs_Active || this_call->vin_idx == -1) { | ||||||
|         line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped."); |         line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error) == false) { |     if (toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error) == false) { | ||||||
|         line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame"); |         line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame"); | ||||||
|  |  | ||||||
|         if (error == TOXAV_ERR_SEND_FRAME_NULL) { |         if (error == TOXAV_ERR_SEND_FRAME_NULL) { | ||||||
|             line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame"); |             line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame"); | ||||||
|         } else if (error == TOXAV_ERR_SEND_FRAME_INVALID) { |         } else if (error == TOXAV_ERR_SEND_FRAME_INVALID) { | ||||||
|             line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame"); |             line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -138,22 +138,22 @@ void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_ | |||||||
| int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call) | int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call) | ||||||
| { | { | ||||||
|     if (!self || !av) { |     if (!self || !av) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) { |     if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None) { |     if (register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) { |     if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -347,11 +347,11 @@ void cmd_res(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         if (call->status == cs_Active && call->vin_idx != -1) { |         if (call->status == cs_Active && call->vin_idx != -1) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                           "Resolution of current call: %u x %u", |                           "Resolution of current call: %u x %u", | ||||||
|                           call->video_width, call->video_height); |                           call->video_width, call->video_height); | ||||||
|         } else { |         } else { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|                           "Initial resolution for video calls: %u x %u", |                           "Initial resolution for video calls: %u x %u", | ||||||
|                           CallControl.default_video_width, CallControl.default_video_height); |                           CallControl.default_video_width, CallControl.default_video_height); | ||||||
|         } |         } | ||||||
| @@ -411,7 +411,7 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -447,7 +447,7 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -852,7 +852,7 @@ void print_video_devices(ToxWindow *self, VideoDeviceType type) | |||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < size[type]; ++i) { |     for (i = 0; i < size[type]; ++i) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
|   | |||||||
							
								
								
									
										178
									
								
								src/windows.c
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								src/windows.c
									
									
									
									
									
								
							| @@ -336,7 +336,7 @@ int add_window(Tox *m, ToxWindow *w) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         w->index = i; |         w->index = i; | ||||||
|         w->window = newwin(LINES - 2, COLS, 0, 0); |         w->window = newwin(LINES, COLS, 0, 0); | ||||||
|  |  | ||||||
|         if (w->window == NULL) { |         if (w->window == NULL) { | ||||||
|             return -1; |             return -1; | ||||||
| @@ -397,8 +397,9 @@ void del_window(ToxWindow *w) | |||||||
|     set_active_window_index(0); |     set_active_window_index(0); | ||||||
|  |  | ||||||
|     uint8_t idx = w->index; |     uint8_t idx = w->index; | ||||||
|  |     delwin(w->window_bar); | ||||||
|     delwin(w->window); |     delwin(w->window); | ||||||
|     free(windows[idx]); |     free(w); | ||||||
|     windows[idx] = NULL; |     windows[idx] = NULL; | ||||||
|  |  | ||||||
|     clear(); |     clear(); | ||||||
| @@ -408,7 +409,12 @@ void del_window(ToxWindow *w) | |||||||
|  |  | ||||||
| ToxWindow *init_windows(Tox *m) | ToxWindow *init_windows(Tox *m) | ||||||
| { | { | ||||||
|  |     if (COLS <= CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT) { | ||||||
|  |         exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     prompt = new_prompt(); |     prompt = new_prompt(); | ||||||
|  |  | ||||||
|     int n_prompt = add_window(m, prompt); |     int n_prompt = add_window(m, prompt); | ||||||
|  |  | ||||||
|     if (n_prompt < 0) { |     if (n_prompt < 0) { | ||||||
| @@ -430,25 +436,18 @@ void on_window_resize(void) | |||||||
|     refresh(); |     refresh(); | ||||||
|     clear(); |     clear(); | ||||||
|  |  | ||||||
|     /* equivalent to LINES and COLS */ |  | ||||||
|     int x2, y2; |  | ||||||
|     getmaxyx(stdscr, y2, x2); |  | ||||||
|     y2 -= 2; |  | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] == NULL) { |         ToxWindow *w = windows[i]; | ||||||
|  |  | ||||||
|  |         if (w == NULL) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ToxWindow *w = windows[i]; |         if (w->type == WINDOW_TYPE_FRIEND_LIST)  { | ||||||
|  |             delwin(w->window_bar); | ||||||
|         if (windows[i]->type == WINDOW_TYPE_FRIEND_LIST)  { |  | ||||||
|             delwin(w->window); |             delwin(w->window); | ||||||
|             w->window = newwin(y2, x2, 0, 0); |             w->window = newwin(LINES, COLS, 0, 0); | ||||||
|  |             w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -465,22 +464,35 @@ void on_window_resize(void) | |||||||
|  |  | ||||||
|         delwin(w->chatwin->linewin); |         delwin(w->chatwin->linewin); | ||||||
|         delwin(w->chatwin->history); |         delwin(w->chatwin->history); | ||||||
|  |         delwin(w->window_bar); | ||||||
|         delwin(w->window); |         delwin(w->window); | ||||||
|  |  | ||||||
|         w->window = newwin(y2, x2, 0, 0); |         w->window = newwin(LINES, COLS, 0, 0); | ||||||
|         w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |  | ||||||
|  |         int x2; | ||||||
|  |         int y2; | ||||||
|  |         getmaxyx(w->window, y2, x2); | ||||||
|  |  | ||||||
|  |         if (y2 <= 0 || x2 <= 0) { | ||||||
|  |             fprintf(stderr, "Failed to resize window: max_x: %d, max_y: %d\n", x2, y2); | ||||||
|  |             delwin(w->window); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (w->show_peerlist) { |         if (w->show_peerlist) { | ||||||
|             w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); |             w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); | ||||||
|             w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); |             w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); | ||||||
|         } else { |         } else { | ||||||
|             w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |             w->chatwin->history =  subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||||
|  |  | ||||||
|             if (w->type != WINDOW_TYPE_CONFERENCE) { |             if (w->type != WINDOW_TYPE_CONFERENCE) { | ||||||
|                 w->stb->topline = subwin(w->window, 2, x2, 0, 0); |                 w->stb->topline = subwin(w->window, TOP_BAR_HEIGHT, x2, 0, 0); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||||
|  |         w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|         if (w->chatwin->infobox.active) { |         if (w->chatwin->infobox.active) { | ||||||
| @@ -491,82 +503,103 @@ void on_window_resize(void) | |||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|         scrollok(w->chatwin->history, 0); |         scrollok(w->chatwin->history, 0); | ||||||
|  |         wmove(w->window, y2 - CURS_Y_OFFSET, 0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void draw_window_tab(ToxWindow *toxwin) | static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window) | ||||||
| { | { | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (toxwin->alert != WINDOW_ALERT_NONE) { |     bool has_alert = toxwin->alert != WINDOW_ALERT_NONE; | ||||||
|         attron(COLOR_PAIR(toxwin->alert)); |     unsigned int pending_messages = toxwin->pending_messages; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     clrtoeol(); |     WINDOW_TYPE type = toxwin->type; | ||||||
|     printw(" [%s]", toxwin->name); |  | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     if (active_window) { | ||||||
|  |         wattron(win, A_BOLD | COLOR_PAIR(BAR_ACCENT)); | ||||||
|     if (toxwin->alert != WINDOW_ALERT_NONE) { |         wprintw(win, " ["); | ||||||
|         attroff(COLOR_PAIR(toxwin->alert)); |         wattroff(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wattron(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |     } else { | ||||||
|  |         if (has_alert) { | ||||||
|  |             wattron(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wprintw(win, " ["); | ||||||
|  |             wattroff(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert)); | ||||||
|  |         } else { | ||||||
|  |             wattron(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wprintw(win, " ["); | ||||||
|  |             wattroff(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wattron(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) { | ||||||
|  |         wprintw(win, "%s", toxwin->name); | ||||||
|  |     } else { | ||||||
|  |         if (pending_messages > 0) { | ||||||
|  |             wprintw(win, "%u", pending_messages); | ||||||
|  |         } else { | ||||||
|  |             wprintw(win, "-"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (active_window) { | ||||||
|  |         wattroff(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wattron(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         wprintw(win, "]"); | ||||||
|  |         wattroff(win, A_BOLD | COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |     } else { | ||||||
|  |         if (has_alert) { | ||||||
|  |             wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert)); | ||||||
|  |             wattron(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wprintw(win, "]"); | ||||||
|  |             wattroff(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         } else { | ||||||
|  |             wattroff(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |             wattron(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |             wprintw(win, "]"); | ||||||
|  |             wattroff(win, COLOR_PAIR(BAR_ACCENT)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void draw_bar(void) | void draw_window_bar(ToxWindow *self) | ||||||
| { | { | ||||||
|     int y, x; |     WINDOW *win = self->window_bar; | ||||||
|  |     wclear(win); | ||||||
|  |  | ||||||
|     ToxWindow *w = windows[active_window_index]; |     if (self->scroll_pause) { | ||||||
|  |         wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY)); | ||||||
|     if (w == NULL) { |         wprintw(win, "^"); | ||||||
|         return; |         wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY)); | ||||||
|  |     } else { | ||||||
|  |         wattron(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|  |         wprintw(win, " "); | ||||||
|  |         wattroff(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // save current cursor position |  | ||||||
|     getyx(w->window, y, x); |  | ||||||
|  |  | ||||||
|     attron(COLOR_PAIR(BLUE)); |  | ||||||
|     mvhline(LINES - 2, 0, '_', COLS); |  | ||||||
|     attroff(COLOR_PAIR(BLUE)); |  | ||||||
|  |  | ||||||
|     move(LINES - 1, 0); |  | ||||||
|  |  | ||||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i] == NULL) { |         if (windows[i] == NULL) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (i == active_window_index) { |         bool active_window = i == active_window_index; | ||||||
|  |         draw_window_tab(win, windows[i], active_window); | ||||||
| #ifdef URXVT_FIX |  | ||||||
|             attron(A_BOLD | COLOR_PAIR(GREEN)); |  | ||||||
|         } else { |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|             attron(A_BOLD); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         draw_window_tab(windows[i]); |     int cur_x; | ||||||
|  |     int cur_y; | ||||||
|  |     UNUSED_VAR(cur_y); | ||||||
|  |  | ||||||
|         if (i == active_window_index) { |     getyx(win, cur_y, cur_x); | ||||||
|  |  | ||||||
| #ifdef URXVT_FIX |     wattron(win, COLOR_PAIR(BAR_TEXT)); | ||||||
|             attroff(A_BOLD | COLOR_PAIR(GREEN)); |     mvwhline(win, 0, cur_x, ' ', COLS - cur_x); | ||||||
|         } else { |     wattroff(win, COLOR_PAIR(BAR_TEXT)); | ||||||
| #endif |  | ||||||
|  |  | ||||||
|             attroff(A_BOLD); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // restore cursor position after drawing |  | ||||||
|     move(y, x); |  | ||||||
|  |  | ||||||
|     refresh(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -666,10 +699,9 @@ void draw_active_window(Tox *m) | |||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|     a->alert = WINDOW_ALERT_NONE; |     a->alert = WINDOW_ALERT_NONE; | ||||||
|  |     a->pending_messages = 0; | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     draw_bar(); |  | ||||||
|  |  | ||||||
|     touchwin(a->window); |     touchwin(a->window); | ||||||
|     a->onDraw(a, m); |     a->onDraw(a, m); | ||||||
|     wrefresh(a->window); |     wrefresh(a->window); | ||||||
|   | |||||||
| @@ -36,12 +36,14 @@ | |||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| #define MAX_WINDOWS_NUM 16 | #define MAX_WINDOWS_NUM 20 | ||||||
| #define MAX_WINDOW_NAME_LENGTH 22 | #define MAX_WINDOW_NAME_LENGTH 22 | ||||||
| #define CURS_Y_OFFSET 1    /* y-axis cursor offset for chat contexts */ | #define CURS_Y_OFFSET 1    /* y-axis cursor offset for chat contexts */ | ||||||
| #define CHATBOX_HEIGHT 2 | #define CHATBOX_HEIGHT 1 | ||||||
|  | #define TOP_BAR_HEIGHT 1 | ||||||
|  | #define WINDOW_BAR_HEIGHT 1 | ||||||
|  |  | ||||||
| /* Curses foreground colours (background is black) */ | /* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     WHITE, |     WHITE, | ||||||
|     GREEN, |     GREEN, | ||||||
| @@ -51,14 +53,24 @@ typedef enum { | |||||||
|     YELLOW, |     YELLOW, | ||||||
|     MAGENTA, |     MAGENTA, | ||||||
|     BLACK, |     BLACK, | ||||||
|  |     BLUE_BLACK, | ||||||
|  |     BLACK_WHITE, | ||||||
|  |     BAR_TEXT, | ||||||
|  |     STATUS_ONLINE, | ||||||
|  |     BAR_ACCENT, | ||||||
|  |     PURPLE_BG, | ||||||
|  |     BLACK_BG, | ||||||
|  |     STATUS_BUSY, | ||||||
|  |     STATUS_AWAY, | ||||||
|  |     BAR_NOTIFY, | ||||||
| } C_COLOURS; | } C_COLOURS; | ||||||
|  |  | ||||||
| /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ | /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     WINDOW_ALERT_NONE = 0, |     WINDOW_ALERT_NONE = 0, | ||||||
|     WINDOW_ALERT_0 = GREEN, |     WINDOW_ALERT_0 = STATUS_ONLINE, | ||||||
|     WINDOW_ALERT_1 = RED, |     WINDOW_ALERT_1 = BAR_ACCENT, | ||||||
|     WINDOW_ALERT_2 = MAGENTA, |     WINDOW_ALERT_2 = PURPLE_BG, | ||||||
| } WINDOW_ALERTS; | } WINDOW_ALERTS; | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
| @@ -167,6 +179,8 @@ struct ToxWindow { | |||||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     uint32_t num;    /* corresponds to friendnumber in chat windows */ |     uint32_t num;    /* corresponds to friendnumber in chat windows */ | ||||||
|     uint8_t index; /* This window's index in the windows array */ |     uint8_t index; /* This window's index in the windows array */ | ||||||
|  |     bool scroll_pause; /* true if this window is not scrolled to the bottom */ | ||||||
|  |     unsigned int pending_messages;  /* # of new messages in this window since the last time it was focused */ | ||||||
|     int x; |     int x; | ||||||
|  |  | ||||||
|     WINDOW_TYPE type; |     WINDOW_TYPE type; | ||||||
| @@ -180,6 +194,7 @@ struct ToxWindow { | |||||||
|     Help *help; |     Help *help; | ||||||
|  |  | ||||||
|     WINDOW *window; |     WINDOW *window; | ||||||
|  |     WINDOW *window_bar; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* statusbar info holder */ | /* statusbar info holder */ | ||||||
| @@ -263,6 +278,7 @@ void on_window_resize(void); | |||||||
| void force_refresh(WINDOW *w); | void force_refresh(WINDOW *w); | ||||||
| ToxWindow *get_window_ptr(size_t i); | ToxWindow *get_window_ptr(size_t i); | ||||||
| ToxWindow *get_active_window(void); | ToxWindow *get_active_window(void); | ||||||
|  | void draw_window_bar(ToxWindow *self); | ||||||
|  |  | ||||||
| /* refresh inactive windows to prevent scrolling bugs. | /* refresh inactive windows to prevent scrolling bugs. | ||||||
|    call at least once per second */ |    call at least once per second */ | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								src/x11focus.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/x11focus.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | /*  x11focus.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2020 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "x11focus.h" | ||||||
|  |  | ||||||
|  | #ifndef __APPLE__ | ||||||
|  |  | ||||||
|  | #include <X11/Xlib.h> | ||||||
|  |  | ||||||
|  | static struct Focus { | ||||||
|  |     Display *display; | ||||||
|  |     Window terminal_window; | ||||||
|  | } Focus; | ||||||
|  |  | ||||||
|  | static long unsigned int focused_window_id(void) | ||||||
|  | { | ||||||
|  |     if (!Focus.display) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Window focus; | ||||||
|  |     int revert; | ||||||
|  |  | ||||||
|  |     XLockDisplay(Focus.display); | ||||||
|  |     XGetInputFocus(Focus.display, &focus, &revert); | ||||||
|  |     XUnlockDisplay(Focus.display); | ||||||
|  |  | ||||||
|  |     return focus; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool is_focused(void) | ||||||
|  | { | ||||||
|  |     if (!Focus.display) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Focus.terminal_window == focused_window_id(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int init_x11focus(void) | ||||||
|  | { | ||||||
|  |     if (XInitThreads() == 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Focus.display = XOpenDisplay(NULL); | ||||||
|  |  | ||||||
|  |     if (!Focus.display) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Focus.terminal_window = focused_window_id(); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void terminate_x11focus(void) | ||||||
|  | { | ||||||
|  |     if (!Focus.display || !Focus.terminal_window) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     XLockDisplay(Focus.display); | ||||||
|  |     XCloseDisplay(Focus.display); | ||||||
|  |     XUnlockDisplay(Focus.display); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif /* !__APPLE__ */ | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| /*  xtra.h
 | /*  xtra.h
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2020 Toxic All Rights Reserved. | ||||||
|  * |  * | ||||||
|  *  This file is part of Toxic. |  *  This file is part of Toxic. | ||||||
|  * |  * | ||||||
| @@ -20,21 +20,15 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifndef XTRA_H | #ifndef X11FOCUS_H | ||||||
| #define XTRA_H | #define X11FOCUS_H | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| /* NOTE: If no xlib present don't compile */ | /* NOTE: If no xlib present don't compile */ | ||||||
| 
 | 
 | ||||||
| typedef enum { | int               init_x11focus(void); | ||||||
|     DT_plain, | void              terminate_x11focus(void); | ||||||
|     DT_file_list | bool              is_focused(void); | ||||||
| } |  | ||||||
| DropType; |  | ||||||
| 
 | 
 | ||||||
| typedef void (*drop_callback)(const char *, DropType); | #endif /* X11FOCUS */ | ||||||
| 
 |  | ||||||
| int               init_xtra(drop_callback d); |  | ||||||
| void              terminate_xtra(void); |  | ||||||
| int               is_focused(void); /* returns bool */ |  | ||||||
| 
 |  | ||||||
| #endif /* XTRA_H */ |  | ||||||
							
								
								
									
										425
									
								
								src/xtra.c
									
									
									
									
									
								
							
							
						
						
									
										425
									
								
								src/xtra.c
									
									
									
									
									
								
							| @@ -1,425 +0,0 @@ | |||||||
| /*  xtra.c |  | ||||||
|  * |  | ||||||
|  * |  | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  | ||||||
|  * |  | ||||||
|  *  This file is part of Toxic. |  | ||||||
|  * |  | ||||||
|  *  Toxic is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  *  the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  *  (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  *  Toxic is distributed in the hope that it will be useful, |  | ||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  *  GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "xtra.h" |  | ||||||
|  |  | ||||||
| #ifndef __APPLE__ |  | ||||||
|  |  | ||||||
| #include <X11/Xatom.h> |  | ||||||
| #include <X11/Xlib.h> |  | ||||||
|  |  | ||||||
| #include <assert.h> |  | ||||||
| #include <pthread.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <unistd.h> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const  Atom XtraTerminate = 1; |  | ||||||
| const  Atom XtraNil = 0; |  | ||||||
|  |  | ||||||
| static Atom XdndAware; |  | ||||||
| static Atom XdndEnter; |  | ||||||
| static Atom XdndLeave; |  | ||||||
| static Atom XdndPosition; |  | ||||||
| static Atom XdndStatus; |  | ||||||
| static Atom XdndDrop; |  | ||||||
| static Atom XdndSelection; |  | ||||||
| static Atom XdndDATA; |  | ||||||
| static Atom XdndTypeList; |  | ||||||
| static Atom XdndActionCopy; |  | ||||||
| static Atom XdndFinished; |  | ||||||
|  |  | ||||||
| struct Xtra { |  | ||||||
|     drop_callback on_drop; |  | ||||||
|     Display *display; |  | ||||||
|     Window terminal_window; |  | ||||||
|     Window proxy_window; |  | ||||||
|     Window source_window; /* When we have a drop */ |  | ||||||
|     Atom handling_version; |  | ||||||
|     Atom expecting_type; |  | ||||||
| } Xtra; |  | ||||||
|  |  | ||||||
| typedef struct Property { |  | ||||||
|     unsigned char *data; |  | ||||||
|     int            read_format; |  | ||||||
|     unsigned long  read_num; |  | ||||||
|     Atom           read_type; |  | ||||||
| } Property; |  | ||||||
|  |  | ||||||
| static Property read_property(Window s, Atom p) |  | ||||||
| { |  | ||||||
|     Atom read_type; |  | ||||||
|     int  read_format; |  | ||||||
|     unsigned long read_num; |  | ||||||
|     unsigned long left_bytes; |  | ||||||
|     unsigned char *data = NULL; |  | ||||||
|  |  | ||||||
|     int read_bytes = 1024; |  | ||||||
|  |  | ||||||
|     /* Keep trying to read the property until there are no bytes unread */ |  | ||||||
|     do { |  | ||||||
|         if (data) { |  | ||||||
|             XFree(data); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         XGetWindowProperty(Xtra.display, s, |  | ||||||
|                            p, 0, |  | ||||||
|                            read_bytes, |  | ||||||
|                            False, AnyPropertyType, |  | ||||||
|                            &read_type, &read_format, |  | ||||||
|                            &read_num, &left_bytes, |  | ||||||
|                            &data); |  | ||||||
|  |  | ||||||
|         read_bytes *= 2; |  | ||||||
|     } while (left_bytes != 0); |  | ||||||
|  |  | ||||||
|     Property property = {data, read_format, read_num, read_type}; |  | ||||||
|     return property; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static Atom get_dnd_type(long *a, int l) |  | ||||||
| { |  | ||||||
|     int i = 0; |  | ||||||
|  |  | ||||||
|     for (; i < l; i ++) { |  | ||||||
|         if (a[i] != XtraNil) { |  | ||||||
|             return a[i];    /* Get first valid */ |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return XtraNil; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* TODO maybe support only certain types in the future */ |  | ||||||
| static void handle_xdnd_enter(XClientMessageEvent *e) |  | ||||||
| { |  | ||||||
|     Xtra.handling_version = (e->data.l[1] >> 24); |  | ||||||
|  |  | ||||||
|     if ((e->data.l[1] & 1)) { |  | ||||||
|         // Fetch the list of possible conversions |  | ||||||
|         Property p = read_property(e->data.l[0], XdndTypeList); |  | ||||||
|         Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num); |  | ||||||
|         XFree(p.data); |  | ||||||
|     } else { |  | ||||||
|         // Use the available list |  | ||||||
|         Xtra.expecting_type = get_dnd_type(e->data.l + 2, 3); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void handle_xdnd_position(XClientMessageEvent *e) |  | ||||||
| { |  | ||||||
|     XEvent ev = { |  | ||||||
|         .xclient = { |  | ||||||
|             .type = ClientMessage, |  | ||||||
|             .display = e->display, |  | ||||||
|             .window = e->data.l[0], |  | ||||||
|             .message_type = XdndStatus, |  | ||||||
|             .format = 32, |  | ||||||
|             .data = { |  | ||||||
|                 .l = { |  | ||||||
|                     Xtra.proxy_window, |  | ||||||
|                     (Xtra.expecting_type != XtraNil), |  | ||||||
|                     0, 0, |  | ||||||
|                     XdndActionCopy |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); |  | ||||||
|     XFlush(Xtra.display); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void handle_xdnd_drop(XClientMessageEvent *e) |  | ||||||
| { |  | ||||||
|     /* Not expecting any type */ |  | ||||||
|     if (Xtra.expecting_type == XtraNil) { |  | ||||||
|         XEvent ev = { |  | ||||||
|             .xclient = { |  | ||||||
|                 .type = ClientMessage, |  | ||||||
|                 .display = e->display, |  | ||||||
|                 .window = e->data.l[0], |  | ||||||
|                 .message_type = XdndFinished, |  | ||||||
|                 .format = 32, |  | ||||||
|                 .data = { |  | ||||||
|                     .l = {Xtra.proxy_window, 0, 0} |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); |  | ||||||
|     } else { |  | ||||||
|         Xtra.source_window = e->data.l[0]; |  | ||||||
|         XConvertSelection(Xtra.display, |  | ||||||
|                           XdndSelection, |  | ||||||
|                           Xtra.expecting_type, |  | ||||||
|                           XdndSelection, |  | ||||||
|                           Xtra.proxy_window, |  | ||||||
|                           Xtra.handling_version >= 1 ? e->data.l[2] : CurrentTime); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void handle_xdnd_selection(XSelectionEvent *e) |  | ||||||
| { |  | ||||||
|     UNUSED_VAR(e); |  | ||||||
|  |  | ||||||
|     /* DnD succesfully finished, send finished and call callback */ |  | ||||||
|     XEvent ev = { |  | ||||||
|         .xclient = { |  | ||||||
|             .type = ClientMessage, |  | ||||||
|             .display = Xtra.display, |  | ||||||
|             .window = Xtra.source_window, |  | ||||||
|             .message_type = XdndFinished, |  | ||||||
|             .format = 32, |  | ||||||
|             .data = { |  | ||||||
|                 .l = {Xtra.proxy_window, 1, XdndActionCopy} |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev); |  | ||||||
|  |  | ||||||
|     Property p = read_property(Xtra.proxy_window, XdndSelection); |  | ||||||
|     DropType dt; |  | ||||||
|  |  | ||||||
|     if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) { |  | ||||||
|         dt = DT_file_list; |  | ||||||
|     } else { /* text/uri-list */ |  | ||||||
|         dt = DT_plain; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /* Call callback for every entry */ |  | ||||||
|     if (Xtra.on_drop && p.read_num) { |  | ||||||
|         char *sptr; |  | ||||||
|         char *str = strtok_r((char *) p.data, "\n\r", &sptr); |  | ||||||
|  |  | ||||||
|         if (str) { |  | ||||||
|             Xtra.on_drop(str, dt); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         while ((str = strtok_r(NULL, "\n\r", &sptr))) { |  | ||||||
|             Xtra.on_drop(str, dt); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (p.data) { |  | ||||||
|         XFree(p.data); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void *event_loop(void *p) |  | ||||||
| { |  | ||||||
|     /* Handle events like a real nigga */ |  | ||||||
|  |  | ||||||
|     UNUSED_VAR(p); /* DINDUNOTHIN */ |  | ||||||
|  |  | ||||||
|     XEvent event; |  | ||||||
|     int pending; |  | ||||||
|  |  | ||||||
|     while (Xtra.display) { |  | ||||||
|         /* NEEDMOEVENTSFODEMPROGRAMS */ |  | ||||||
|  |  | ||||||
|         XLockDisplay(Xtra.display); |  | ||||||
|  |  | ||||||
|         if ((pending = XPending(Xtra.display))) { |  | ||||||
|             XNextEvent(Xtra.display, &event); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!pending) { |  | ||||||
|             XUnlockDisplay(Xtra.display); |  | ||||||
|             sleep_thread(10000L); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (event.type == ClientMessage) { |  | ||||||
|             Atom type = event.xclient.message_type; |  | ||||||
|  |  | ||||||
|             if (type == XdndEnter) { |  | ||||||
|                 handle_xdnd_enter(&event.xclient); |  | ||||||
|             } else if (type == XdndPosition) { |  | ||||||
|                 handle_xdnd_position(&event.xclient); |  | ||||||
|             } else if (type == XdndDrop) { |  | ||||||
|                 handle_xdnd_drop(&event.xclient); |  | ||||||
|             } else if (type == XtraTerminate) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } else if (event.type == SelectionNotify) { |  | ||||||
|             handle_xdnd_selection(&event.xselection); |  | ||||||
|         } |  | ||||||
|         /* AINNOBODYCANHANDLEDEMEVENTS*/ |  | ||||||
|         else { |  | ||||||
|             XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         XUnlockDisplay(Xtra.display); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Actual XTRA termination |  | ||||||
|      * Please call xtra_terminate() at exit |  | ||||||
|      * otherwise HEWUSAGUDBOI happens |  | ||||||
|      */ |  | ||||||
|     if (Xtra.display) { |  | ||||||
|         XCloseDisplay(Xtra.display); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return (Xtra.display = NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static long unsigned int focused_window_id(void) |  | ||||||
| { |  | ||||||
|     if (!Xtra.display) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Window focus; |  | ||||||
|     int revert; |  | ||||||
|     XLockDisplay(Xtra.display); |  | ||||||
|     XGetInputFocus(Xtra.display, &focus, &revert); |  | ||||||
|     XUnlockDisplay(Xtra.display); |  | ||||||
|     return focus; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int is_focused(void) |  | ||||||
| { |  | ||||||
|     return Xtra.display && (Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int init_xtra(drop_callback d) |  | ||||||
| { |  | ||||||
|     if (!d) { |  | ||||||
|         return -1; |  | ||||||
|     } else { |  | ||||||
|         Xtra.on_drop = d; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     XInitThreads(); |  | ||||||
|  |  | ||||||
|     if (!(Xtra.display = XOpenDisplay(NULL))) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Xtra.terminal_window = focused_window_id(); |  | ||||||
|  |  | ||||||
|     /* OSX: if focused window is 0, it means toxic is ran from |  | ||||||
|      * native terminal and not X11 terminal window, silently exit */ |  | ||||||
|     if (!Xtra.terminal_window) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     { |  | ||||||
|         /* Create an invisible window which will act as proxy for the DnD operation. */ |  | ||||||
|         XSetWindowAttributes attr  = {0}; |  | ||||||
|         attr.event_mask            = EnterWindowMask | |  | ||||||
|                                      LeaveWindowMask | |  | ||||||
|                                      ButtonMotionMask | |  | ||||||
|                                      ButtonPressMask | |  | ||||||
|                                      ButtonReleaseMask | |  | ||||||
|                                      ResizeRedirectMask; |  | ||||||
|  |  | ||||||
|         attr.do_not_propagate_mask = NoEventMask; |  | ||||||
|  |  | ||||||
|         Window root; |  | ||||||
|         int x, y; |  | ||||||
|         unsigned int wht, hht, b, d; |  | ||||||
|  |  | ||||||
|         /* Since we cannot capture resize events for parent window we will have to create |  | ||||||
|          * this window to have maximum size as defined in root window |  | ||||||
|          */ |  | ||||||
|         XGetGeometry(Xtra.display, |  | ||||||
|                      XDefaultRootWindow(Xtra.display), |  | ||||||
|                      &root, &x, &y, &wht, &hht, &b, &d); |  | ||||||
|  |  | ||||||
|         if (!(Xtra.proxy_window = XCreateWindow |  | ||||||
|                                   (Xtra.display, Xtra.terminal_window,       /* Parent */ |  | ||||||
|                                    0, 0,                                     /* Position */ |  | ||||||
|                                    wht, hht,                                 /* Width + height */ |  | ||||||
|                                    0,                                        /* Border width */ |  | ||||||
|                                    CopyFromParent,                           /* Depth */ |  | ||||||
|                                    InputOnly,                                /* Class */ |  | ||||||
|                                    CopyFromParent,                           /* Visual */ |  | ||||||
|                                    CWEventMask | CWCursor,                   /* Value mask */ |  | ||||||
|                                    &attr))) {                                /* Attributes for value mask */ |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     XMapWindow(Xtra.display, Xtra.proxy_window);   /* Show window (sandwich) */ |  | ||||||
|     XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */ |  | ||||||
|  |  | ||||||
|     XdndAware = XInternAtom(Xtra.display, "XdndAware", False); |  | ||||||
|     XdndEnter = XInternAtom(Xtra.display, "XdndEnter", False); |  | ||||||
|     XdndLeave = XInternAtom(Xtra.display, "XdndLeave", False); |  | ||||||
|     XdndPosition = XInternAtom(Xtra.display, "XdndPosition", False); |  | ||||||
|     XdndStatus = XInternAtom(Xtra.display, "XdndStatus", False); |  | ||||||
|     XdndDrop = XInternAtom(Xtra.display, "XdndDrop", False); |  | ||||||
|     XdndSelection = XInternAtom(Xtra.display, "XdndSelection", False); |  | ||||||
|     XdndDATA = XInternAtom(Xtra.display, "XdndDATA", False); |  | ||||||
|     XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False); |  | ||||||
|     XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False); |  | ||||||
|     XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False); |  | ||||||
|  |  | ||||||
|     /* Inform my nigga windows that we are aware of dnd */ |  | ||||||
|     Atom XdndVersion = 3; |  | ||||||
|     XChangeProperty(Xtra.display, |  | ||||||
|                     Xtra.proxy_window, |  | ||||||
|                     XdndAware, |  | ||||||
|                     XA_ATOM, |  | ||||||
|                     32, |  | ||||||
|                     PropModeReplace, |  | ||||||
|                     (unsigned char *)&XdndVersion, 1); |  | ||||||
|  |  | ||||||
|     pthread_t id; |  | ||||||
|  |  | ||||||
|     if (pthread_create(&id, NULL, event_loop, NULL) != 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pthread_detach(id); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void terminate_xtra(void) |  | ||||||
| { |  | ||||||
|     if (!Xtra.display || !Xtra.terminal_window) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     XEvent terminate = { |  | ||||||
|         .xclient = { |  | ||||||
|             .type = ClientMessage, |  | ||||||
|             .display = Xtra.display, |  | ||||||
|             .message_type = XtraTerminate, |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     XLockDisplay(Xtra.display); |  | ||||||
|     XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware); |  | ||||||
|     XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate); |  | ||||||
|     XUnlockDisplay(Xtra.display); |  | ||||||
|  |  | ||||||
|     while (Xtra.display); /* Wait for termination */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif /* !__APPLE__ */ |  | ||||||
		Reference in New Issue
	
	Block a user